Files
github-actions-library/.github/workflows/docker-ci.yml
Greg Hendrickson 03325f5784 feat(docker): add Docker CI/CD reusable workflow
- OIDC authentication to GHCR (keyless, no secrets required)
- Multi-platform builds (linux/amd64, linux/arm64)
- SBOM generation and attestation
- Build provenance attestation
- Trivy vulnerability scanning with SARIF upload
- GitHub Actions cache for layer reuse
- Semantic version tagging from git refs
- All actions pinned to SHA for supply chain security

Follows 2026 GitHub Actions security best practices:
- Pin dependencies by SHA
- Generate software attestations
- Scan for vulnerabilities before push
2026-02-08 18:02:08 +00:00

207 lines
7.3 KiB
YAML

# 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