Skip to content

docs: cut 0.0.6 release notes and bump documented version #5

docs: cut 0.0.6 release notes and bump documented version

docs: cut 0.0.6 release notes and bump documented version #5

Workflow file for this run

name: release
# Tag-driven release: build + smoke-test the PHP matrix, push the images to
# GHCR, then cut a GitHub Release with notes lifted from CHANGELOG.md.
# Trigger: git tag -a vX.Y.Z && git push --follow-tags
on:
push:
tags: ["v*"]
# Least privilege at the top; the jobs that need more opt in explicitly.
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
REGISTRY: ghcr.io
jobs:
images:
runs-on: ubuntu-latest
permissions:
contents: read # checkout
packages: write # push to GHCR via the built-in GITHUB_TOKEN
id-token: write # keyless (OIDC) signing for the attestations below
attestations: write # record the provenance + SBOM attestations
strategy:
fail-fast: false
matrix:
php: ["8.3", "8.4", "8.5"]
env:
PHP: ${{ matrix.php }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
# Single-source the release coordinates from the Dockerfile ARGs (same as
# the Makefile) so this workflow needs no edit on a version bump. GHCR
# requires a lowercase image path, hence the owner is downcased.
- name: derive image coordinates
run: |
set -euo pipefail
owner=${GITHUB_REPOSITORY_OWNER,,}
# Guard each extraction: a drifted ARG format (quoting, inline comment,
# spaces around =) would otherwise emit an empty value and silently push
# a malformed tag. Fail loudly instead, mirroring check-upstream.yml.
suite=$(sed -n 's/^ARG SUITE=//p' Dockerfile)
default_php=$(sed -n 's/^ARG PHP_VER=//p' Dockerfile)
freeunit_release=$(sed -n 's/^ARG FREEUNIT_RELEASE=//p' Dockerfile)
: "${suite:?could not read ARG SUITE from Dockerfile}"
: "${default_php:?could not read ARG PHP_VER from Dockerfile}"
: "${freeunit_release:?could not read ARG FREEUNIT_RELEASE from Dockerfile}"
{
echo "IMAGE=${REGISTRY}/${owner}/freeunit-php"
echo "VERSION=${GITHUB_REF_NAME#v}"
echo "SUITE=${suite}"
echo "DEFAULT_PHP=${default_php}"
echo "FREEUNIT_RELEASE=${freeunit_release}"
} >> "$GITHUB_ENV"
# Reuses the Makefile target: builds php$PHP with the immutable and
# floating tags, then runs the end-to-end smoke test against it.
- name: build + smoke test
run: make test DEFAULT_PHP="$PHP" IMAGE="$IMAGE"
- name: log in to GHCR
env:
GHCR_USER: ${{ github.actor }}
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "$GHCR_TOKEN" | docker login "$REGISTRY" -u "$GHCR_USER" --password-stdin
# Push the two tags make produced (floating + immutable), add a
# release-version tag, and on the default PHP line also publish :latest and
# the bare :$VERSION so `docker pull $IMAGE` and `:$VERSION` resolve.
- name: tag + push
run: |
floating="$IMAGE:${SUITE}-php${PHP}"
immutable="$IMAGE:${SUITE}-${FREEUNIT_RELEASE}-php${PHP}"
versioned="$IMAGE:${VERSION}-php${PHP}"
docker tag "$floating" "$versioned"
docker push "$floating"
docker push "$immutable"
docker push "$versioned"
if [ "$PHP" = "$DEFAULT_PHP" ]; then
docker tag "$floating" "$IMAGE:latest"
docker tag "$floating" "$IMAGE:${VERSION}"
docker push "$IMAGE:latest"
docker push "$IMAGE:${VERSION}"
fi
# All tags for this PHP leg point at one image; resolve its registry
# digest once so the attestations below bind to the manifest, not a
# mutable tag. Export the local ref too for the SBOM scan.
digest=$(docker buildx imagetools inspect "$immutable" --format '{{ .Manifest.Digest }}')
{
echo "IMAGE_DIGEST=$digest"
echo "IMAGE_REF=$immutable"
} >> "$GITHUB_ENV"
# Scan the just-built image into an SPDX SBOM (from the local daemon, so no
# re-pull). Artifact upload is off: the SBOM is published as an attestation
# below, and parallel matrix legs would collide on a shared artifact name.
- name: generate SBOM
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
image: ${{ env.IMAGE_REF }}
format: spdx-json
output-file: sbom.spdx.json
upload-artifact: false
# Keyless (OIDC) provenance + SBOM attestations, pushed to GHCR as OCI
# referrers of the image manifest. Verify with:
# gh attestation verify oci://$IMAGE@$DIGEST --owner <owner>
# Both write a Sigstore public-good Rekor transparency-log entry, which
# intermittently times out ("error creating tlog entry"). GitHub Actions
# has no native retry for `uses:` steps, so each attestation runs once with
# continue-on-error and is re-run by a guarded step only when the first
# attempt failed — riding out a transient tlog flake without failing the
# whole release.
- name: attest build provenance
id: provenance
continue-on-error: true
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-name: ${{ env.IMAGE }}
subject-digest: ${{ env.IMAGE_DIGEST }}
push-to-registry: true
- name: attest build provenance (retry)
if: steps.provenance.outcome == 'failure'
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-name: ${{ env.IMAGE }}
subject-digest: ${{ env.IMAGE_DIGEST }}
push-to-registry: true
# actions/attest-sbom is deprecated; actions/attest is its successor and
# accepts the same inputs (sbom-path creates the SBOM attestation).
- name: attest SBOM
id: sbom
continue-on-error: true
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
with:
subject-name: ${{ env.IMAGE }}
subject-digest: ${{ env.IMAGE_DIGEST }}
sbom-path: sbom.spdx.json
push-to-registry: true
- name: attest SBOM (retry)
if: steps.sbom.outcome == 'failure'
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
with:
subject-name: ${{ env.IMAGE }}
subject-digest: ${{ env.IMAGE_DIGEST }}
sbom-path: sbom.spdx.json
push-to-registry: true
- name: log out of GHCR
if: always()
run: docker logout "$REGISTRY"
release:
needs: images
runs-on: ubuntu-latest
permissions:
contents: write # create the GitHub Release
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
# Pull just this version's section out of CHANGELOG.md for the release body
# (everything between this `## [x.y.z]` heading and the next `## [`).
- name: extract changelog section
run: |
version=${GITHUB_REF_NAME#v}
awk -v v="$version" '
$0 ~ "^## \\[" v "\\]" { grab = 1; next }
grab && /^## \[/ { exit }
grab { print }
' CHANGELOG.md > release-notes.md
if [ ! -s release-notes.md ]; then
echo "ERROR: no CHANGELOG.md section for $version" >&2
exit 1
fi
- name: create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes-file release-notes.md