8888 persist-credentials : false
8989
9090 - id : shard
91- uses : stackabletech/actions/shard@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
91+ uses : stackabletech/actions/shard@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
9292 with :
9393 product-name : ${{ inputs.product-name }}
9494 boil-version : 0.2.2
@@ -116,11 +116,11 @@ jobs:
116116 persist-credentials : false
117117
118118 - name : Free Disk Space
119- uses : stackabletech/actions/free-disk-space@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
119+ uses : stackabletech/actions/free-disk-space@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
120120
121121 - name : Build Product Image
122122 id : build
123- uses : stackabletech/actions/build-product-image@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
123+ uses : stackabletech/actions/build-product-image@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
124124 with :
125125 registry-namespace : ${{ inputs.registry-namespace }}
126126 product-name : ${{ inputs.product-name }}
@@ -130,7 +130,7 @@ jobs:
130130
131131 - name : Publish Container Image on oci.stackable.tech
132132 if : inputs.publish
133- uses : stackabletech/actions/publish-image@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
133+ uses : stackabletech/actions/publish-image@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
134134 with :
135135 image-registry-uri : oci.stackable.tech
136136 image-registry-username : robot$${{ inputs.registry-namespace }}+github-action-build
@@ -146,7 +146,7 @@ jobs:
146146
147147 - name : Publish Container Image on quay.io
148148 if : inputs.publish && inputs.publish-to-quay
149- uses : stackabletech/actions/publish-image@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
149+ uses : stackabletech/actions/publish-image@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
150150 with :
151151 image-registry-uri : quay.io
152152 image-registry-username : stackable+robot_${{ inputs.registry-namespace }}_github_action_build
@@ -178,8 +178,9 @@ jobs:
178178 persist-credentials : false
179179
180180 - name : Publish and Sign Image Index Manifest to oci.stackable.tech
181+ id : publish-oci
181182 if : inputs.publish
182- uses : stackabletech/actions/publish-image-index-manifest@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
183+ uses : stackabletech/actions/publish-image-index-manifest@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
183184 with :
184185 image-registry-uri : oci.stackable.tech
185186 image-registry-username : robot$${{ inputs.registry-namespace }}+github-action-build
@@ -193,8 +194,9 @@ jobs:
193194 image-index-manifest-tag : ${{ matrix.versions }}-stackable${{ inputs.sdp-version }}
194195
195196 - name : Publish and Sign Image Index Manifest to quay.io
197+ id : publish-quay
196198 if : inputs.publish && inputs.publish-to-quay
197- uses : stackabletech/actions/publish-image-index-manifest@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
199+ uses : stackabletech/actions/publish-image-index-manifest@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
198200 with :
199201 image-registry-uri : quay.io
200202 image-registry-username : stackable+robot_${{ inputs.registry-namespace }}_github_action_build
@@ -207,16 +209,127 @@ jobs:
207209 image-repository : stackable/${{ inputs.registry-namespace }}/${{ inputs.image-name || inputs.product-name }}
208210 image-index-manifest-tag : ${{ matrix.versions }}-stackable${{ inputs.sdp-version }}
209211
212+ # Record the pushed index manifest digest(s) for this matrix version so the
213+ # provenance jobs can pick them up. Matrix jobs cannot expose per-version
214+ # outputs (they overwrite each other), so we pass the digests through
215+ # artifacts instead.
216+ - name : Record image index digests for provenance
217+ if : inputs.publish
218+ shell : bash
219+ env :
220+ VERSION : ${{ matrix.versions }}
221+ OCI_DIGEST : ${{ steps.publish-oci.outputs.image-index-manifest-digest }}
222+ QUAY_DIGEST : ${{ steps.publish-quay.outputs.image-index-manifest-digest }}
223+ run : |
224+ set -euo pipefail
225+ jq -n \
226+ --arg version "$VERSION" \
227+ --arg oci "$OCI_DIGEST" \
228+ --arg quay "$QUAY_DIGEST" \
229+ '{version: $version, oci_digest: $oci, quay_digest: $quay}' > digests.json
230+
231+ - name : Upload provenance digests
232+ if : inputs.publish
233+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
234+ with :
235+ name : provenance-digests-${{ matrix.versions }}
236+ path : digests.json
237+ retention-days : 1
238+
239+ # Collect the per-version index manifest digests uploaded by publish_manifests
240+ # and turn them into a matrix (one entry per version and registry) for the
241+ # provenance job. This is needed because a matrix job cannot expose per-leg
242+ # outputs directly.
243+ collect-provenance-matrix :
244+ name : Collect Provenance Matrix
245+ if : inputs.publish
246+ needs : [publish_manifests]
247+ permissions :
248+ contents : read
249+ runs-on : ubuntu-latest
250+ outputs :
251+ matrix : ${{ steps.build-matrix.outputs.matrix }}
252+ has-entries : ${{ steps.build-matrix.outputs.has_entries }}
253+ steps :
254+ - name : Download provenance digests
255+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
256+ with :
257+ pattern : provenance-digests-*
258+ path : artifacts
259+
260+ - name : Build provenance matrix
261+ id : build-matrix
262+ shell : bash
263+ env :
264+ REGISTRY_NAMESPACE : ${{ inputs.registry-namespace }}
265+ PRODUCT_NAME : ${{ inputs.product-name }}
266+ IMAGE_NAME : ${{ inputs.image-name }}
267+ run : |
268+ set -euo pipefail
269+ IMAGE="${IMAGE_NAME:-$PRODUCT_NAME}"
270+
271+ # Build a JSON matrix include array. Each version contributes one entry
272+ # for oci.stackable.tech and, if a quay digest was recorded, one for
273+ # quay.io. The slsa generator attaches provenance by digest, so no tag
274+ # is needed in the image reference.
275+ INCLUDE=$(jq -c -s \
276+ --arg ns "$REGISTRY_NAMESPACE" \
277+ --arg img "$IMAGE" '
278+ [ .[]
279+ | ( { registry: "oci.stackable.tech",
280+ image: ("oci.stackable.tech/" + $ns + "/" + $img),
281+ digest: .oci_digest,
282+ username: ("robot$" + $ns + "+github-action-build") } ),
283+ ( if (.quay_digest // "") != ""
284+ then { registry: "quay.io",
285+ image: ("quay.io/stackable/" + $ns + "/" + $img),
286+ digest: .quay_digest,
287+ username: ("stackable+robot_" + $ns + "_github_action_build") }
288+ else empty end )
289+ ]' artifacts/*/digests.json)
290+
291+ echo "matrix={\"include\":$INCLUDE}" | tee -a "$GITHUB_OUTPUT"
292+ if [ "$INCLUDE" = "[]" ]; then
293+ echo "has_entries=false" | tee -a "$GITHUB_OUTPUT"
294+ else
295+ echo "has_entries=true" | tee -a "$GITHUB_OUTPUT"
296+ fi
297+
298+ # Generate SLSA build provenance for every published multi-arch image index
299+ # (one matrix entry per version and registry) and attach it to the image. The
300+ # reusable workflow signs the provenance with keyless signing (GitHub Actions
301+ # as the OIDC identity) and pushes the attestation next to the image.
302+ provenance :
303+ name : Generate Provenance (${{ matrix.image }}@${{ matrix.digest }})
304+ if : inputs.publish && needs.collect-provenance-matrix.outputs.has-entries == 'true'
305+ needs : [collect-provenance-matrix]
306+ permissions :
307+ actions : read # detect the build workflow that generated the image
308+ id-token : write # mint the OIDC token for keyless signing
309+ packages : write # needed until https://github.com/slsa-framework/slsa-github-generator/issues/1257 is resolved
310+ strategy :
311+ fail-fast : false
312+ matrix : ${{ fromJson(needs.collect-provenance-matrix.outputs.matrix) }}
313+ # MUST be referenced by a @vX.Y.Z tag (not a SHA), otherwise the reusable
314+ # workflow cannot verify its own provenance.
315+ uses : slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
316+ with :
317+ image : ${{ matrix.image }}
318+ digest : ${{ matrix.digest }}
319+ registry-username : ${{ matrix.username }}
320+ secrets :
321+ registry-password : ${{ matrix.registry == 'quay.io' && secrets.quay-robot-secret || secrets.harbor-robot-secret }}
322+
210323 notify :
211324 name : Failure Notification
212- needs : [generate_version_dimension, build, publish_manifests]
325+ needs : [generate_version_dimension, build, publish_manifests, collect-provenance-matrix, provenance ]
213326 runs-on : ubuntu-latest
214327 # TODO (@NickLarsenNZ): Allow a condition from input so that we can always
215328 # be notified of new builds for precompiled product images.
216329 if : failure() || (github.run_attempt > 1 && !cancelled())
217330 steps :
218331 - name : Send Notification
219- uses : stackabletech/actions/send-slack-notification@1b8db26b7406c66a7ab74e344a9e54edb2bc2690 # v0.14.3
332+ uses : stackabletech/actions/send-slack-notification@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0
220333 with :
221334 publish-manifests-result : ${{ needs.publish_manifests.result }}
222335 build-result : ${{ needs.build.result }}
0 commit comments