|
7 | 7 | # on a bit-identical rebuild of all eight (a non-reproducible image fails the job |
8 | 8 | # before anything is published), generates a CycloneDX SBOM, hashes everything |
9 | 9 | # into SHA256SUMS, signs that with keyless cosign (sigstore/Fulcio via OIDC — no |
10 | | -# private key), and creates the GitHub Release as a DRAFT. |
| 10 | +# private key), attests GitHub build provenance for every .uf2, and publishes the |
| 11 | +# GitHub Release. |
11 | 12 | # |
12 | | -# `provenance` emits SLSA build provenance for every released artifact via the |
13 | | -# slsa-github-generator reusable workflow (also keyless) and uploads it to the |
14 | | -# draft: a consumer can verify which workflow, at which commit, on which runner |
15 | | -# built each .uf2. |
16 | | -# |
17 | | -# `publish` un-drafts the release once the provenance is attached — a release |
18 | | -# becomes immutable on publish, so the provenance must land while it's a draft. |
| 13 | +# The provenance is a GitHub attestation (Sigstore-signed, in the attestation API |
| 14 | +# + the public Rekor log), NOT a release asset — so it stays compatible with |
| 15 | +# immutable releases. A consumer verifies which workflow / commit / runner built |
| 16 | +# each .uf2 with `gh attestation verify` (docs/supply-chain.md). |
19 | 17 | # |
20 | 18 | # The .uf2 images are UNSIGNED for secure boot — cosign + the SLSA provenance |
21 | 19 | # attest the BUILD, not the boot seal. On a secure-boot device, seal an image |
|
43 | 41 | timeout-minutes: 150 |
44 | 42 | permissions: |
45 | 43 | contents: write # create the release + upload assets |
46 | | - id-token: write # keyless cosign (OIDC token from Fulcio) |
47 | | - outputs: |
48 | | - hashes: ${{ steps.hashes.outputs.hashes }} |
49 | | - tag: ${{ steps.tag.outputs.tag }} |
| 44 | + id-token: write # keyless cosign + the attestation's Fulcio OIDC token |
| 45 | + attestations: write # GitHub build-provenance attestation (immutable-safe) |
50 | 46 | steps: |
51 | 47 | - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 |
52 | 48 | with: |
@@ -129,13 +125,14 @@ jobs: |
129 | 125 | sha256sum ./*.uf2 ./*.cdx.json > SHA256SUMS |
130 | 126 | cat SHA256SUMS |
131 | 127 |
|
132 | | - - name: subjects for SLSA provenance |
133 | | - id: hashes |
134 | | - run: | |
135 | | - # base64(sha256sum) over the released build artifacts (.uf2 + SBOM); |
136 | | - # clean names (no ./) so `slsa-verifier verify-artifact <file>` matches. |
137 | | - cd dist |
138 | | - echo "hashes=$(sha256sum ./*.uf2 ./*.cdx.json | sed 's, \./, ,' | base64 -w0)" >> "$GITHUB_OUTPUT" |
| 128 | + - name: attest build provenance |
| 129 | + # GitHub-native build provenance for every .uf2 — signed keyless via |
| 130 | + # Sigstore/Fulcio against this run's OIDC identity, recorded in the GitHub |
| 131 | + # attestation API + the public Rekor log (NOT a release asset, so it is |
| 132 | + # compatible with immutable releases). Verify with `gh attestation verify`. |
| 133 | + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 |
| 134 | + with: |
| 135 | + subject-path: dist/*.uf2 |
139 | 136 |
|
140 | 137 | - name: sign SHA256SUMS (keyless cosign) |
141 | 138 | env: |
@@ -163,53 +160,12 @@ jobs: |
163 | 160 | } >> release-notes.md |
164 | 161 | cat release-notes.md |
165 | 162 |
|
166 | | - - name: create the GitHub Release (draft — published after provenance) |
| 163 | + - name: create the GitHub Release |
167 | 164 | env: |
168 | 165 | GH_TOKEN: ${{ github.token }} |
169 | 166 | run: | |
170 | 167 | tag="${{ steps.tag.outputs.tag }}" |
171 | 168 | gh release create "$tag" \ |
172 | 169 | --title "RS-Key $tag" \ |
173 | 170 | --notes-file release-notes.md \ |
174 | | - --draft \ |
175 | 171 | dist/* |
176 | | -
|
177 | | - # SLSA build provenance for every released artifact, generated + signed in the |
178 | | - # SLSA project's trusted reusable workflow (keyless, no private key). The .uf2 |
179 | | - # subjects come from the build job; the provenance is uploaded to the same |
180 | | - # release. Verify with `slsa-verifier` (docs/supply-chain.md). |
181 | | - # |
182 | | - # NB: pinned by version TAG, not a commit SHA — the one `uses:` here that is |
183 | | - # intentionally not SHA-pinned. The generator verifies its own ref, and |
184 | | - # slsa-verifier checks the builder id |
185 | | - # `…/generator_generic_slsa3.yml@refs/tags/v2.1.0`; a SHA pin breaks both. |
186 | | - provenance: |
187 | | - needs: [build] |
188 | | - permissions: |
189 | | - actions: read # read the workflow run that built the artifacts |
190 | | - id-token: write # keyless signing of the provenance (Fulcio OIDC) |
191 | | - contents: write # attach the provenance to the release |
192 | | - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 |
193 | | - with: |
194 | | - base64-subjects: "${{ needs.build.outputs.hashes }}" |
195 | | - upload-assets: true |
196 | | - upload-tag-name: "${{ needs.build.outputs.tag }}" |
197 | | - provenance-name: "rs-key-${{ needs.build.outputs.tag }}.intoto.jsonl" |
198 | | - |
199 | | - # Publish (un-draft) the release only after the provenance asset is attached. |
200 | | - # A release becomes immutable on publish, so the draft must carry everything — |
201 | | - # artifacts (build) and provenance (provenance) — before this flips it live. |
202 | | - publish: |
203 | | - needs: [build, provenance] |
204 | | - runs-on: ubuntu-latest |
205 | | - timeout-minutes: 10 |
206 | | - permissions: |
207 | | - contents: write # publish (un-draft) the release |
208 | | - steps: |
209 | | - - name: publish the draft release |
210 | | - env: |
211 | | - GH_TOKEN: ${{ github.token }} |
212 | | - run: | |
213 | | - gh release edit "${{ needs.build.outputs.tag }}" \ |
214 | | - --repo "${{ github.repository }}" \ |
215 | | - --draft=false |
0 commit comments