|
5 | 5 | # |
6 | 6 | # Triggered by pushing one of THIS repo's own release tags — bare semver, NO |
7 | 7 | # 'v' prefix (e.g. 11.0.8), matching the bundled GLPI version. It builds the |
8 | | -# multi-arch image through docker-bake.hcl, pushes the canonical tag set |
9 | | -# (<version>, <major.minor>, <major>, latest), cosign-signs each tag, and |
10 | | -# publishes a GitHub Release with auto-categorized notes (.github/release.yml). |
11 | | -# |
12 | | -# The daily build.yml keeps the same floating tags fresh against new base-image |
13 | | -# CVEs; this workflow is the signed, release-noted cut for a specific GLPI |
14 | | -# version. Tagging is done locally (signed tag) — see github-release flow. |
| 8 | +# multi-arch image through docker-bake.hcl, pushes + signs the canonical tag |
| 9 | +# set, emits SBOM + provenance, and publishes a GitHub Release — all via the |
| 10 | +# shared netresearch/.github reusables so no external action is pinned here. |
15 | 11 |
|
16 | 12 | name: Release |
17 | 13 |
|
18 | 14 | on: |
19 | 15 | push: |
20 | | - # Bare semver, no 'v' prefix. In GitHub's tag filter syntax `+` means |
21 | | - # "one or more of the preceding character", so [0-9]+ is one-or-more digits |
22 | | - # and a leading 'v' would NOT match — enforcing the no-v-prefix convention. |
| 16 | + # Bare semver, no 'v' prefix (matches the bundled GLPI version). |
23 | 17 | tags: |
24 | 18 | - '[0-9]+.[0-9]+.[0-9]+' |
25 | 19 |
|
26 | | -# Deny by default; each job opts into exactly what it needs. |
27 | 20 | permissions: {} |
28 | 21 |
|
29 | | -env: |
30 | | - REGISTRY: ghcr.io |
31 | | - IMAGE_NAME: ${{ github.repository_owner }}/glpi-php-fpm |
32 | | - |
33 | 22 | jobs: |
34 | | - release: |
35 | | - name: build, sign & release |
| 23 | + # Resolve version facts for the release step (the build derives its own via |
| 24 | + # pre-build-command). No external actions — just the runner's gh CLI. |
| 25 | + version: |
| 26 | + name: Resolve version |
36 | 27 | runs-on: ubuntu-latest |
37 | 28 | permissions: |
38 | | - contents: write # create the GitHub Release |
39 | | - packages: write # push image to ghcr.io |
40 | | - id-token: write # keyless cosign signing + provenance (OIDC) |
41 | | - attestations: write # SLSA build provenance |
| 29 | + contents: read |
| 30 | + outputs: |
| 31 | + version: ${{ steps.v.outputs.version }} |
| 32 | + is_newest: ${{ steps.v.outputs.is_newest }} |
42 | 33 | steps: |
43 | | - - name: Checkout |
44 | | - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
45 | | - with: |
46 | | - persist-credentials: false |
47 | | - |
48 | | - # QEMU so a single bake call can emit linux/arm64 alongside the native |
49 | | - # amd64 runner. (The daily build.yml uses native per-arch runners instead; |
50 | | - # for a once-per-release cut, emulation is the simpler tradeoff.) |
51 | | - - name: Set up QEMU |
52 | | - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 |
53 | | - |
54 | | - - name: Set up Docker Buildx |
55 | | - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 |
56 | | - |
57 | | - - name: Log in to ghcr.io |
58 | | - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 |
59 | | - with: |
60 | | - registry: ${{ env.REGISTRY }} |
61 | | - username: ${{ github.actor }} |
62 | | - password: ${{ secrets.GITHUB_TOKEN }} |
63 | | - |
64 | | - - name: Derive version facts from tag |
65 | | - id: version |
| 34 | + - id: v |
66 | 35 | env: |
67 | | - # github.ref_name is the pushed tag. Route it through an env var |
68 | | - # rather than interpolating into the script body (sonarcloud |
69 | | - # githubactions:S7630, shell injection). |
70 | 36 | REF_NAME: ${{ github.ref_name }} |
71 | 37 | REPO: ${{ github.repository }} |
72 | | - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 38 | + GH_TOKEN: ${{ github.token }} |
73 | 39 | run: | |
74 | 40 | set -euo pipefail |
75 | | - # Defensive 'v' strip even though the tag filter already excludes it. |
76 | | - VERSION="${REF_NAME#v}" |
77 | | - # Is this the newest semver tag in the repo? An out-of-order / backport |
78 | | - # release must NOT move the GH "Latest" badge (and ideally not the |
79 | | - # floating image tags) onto an older version. |
| 41 | + V="$REF_NAME" |
| 42 | + # An out-of-order / backport release must NOT move the "Latest" badge |
| 43 | + # onto an older version. |
80 | 44 | NEWEST=$(gh api "repos/${REPO}/git/refs/tags" \ |
81 | 45 | --jq '.[].ref | ltrimstr("refs/tags/")' 2>/dev/null \ |
82 | 46 | | sed 's/^v//' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' \ |
83 | 47 | | sort -V | tail -1) |
84 | 48 | IS_NEWEST=true |
85 | | - [ -n "$NEWEST" ] && [ "$VERSION" != "$NEWEST" ] && IS_NEWEST=false |
| 49 | + if [ -n "$NEWEST" ] && [ "$V" != "$NEWEST" ]; then |
| 50 | + IS_NEWEST=false |
| 51 | + fi |
86 | 52 | { |
87 | | - echo "version=$VERSION" |
88 | | - echo "major=$(echo "$VERSION" | cut -d. -f1)" |
89 | | - echo "minor=$(echo "$VERSION" | cut -d. -f1-2)" |
| 53 | + echo "version=$V" |
90 | 54 | echo "is_newest=$IS_NEWEST" |
91 | | - echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" |
92 | | - echo "git_sha=$(git rev-parse --short HEAD)" |
93 | 55 | } >> "$GITHUB_OUTPUT" |
94 | 56 |
|
95 | | - - name: Resolve GLPI tarball sha256 (supply-chain pin) |
96 | | - id: sha |
97 | | - env: |
98 | | - VERSION: ${{ steps.version.outputs.version }} |
99 | | - run: | |
100 | | - set -euo pipefail |
101 | | - # The release tag IS the bundled GLPI version, so hash that exact |
102 | | - # upstream asset and pin the build to it — a swapped release artifact |
103 | | - # cannot make it into a signed image. |
104 | | - URL="https://github.com/glpi-project/glpi/releases/download/${VERSION}/glpi-${VERSION}.tgz" |
105 | | - GLPI_SHA256=$(curl -fsSL "$URL" | sha256sum | cut -d' ' -f1) |
106 | | - [ -n "$GLPI_SHA256" ] || { echo "could not hash $URL" >&2; exit 1; } |
107 | | - echo "glpi_sha256=$GLPI_SHA256" >> "$GITHUB_OUTPUT" |
108 | | - echo "─ GLPI ${VERSION} sha256=${GLPI_SHA256:0:16}…" |
109 | | -
|
110 | | - - name: Build and push (multi-arch, via docker-bake.hcl) |
111 | | - uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0 |
112 | | - with: |
113 | | - # Read ONLY the bake file — bake would otherwise also merge the |
114 | | - # sibling compose.yml (extra targets + `${TZ}` interpolation warnings). |
115 | | - files: docker-bake.hcl |
116 | | - # The `default` group resolves to the single `glpi` target. |
117 | | - targets: glpi |
118 | | - push: true |
119 | | - # Attestations are declared in docker-bake.hcl (type=provenance,mode=max |
120 | | - # + type=sbom); re-asserting them here makes the release build's |
121 | | - # supply-chain posture explicit and independent of HCL edits. |
122 | | - provenance: mode=max |
123 | | - sbom: true |
124 | | - env: |
125 | | - # Bake reads env vars matching its `variable` names. |
126 | | - GLPI_VERSION: ${{ steps.version.outputs.version }} |
127 | | - GLPI_MINOR: ${{ steps.version.outputs.minor }} |
128 | | - GLPI_MAJOR: ${{ steps.version.outputs.major }} |
129 | | - GLPI_SHA256: ${{ steps.sha.outputs.glpi_sha256 }} |
130 | | - BUILD_DATE: ${{ steps.version.outputs.build_date }} |
131 | | - GIT_SHA: ${{ steps.version.outputs.git_sha }} |
132 | | - |
133 | | - - name: Install Cosign |
134 | | - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 |
135 | | - |
136 | | - - name: Sign the pushed tags (keyless OIDC) |
137 | | - env: |
138 | | - IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} |
139 | | - VERSION: ${{ steps.version.outputs.version }} |
140 | | - MINOR: ${{ steps.version.outputs.minor }} |
141 | | - MAJOR: ${{ steps.version.outputs.major }} |
142 | | - run: | |
143 | | - set -euo pipefail |
144 | | - # All four canonical tags point at the same multi-arch index digest. |
145 | | - # --recursive also signs the per-arch child manifests, so arch-pinned |
146 | | - # `cosign verify` of a resolved digest stays green too. |
147 | | - for tag in "$VERSION" "$MINOR" "$MAJOR" latest; do |
148 | | - cosign sign --yes --recursive "${IMAGE}:${tag}" |
149 | | - done |
| 57 | + # Multi-arch build (QEMU) + push + sign every produced tag + SBOM + provenance. |
| 58 | + # The GLPI tarball sha256 is resolved in pre-build-command and pins the build. |
| 59 | + build: |
| 60 | + needs: version |
| 61 | + uses: netresearch/.github/.github/workflows/build-container-bake.yml@main |
| 62 | + permissions: |
| 63 | + contents: read |
| 64 | + packages: write |
| 65 | + security-events: write |
| 66 | + id-token: write |
| 67 | + with: |
| 68 | + bake-file: docker-bake.hcl |
| 69 | + targets: glpi |
| 70 | + push: true |
| 71 | + sign: true |
| 72 | + sbom: true |
| 73 | + attest: true |
| 74 | + scan: false |
| 75 | + pre-build-command: | |
| 76 | + set -euo pipefail |
| 77 | + V="$GITHUB_REF_NAME" |
| 78 | + # The release tag IS the bundled GLPI version — hash that exact upstream |
| 79 | + # asset and pin the build to it (a swapped artifact cannot be signed). |
| 80 | + URL="https://github.com/glpi-project/glpi/releases/download/${V}/glpi-${V}.tgz" |
| 81 | + GLPI_SHA256=$(curl -fsSL "$URL" | sha256sum | cut -d' ' -f1) |
| 82 | + [ -n "$GLPI_SHA256" ] || { echo "could not hash $URL" >&2; exit 1; } |
| 83 | + { |
| 84 | + echo "GLPI_VERSION=$V" |
| 85 | + echo "GLPI_SHA256=$GLPI_SHA256" |
| 86 | + echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" |
| 87 | + echo "GIT_SHA=$(git rev-parse --short HEAD)" |
| 88 | + } >> "$GITHUB_ENV" |
150 | 89 |
|
151 | | - - name: Create GitHub Release |
152 | | - uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 |
153 | | - with: |
154 | | - # Auto-categorized from .github/release.yml; prepend the usage block. |
155 | | - generate_release_notes: true |
156 | | - # Only the newest semver release gets the "Latest" badge — a backport |
157 | | - # release of an older line must not steal it (GH marks latest by |
158 | | - # creation time otherwise). |
159 | | - make_latest: ${{ steps.version.outputs.is_newest }} |
160 | | - body: | |
161 | | - ## Container image |
| 90 | + release: |
| 91 | + needs: [version, build] |
| 92 | + uses: netresearch/.github/.github/workflows/gh-release-image.yml@main |
| 93 | + permissions: |
| 94 | + contents: write |
| 95 | + with: |
| 96 | + # Only the newest semver release gets the "Latest" badge. |
| 97 | + make-latest: ${{ needs.version.outputs.is_newest }} |
| 98 | + body: | |
| 99 | + ## Container image |
162 | 100 |
|
163 | | - ```bash |
164 | | - docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} |
165 | | - ``` |
| 101 | + ```bash |
| 102 | + docker pull ghcr.io/netresearch/glpi-php-fpm:${{ needs.version.outputs.version }} |
| 103 | + ``` |
166 | 104 |
|
167 | | - Tags published for this release: `${{ steps.version.outputs.version }}`, |
168 | | - `${{ steps.version.outputs.minor }}`, `${{ steps.version.outputs.major }}`, `latest` |
169 | | - (linux/amd64 + linux/arm64). |
| 105 | + Tags published: `${{ needs.version.outputs.version }}`, `<major.minor>`, |
| 106 | + `<major>`, `latest` (linux/amd64 + linux/arm64). |
170 | 107 |
|
171 | | - ## Verify the signature |
| 108 | + ## Verify the signature |
172 | 109 |
|
173 | | - ```bash |
174 | | - cosign verify ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} \ |
175 | | - --certificate-identity-regexp "https://github.com/${{ github.repository }}" \ |
176 | | - --certificate-oidc-issuer "https://token.actions.githubusercontent.com" |
177 | | - ``` |
| 110 | + ```bash |
| 111 | + cosign verify ghcr.io/netresearch/glpi-php-fpm:${{ needs.version.outputs.version }} \ |
| 112 | + --certificate-identity-regexp "https://github.com/netresearch/glpi-docker-compose-stack" \ |
| 113 | + --certificate-oidc-issuer "https://token.actions.githubusercontent.com" |
| 114 | + ``` |
178 | 115 |
|
179 | | - ## SBOM |
| 116 | + ## SBOM |
180 | 117 |
|
181 | | - ```bash |
182 | | - cosign download sbom ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} |
183 | | - ``` |
| 118 | + ```bash |
| 119 | + cosign download sbom ghcr.io/netresearch/glpi-php-fpm:${{ needs.version.outputs.version }} |
| 120 | + ``` |
184 | 121 |
|
185 | | - --- |
| 122 | + --- |
186 | 123 |
|
187 | | - This image bundles **GLPI ${{ steps.version.outputs.version }}** |
188 | | - (GPL-3.0-or-later). The Docker packaging in this repository is MIT. |
| 124 | + This image bundles **GLPI ${{ needs.version.outputs.version }}** |
| 125 | + (GPL-3.0-or-later). The Docker packaging in this repository is MIT. |
0 commit comments