# Docker CI/CD Reusable Workflow # Builds, scans, and pushes container images with attestations # # Features: # - OIDC authentication (no secrets required for GHCR) # - Multi-platform builds (linux/amd64, linux/arm64) # - SBOM generation and attestation # - Trivy vulnerability scanning # - Build provenance attestation # - Smart caching with GitHub Actions cache # # Usage: # jobs: # docker: # uses: ghndrx/github-actions-library/.github/workflows/docker-ci.yml@main # with: # image-name: my-app # push: ${{ github.event_name != 'pull_request' }} # permissions: # contents: read # packages: write # id-token: write # attestations: write name: Docker CI on: workflow_call: inputs: image-name: description: 'Image name (without registry prefix)' required: true type: string context: description: 'Docker build context path' required: false type: string default: '.' dockerfile: description: 'Path to Dockerfile' required: false type: string default: 'Dockerfile' push: description: 'Push image to registry' required: false type: boolean default: false platforms: description: 'Target platforms (comma-separated)' required: false type: string default: 'linux/amd64,linux/arm64' build-args: description: 'Build arguments (newline-separated KEY=value)' required: false type: string default: '' target: description: 'Docker build target stage' required: false type: string default: '' scan-severity: description: 'Trivy scan severity threshold (CRITICAL,HIGH,MEDIUM,LOW)' required: false type: string default: 'CRITICAL,HIGH' fail-on-vuln: description: 'Fail workflow if vulnerabilities found' required: false type: boolean default: false generate-sbom: description: 'Generate SBOM attestation' required: false type: boolean default: true generate-provenance: description: 'Generate build provenance attestation' required: false type: boolean default: true outputs: image-digest: description: 'Image digest (sha256:...)' value: ${{ jobs.build.outputs.digest }} image-tags: description: 'Generated image tags (JSON array)' value: ${{ jobs.build.outputs.tags }} sbom-attestation-id: description: 'SBOM attestation bundle ID' value: ${{ jobs.build.outputs.sbom-id }} jobs: build: name: Build & Push runs-on: ubuntu-latest permissions: contents: read packages: write id-token: write attestations: write outputs: digest: ${{ steps.build.outputs.digest }} tags: ${{ steps.meta.outputs.tags }} sbom-id: ${{ steps.sbom-attest.outputs.bundle-id }} steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 - name: Log in to GitHub Container Registry if: inputs.push uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }} tags: | # Branch name type=ref,event=branch # PR number type=ref,event=pr # Semantic version tags type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }} # Short SHA type=sha,prefix=sha-,format=short # Latest on default branch type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - name: Build and push id: build uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 with: context: ${{ inputs.context }} file: ${{ inputs.context }}/${{ inputs.dockerfile }} platforms: ${{ inputs.platforms }} push: ${{ inputs.push }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: ${{ inputs.build-args }} target: ${{ inputs.target }} cache-from: type=gha cache-to: type=gha,mode=max provenance: ${{ inputs.generate-provenance }} sbom: ${{ inputs.generate-sbom }} - name: Generate SBOM attestation if: inputs.push && inputs.generate-sbom id: sbom-attest uses: actions/attest-sbom@115c3be05ff3974bcbd596578934b3f9ce39bf68 # v2.1.0 with: subject-name: ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }} subject-digest: ${{ steps.build.outputs.digest }} sbom-path: ${{ runner.temp }}/sbom.spdx.json continue-on-error: true - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # v0.29.0 with: image-ref: ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }}@${{ steps.build.outputs.digest }} format: 'sarif' output: 'trivy-results.sarif' severity: ${{ inputs.scan-severity }} exit-code: ${{ inputs.fail-on-vuln && '1' || '0' }} if: inputs.push - name: Upload Trivy scan results uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 with: sarif_file: 'trivy-results.sarif' if: inputs.push && always() - name: Output summary if: always() run: | echo "## 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY echo "| Image | \`ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Digest | \`${{ steps.build.outputs.digest }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Platforms | ${{ inputs.platforms }} |" >> $GITHUB_STEP_SUMMARY echo "| Pushed | ${{ inputs.push }} |" >> $GITHUB_STEP_SUMMARY echo "| Provenance | ${{ inputs.generate-provenance }} |" >> $GITHUB_STEP_SUMMARY echo "| SBOM | ${{ inputs.generate-sbom }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Tags" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY