diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 059951f..b2f53d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -293,6 +293,39 @@ jobs: | sort -k2 > SHA256SUMS cat SHA256SUMS + # ORDER MATTERS: everything that belongs on the Release, and the + # provenance for it, is produced BEFORE the Release exists. Releases in + # this repository are immutable — publishing one freezes its assets — so + # the old order (create with SHA256SUMS, then upload the SBOM, then + # attest) failed with "HTTP 422: Cannot upload assets to an immutable + # release" at the SBOM upload, and the attestation step behind it never + # ran. v0.4.3 through v0.4.6 shipped with SHA256SUMS only and no + # attestation, while the comment below told users to verify one. + + # SPDX JSON SBOM of the released commit. Written to a file and attached + # at creation rather than by the action itself: its own release upload + # only works on `release` events, and this workflow runs on push. + - name: Generate SBOM (SPDX JSON) + uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2 + with: + path: . + format: spdx-json + output-file: sbom.spdx.json + upload-artifact: false + upload-release-assets: false + + # Provenance for what users actually run: every installer listed in + # SHA256SUMS, by digest. Verify one with + # gh attestation verify install.sh --repo resq-software/dev + # Attestations are stored against the repository, not the Release, so + # this step does not depend on it — and running it first means a + # workflow_dispatch re-run still attests a version whose Release + # already exists. + - name: Attest build provenance + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-checksums: SHA256SUMS + - name: Create GitHub Release env: GH_TOKEN: ${{ github.token }} @@ -300,10 +333,11 @@ jobs: set -euo pipefail # Idempotent alongside the tag step: a re-run must not fail because - # the release already exists. + # the release already exists. An immutable Release cannot be changed, + # so there is nothing to refresh; say what it carries and stop. if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then - echo "release $TAG already exists; refreshing SHA256SUMS only" - gh release upload "$TAG" SHA256SUMS --repo "$GITHUB_REPOSITORY" --clobber + assets="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '[.assets[].name] | join(", ")')" + echo "release $TAG already exists and is immutable; leaving it as is (assets: ${assets:-none})" exit 0 fi @@ -335,37 +369,12 @@ jobs: cat SHA256SUMS echo '```' } > NOTES.md - gh release create "$TAG" SHA256SUMS \ + # Every asset in the one call that publishes: after it, none can be added. + gh release create "$TAG" SHA256SUMS sbom.spdx.json \ --repo "$GITHUB_REPOSITORY" \ --title "$TAG" \ --notes-file NOTES.md - # SPDX JSON SBOM of the released commit. Written to a file and uploaded - # below rather than by the action itself: its own release upload only - # works on `release` events, and this workflow runs on push. - - name: Generate SBOM (SPDX JSON) - uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2 - with: - path: . - format: spdx-json - output-file: sbom.spdx.json - upload-artifact: false - upload-release-assets: false - - - name: Attach the SBOM to the Release - env: - GH_TOKEN: ${{ github.token }} - # --clobber keeps a re-run idempotent, like the SHA256SUMS refresh above. - run: gh release upload "$TAG" sbom.spdx.json --repo "$GITHUB_REPOSITORY" --clobber - - # Provenance for what users actually run: every installer listed in - # SHA256SUMS, by digest. Verify one with - # gh attestation verify install.sh --repo resq-software/dev - - name: Attest build provenance - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 - with: - subject-checksums: SHA256SUMS - bump: name: propose pin bump needs: verify