Releases

Release pipeline

How a push to main becomes sixteen published multi-architecture images, and what stops a bad one getting out.

Builds run in GitHub Actions from .github/workflows/docker-build.yml. They are triggered by a push to main, a manual dispatch, and a weekly schedule.

Jobs

php-matrix ──┐
             ├──▶ build-php (one job per PHP minor, in parallel)
build-base ──┘        php ──▶ php-nginx ──▶ php-nginx-dev
  1. php-matrix reads alpine/php-versions.txt and turns it into the build matrix, so adding a PHP version is a one-line change.
  2. build-base builds, scans and promotes adamrollogi/base:latest.
  3. build-php runs once per minor and builds php, then php-nginx from the promoted php tag, then php-nginx-dev from the promoted php-nginx tag. Each goes through the same candidate, scan, attest, promote sequence.

Multi-architecture builds

Images are built for linux/amd64 and linux/arm64 with buildx using the docker-container driver. The arm64 half is built under QEMU on the amd64 runner. Layers are cached in the GitHub Actions cache, one scope per image, so unchanged layers are reused between runs. A full uncached run takes a few minutes per PHP minor.

Candidate, scan, promote

Every image is first pushed under candidate-<git sha> (base) or candidate-<git sha>-<minor> (PHP images). Trivy scans that candidate for CRITICAL and HIGH findings with fixes available; a finding fails the job before anything reaches a public tag. The VEX attestation is attached, then docker buildx imagetools create retags the candidate as the real tags, copying the whole manifest list including the SBOM, provenance and VEX. Finally the candidate tag is deleted through the Docker Hub API.

The Docker Hub token used by CI needs the Delete scope for that last step. Without it, candidate tags are left behind but nothing else is affected.

What is pinned

  • Actions are pinned to commit SHAs and kept current by Dependabot.
  • buildx is pinned to a release via BUILDX_VERSION.
  • The Alpine base is pinned to a digest and kept current by Dependabot.
  • PHP is pinned per minor in php-versions.txt and kept current by the bump job.

Weekly rebuild

The schedule rebuilds everything every Monday even when nothing in the repository changed. Because the base Dockerfile runs apk upgrade, this is how Alpine's fixes for musl, OpenSSL, Nginx and other system packages reach the published images without a code change.

Secrets and permissions

The workflow needs a DOCKER_USER variable and a DOCKER_PAT secret. The token is passed only to the login step and the candidate cleanup step, never exposed at workflow scope, and the workflow runs with read-only repository permissions.