-
Notifications
You must be signed in to change notification settings - Fork 0
192 lines (174 loc) · 7.64 KB
/
Copy pathrelease.yml
File metadata and controls
192 lines (174 loc) · 7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
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