From f45cf3d1e52f845d444f222b4d71993f12f06ad4 Mon Sep 17 00:00:00 2001 From: Anurag Bandyopadhyay Date: Fri, 19 Jun 2026 22:20:50 +0530 Subject: [PATCH 1/5] fix: attach SLSA provenance to draft release via gh release upload The slsa-github-generator (pinned at v2.1.0) attaches provenance using softprops/action-gh-release@v2.2.1, whose draft lookup reassigns the match on every page of releases instead of breaking on first hit. Once a repo crosses 100 releases (2 pages), the fresh draft on page 1 is clobbered by the empty page 2, the action falls through to createRelease(), and two drafts end up on the same tag. Set upload-assets: false so the generator no longer touches the release, and add a release-provenance job that downloads the provenance artifact and attaches it to goreleaser's single draft via 'gh release upload', which resolves the draft deterministically by tag. --- .github/workflows/main.yaml | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 6ab2c675..b44b5bdd 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -237,9 +237,34 @@ jobs: with: base64-subjects-as-file: "${{ needs.goreleaser.outputs.subjects-as-file }}" provenance-name: "fga.intoto.jsonl" - upload-assets: true - upload-tag-name: ${{ github.ref_name }} - draft-release: true + # Do NOT let the generator attach the provenance to the release. It would call + # softprops/action-gh-release@v2.2.1, whose draft lookup reassigns the match on every + # page of releases instead of breaking on first hit. Once this repo has >100 releases + # (2 pages), the fresh draft on page 1 is clobbered by the empty page 2, the action falls + # through to createRelease(), and we end up with two drafts on the same tag. The provenance + # is always uploaded as a workflow artifact regardless, so we attach it ourselves below via + # `gh release upload`, which resolves the draft deterministically by tag. + upload-assets: false + + release-provenance: + needs: [ binary-provenance ] + runs-on: ubuntu-latest + permissions: + contents: write # To add the provenance asset to the draft release. + steps: + - name: Download the provenance + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: ${{ needs.binary-provenance.outputs.provenance-name }} + + - name: Attach provenance to the draft release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PROVENANCE: ${{ needs.binary-provenance.outputs.provenance-name }} + run: | + set -euo pipefail + gh release upload "$GITHUB_REF_NAME" "$PROVENANCE" \ + --repo "$GITHUB_REPOSITORY" --clobber image-provenance: needs: [ goreleaser ] @@ -258,7 +283,7 @@ jobs: registry-password: ${{ secrets.DOCKERHUB_TOKEN }} undraft-release: - needs: [ binary-provenance, image-provenance ] + needs: [ release-provenance, image-provenance ] permissions: contents: write uses: openfga/.github/.github/workflows/undraft-release.yml@main From ecddc7d19ee57d2d93273b06f910c271f0a8a8db Mon Sep 17 00:00:00 2001 From: Anurag Bandyopadhyay Date: Fri, 19 Jun 2026 23:22:37 +0530 Subject: [PATCH 2/5] chore: trim provenance bypass comment to a minimal bullet list --- .github/workflows/main.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index b44b5bdd..b93f5c95 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -237,13 +237,11 @@ jobs: with: base64-subjects-as-file: "${{ needs.goreleaser.outputs.subjects-as-file }}" provenance-name: "fga.intoto.jsonl" - # Do NOT let the generator attach the provenance to the release. It would call - # softprops/action-gh-release@v2.2.1, whose draft lookup reassigns the match on every - # page of releases instead of breaking on first hit. Once this repo has >100 releases - # (2 pages), the fresh draft on page 1 is clobbered by the empty page 2, the action falls - # through to createRelease(), and we end up with two drafts on the same tag. The provenance - # is always uploaded as a workflow artifact regardless, so we attach it ourselves below via - # `gh release upload`, which resolves the draft deterministically by tag. + # Don't let the generator attach the provenance: + # - it uses softprops/action-gh-release@v2.2.1, whose paginated draft lookup + # creates a duplicate draft once a repo has >100 releases (2 pages) + # - the provenance is still published as a workflow artifact, so the + # release-provenance job attaches it via `gh release upload` (by tag) upload-assets: false release-provenance: From 707aed8768b53b3ce5567ad8cbf20a852d4782d1 Mon Sep 17 00:00:00 2001 From: Anurag Bandyopadhyay Date: Fri, 19 Jun 2026 23:40:31 +0530 Subject: [PATCH 3/5] chore: drop contents:write from binary-provenance (least privilege) With upload-assets: false the generator's upload-assets job (the only one needing contents: write) is skipped, so contents: read suffices. --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index b93f5c95..7481a42f 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -230,7 +230,7 @@ jobs: permissions: actions: read # To read the workflow path. id-token: write # To sign the provenance. - contents: write # To add assets to a release. + contents: read # Note: this _must_ be referenced by tag. See: https://github.com/slsa-framework/slsa-verifier/issues/12 uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 From 7e164c3d590c82dcc67bbd1d63b4ca0b4b619f5e Mon Sep 17 00:00:00 2001 From: SoulPancake Date: Sat, 20 Jun 2026 08:00:06 +0530 Subject: [PATCH 4/5] feat: add a 10 min timeout for download and upload release --- .github/workflows/main.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 7481a42f..7e5c42ab 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -247,6 +247,7 @@ jobs: release-provenance: needs: [ binary-provenance ] runs-on: ubuntu-latest + timeout-minutes: 10 permissions: contents: write # To add the provenance asset to the draft release. steps: From 161461516dbd532463eb5e922bce100497a5eef3 Mon Sep 17 00:00:00 2001 From: SoulPancake Date: Sat, 20 Jun 2026 08:48:53 +0530 Subject: [PATCH 5/5] fix: restore contents: write perm to bin provennace job --- .github/workflows/main.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 7e5c42ab..db2f755a 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -230,7 +230,8 @@ jobs: permissions: actions: read # To read the workflow path. id-token: write # To sign the provenance. - contents: read + # write required even with upload-assets:false due to slsa-framework/slsa-github-generator#2044 + contents: write # Note: this _must_ be referenced by tag. See: https://github.com/slsa-framework/slsa-verifier/issues/12 uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0