Merge branch 'main' of https://github.com/neverinfamous/postgres-mcp #79
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # act -W ../.github/workflows/docker-build-dockerhub.yml -s DOCKERHUB_USERNAME -s DOCKERHUB_TOKEN -s GITHUB_TOKEN="$(gh auth token)" | |
| --- | |
| name: Build and Push Docker Image to DockerHub | |
| on: | |
| push: | |
| branches: [ main ] | |
| tags: [ 'v*' ] | |
| pull_request: | |
| branches: [ main ] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version to release (without v prefix)" | |
| required: true | |
| default: "" | |
| # Security: Explicit permissions following principle of least privilege | |
| permissions: | |
| contents: read | |
| packages: write | |
| security-events: write # For security scanning | |
| pull-requests: write # For PR comments | |
| attestations: write # For generating attestations | |
| jobs: | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.set-version.outputs.VERSION }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Extract version from pyproject.toml | |
| id: set-version | |
| run: | | |
| if [[ "${{ github.event_name }}" == "push" && "${{ github.ref_type }}" == "tag" ]]; then | |
| # Extract version from git tag (remove 'v' prefix) | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| elif [[ -n "${{ github.event.inputs.version }}" ]]; then | |
| # Use workflow_dispatch input | |
| VERSION="${{ github.event.inputs.version }}" | |
| else | |
| # Extract from pyproject.toml for regular pushes | |
| VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') | |
| fi | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Extracted version: $VERSION" | |
| build-and-push: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| environment: production | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| id: buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| install: true | |
| version: latest | |
| driver-opts: image=moby/buildkit:latest | |
| - name: Inspect builder | |
| run: | | |
| echo "Available platforms: $(docker buildx inspect --bootstrap | grep 'Platforms:')" | |
| docker buildx ls | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: arm64,amd64 | |
| image: tonistiigi/binfmt:latest | |
| - name: Prepare Docker tags | |
| id: docker_meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: writenotenow/postgres-mcp-enhanced | |
| flavor: | | |
| latest=false | |
| tags: | | |
| # Create latest tag for main branch pushes only | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| # Create version tags (v1.1.0) | |
| type=raw,value=v${{ needs.prepare.outputs.version }},enable={{is_default_branch}} | |
| # Create short SHA tags for traceability (e.g., sha-8f8183d) | |
| type=sha,prefix=sha-,format=short,enable={{is_default_branch}} | |
| - name: Check directory structure | |
| run: | | |
| echo "check pwd: $(pwd)" | |
| echo "check ls: $(ls -lta)" | |
| # Login to Docker Hub for Scout scanning (needs auth for full vulnerability data) | |
| - name: Login to DockerHub for Scout | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| # Build locally first for security scanning (AMD64 only) | |
| - name: Build Docker image for scanning | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile | |
| platforms: linux/amd64 | |
| push: false | |
| load: true | |
| tags: local-scan:latest | |
| cache-from: type=gha | |
| # Security scanning with Docker Scout CLI - BLOCKS build on critical/high CVEs | |
| - name: Docker Scout security scan (blocking) | |
| timeout-minutes: 10 | |
| continue-on-error: false | |
| run: | | |
| # Install Docker Scout CLI | |
| curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s -- | |
| # Verify image exists | |
| docker images local-scan:latest | |
| # Run security scan - block vulnerabilities above medium level (critical, high) | |
| echo "🔍 Running Docker Scout security scan for local-scan:latest" | |
| # Run scan and capture output | |
| set +e # Don't exit on error, we'll handle it | |
| docker scout cves local-scan:latest 2>&1 | tee scout_output.txt | |
| SCOUT_EXIT=$? | |
| set -e | |
| echo "" | |
| echo "📊 Scout scan completed with exit code: $SCOUT_EXIT" | |
| # Check if critical or high vulnerabilities are present in the output | |
| if grep -E "(CRITICAL|HIGH)" scout_output.txt 2>/dev/null | grep -v "0C" | grep -v "0H" | grep -v "0 " > /dev/null 2>&1; then | |
| echo "❌ Critical or high severity vulnerabilities detected" | |
| echo "🚨 Build blocked due to unacceptable security risk" | |
| exit 1 | |
| else | |
| echo "✅ Security scan passed - no critical/high severity vulnerabilities" | |
| echo "ℹ️ Low/medium severity vulnerabilities are acceptable" | |
| fi | |
| # Only push if security scan passes | |
| # Note: Already logged in from Scout scan step | |
| - name: Build and push | |
| if: success() | |
| id: build-push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.docker_meta.outputs.tags }} | |
| labels: ${{ steps.docker_meta.outputs.labels }} | |
| # Use GitHub Actions cache instead of registry cache to avoid buildcache tag | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Security: Enable build attestations for supply chain security | |
| provenance: mode=max | |
| sbom: true | |
| # Retry mechanism for transient Docker Hub API failures | |
| - name: Retry Docker push on failure | |
| if: failure() && steps.build-push.outcome == 'failure' | |
| run: | | |
| echo "⚠️ Initial push failed - retrying in 30 seconds..." | |
| sleep 30 | |
| echo "🔄 Retry attempt starting..." | |
| # Post-push Docker Scout analysis for reporting (non-blocking) | |
| - name: Docker Scout Analysis (reporting only) | |
| uses: docker/scout-action@v1 | |
| if: github.event_name == 'push' && success() | |
| continue-on-error: true | |
| with: | |
| command: cves,recommendations | |
| image: writenotenow/postgres-mcp-enhanced:v${{ needs.prepare.outputs.version }} | |
| only-severities: critical,high | |
| exit-code: false | |
| write-comment: true | |
| - name: Docker Scout Compare (reporting only) | |
| uses: docker/scout-action@v1 | |
| if: github.event_name == 'push' && success() | |
| continue-on-error: true | |
| with: | |
| command: compare | |
| image: writenotenow/postgres-mcp-enhanced:v${{ needs.prepare.outputs.version }} | |
| to: writenotenow/postgres-mcp-enhanced:latest | |
| exit-code: false | |
| - name: Update Docker Hub Description | |
| if: github.event_name == 'push' | |
| uses: peter-evans/dockerhub-description@v5 | |
| continue-on-error: true | |
| timeout-minutes: 5 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| repository: writenotenow/postgres-mcp-enhanced | |
| readme-filepath: ./DOCKER_README.md | |
| short-description: "Enterprise PostgreSQL MCP Server - 63 specialized tools with comprehensive security and AI-native operations" |