Snippets / CI/CD
GitHub Actions: build, scan, push
Builds a container image, scans it for vulnerabilities, and pushes it to GHCR on every push to main.
github-actionsciregistry
deploy.yml
name: build-and-push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
# Default token permissions are read-only -- "packages: write" is what
# actually lets this job push to GHCR.
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
# Needed for cache-from/cache-to below; the default builder docker
# already has doesn't support the gha cache backend.
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
# GITHUB_TOKEN is minted fresh per run -- no PAT to create or
# rotate for pushing to GHCR.
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
push: true
# Tagged by commit SHA, not "latest" -- every build is traceable
# back to the exact source it was built from.
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
# mode=max also caches intermediate layers from earlier build
# stages, not just the final image -- bigger cache, faster builds.
cache-to: type=gha,mode=max
- name: Scan image
# Pinned to a commit, not a version tag -- most of trivy-action's
# version tags were hijacked to credential-stealing malware in a
# real 2026 supply-chain attack (GHSA-69fq-xp46-6x23). This SHA is
# v0.36.0, a GitHub immutable release.
uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0
with:
image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
severity: CRITICAL,HIGH
# Non-zero exit on a finding fails the job -- the image is
# already pushed by this point, so this gates later deploy
# steps rather than the push itself.
exit-code: "1"
More ci/cd snippets