Docker Scan & Promote #1296
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
| name: Docker Scan & Promote | |
| # Runs after Stack Tests completes on main — promotes sha-xxx → latest. | |
| # "latest" is NEVER set during build. Only this workflow can set it, | |
| # and only after all tests pass. If any test fails, latest stays unchanged. | |
| on: | |
| workflow_run: | |
| workflows: ["Stack Tests"] | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| sha: | |
| description: 'Full commit SHA to promote (defaults to latest main)' | |
| required: false | |
| env: | |
| REGISTRY: ghcr.io | |
| jobs: | |
| scan: | |
| name: Scan ${{ matrix.image }} | |
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| security-events: write | |
| packages: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| image: | |
| - base | |
| - backend | |
| - frontend | |
| - cert-generator | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Compute image ref | |
| id: ref | |
| run: | | |
| PREFIX="${GITHUB_REPOSITORY_OWNER,,}/integr8scode" | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| SHA="${{ github.event.inputs.sha || github.sha }}" | |
| else | |
| SHA="${{ github.event.workflow_run.head_sha }}" | |
| fi | |
| TAG="sha-${SHA::7}" | |
| echo "image=${{ env.REGISTRY }}/$PREFIX/${{ matrix.image }}:$TAG" >> $GITHUB_OUTPUT | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@0.34.1 | |
| with: | |
| image-ref: ${{ steps.ref.outputs.image }} | |
| format: 'sarif' | |
| output: 'trivy-${{ matrix.image }}-results.sarif' | |
| ignore-unfixed: true | |
| severity: 'CRITICAL,HIGH' | |
| timeout: '5m0s' | |
| trivyignores: 'backend/.trivyignore' | |
| version: 'v0.68.2' | |
| - name: Upload Trivy scan results | |
| if: always() | |
| uses: github/codeql-action/upload-sarif@v4 | |
| with: | |
| sarif_file: 'trivy-${{ matrix.image }}-results.sarif' | |
| category: 'trivy-${{ matrix.image }}' | |
| # Promote SHA tag → latest using crane (registry-level manifest copy, no rebuild) | |
| promote: | |
| name: Promote to Latest | |
| needs: [scan] | |
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| packages: write | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install crane | |
| uses: imjasonh/setup-crane@v0.5 | |
| - name: Promote images (SHA → latest) | |
| run: | | |
| PREFIX="${GITHUB_REPOSITORY_OWNER,,}/integr8scode" | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| SHA="${{ github.event.inputs.sha || github.sha }}" | |
| else | |
| SHA="${{ github.event.workflow_run.head_sha }}" | |
| fi | |
| TAG="sha-${SHA::7}" | |
| echo "Promoting tag: $TAG → latest" | |
| echo "" | |
| crane copy "$REGISTRY/$PREFIX/base:$TAG" "$REGISTRY/$PREFIX/base:latest" | |
| crane copy "$REGISTRY/$PREFIX/backend:$TAG" "$REGISTRY/$PREFIX/backend:latest" | |
| crane copy "$REGISTRY/$PREFIX/frontend:$TAG" "$REGISTRY/$PREFIX/frontend:latest" | |
| crane copy "$REGISTRY/$PREFIX/cert-generator:$TAG" "$REGISTRY/$PREFIX/cert-generator:latest" | |
| summary: | |
| name: Summary | |
| needs: [promote] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Generate summary | |
| run: | | |
| PREFIX="${GITHUB_REPOSITORY_OWNER,,}/integr8scode" | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| SHA="${{ github.event.inputs.sha || github.sha }}" | |
| else | |
| SHA="${{ github.event.workflow_run.head_sha }}" | |
| fi | |
| TAG="sha-${SHA::7}" | |
| echo "## Docker Images Promoted to Latest" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | |
| echo "Images promoted manually from \`$TAG\` to \`latest\` — Stack Tests may not have run." >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "All Stack Tests passed. Images promoted from \`$TAG\` to \`latest\`." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Image | Pull Command |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|--------------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Base | \`docker pull $REGISTRY/$PREFIX/base:latest\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Backend | \`docker pull $REGISTRY/$PREFIX/backend:latest\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Frontend | \`docker pull $REGISTRY/$PREFIX/frontend:latest\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Security Scans" >> $GITHUB_STEP_SUMMARY | |
| echo "All 4 images scanned with Trivy (CRITICAL + HIGH, unfixed ignored)." >> $GITHUB_STEP_SUMMARY |