Repository navigation
[DEVOPS-12319] Publish SDK releases as draft then publish last, and fix SLSA provenance - #2098
Merged
Merged
Conversation
…lish last Immutable releases reject asset uploads once a release is published. The publish workflow now requires the release to be a draft, attaches the wheel and sdist while it is a draft, and publishes the release after PyPI publish succeeds. Replace the SLSA generic generator with actions/attest-build-provenance. The SLSA upload has failed on every release since v7.6.0, so v7.7.0 to v7.17.0 shipped without it. Update the README provenance section and add maintainer release steps to CONTRIBUTING. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore the SLSA generic provenance (multiple.intoto.jsonl) and attach it to the draft release with the wheel and sdist. The generator broke when it was pinned to a SHA in #2051: it must be referenced by tag. Reference v2.1.0 by tag and turn off its own release upload, which cannot target draft releases. Also reference the container SLSA generator by tag, and let it run when production tests are skipped. Restore the slsa-verifier example in the README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abacchilb
requested review from
KeshavSahoo,
cyrusj89,
gmadaan-hue,
ikerr,
john-labelbox,
manuaero and
pablito25sp
November 3, 2026 13:50
The generic generator declares contents: write for its upload job, so the caller must grant it even with upload-assets: false (slsa-framework/slsa-github-generator#2044). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c699ea4. Configure here.
Address Cursor Bugbot review on #2098: - Upload to the draft release only after test-build passes or is skipped, so a failed test run (as on the first v7.17.0 run) leaves the draft empty. - Use --clobber so Re-run failed jobs can retry a partial upload. The release is still a draft at this point. - Set the SLSA provenance name explicitly and download it by that name rather than relying on the generator output. - Document retrying with Re-run failed jobs in CONTRIBUTING. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gabefire
approved these changes
Nov 3, 2026
1 task done
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Description
Prepares the SDK release workflow for GitHub Immutable Releases, and fixes SLSA provenance, which has been missing from every release since v7.6.0.
Jira: DEVOPS-12319 (epic DEVOPS-12318). Turning on the repo setting is the follow-up DEVOPS-12321, after this merges.
Why
Once a release is published with immutable releases on, its tag can't be moved and its assets can't be added, replaced or removed. Today
publish.ymluploads the wheel, sdist and SLSA file to a release that a maintainer has already published by hand, so turning on the setting as things stand would break every SDK release.What changes
Release flow (
publish.yml)check-draft-releasejob runs first. It fails within seconds if the release for the tag is missing or already published, instead of after the production tests.publish-releasejob runs last. It publishes the draft (gh release edit --draft=false --verify-tag) only after the asset upload, the GitHub attestation and the PyPI publish have all succeeded.Provenance
slsa-github-generatorto a commit SHA. The generator has to be called by tag (it uses the tag to fetch and verify its builder, andslsa-verifierchecks it), so its upload has failed ever since: v7.7.0 through v7.17.0 have nomultiple.intoto.jsonl. It is now called as@v2.1.0, with a comment explaining the exception to SHA pinning. Its own release upload is turned off;publish-python-package-to-releaseattaches the file to the draft instead.actions/attest-build-provenance, SHA-pinned v4.2.2), in a newattest_pythonjob.gh attestation verifycan check it.always()patternpypi-publishuses). Before, it was skipped in that case.Docs
CONTRIBUTING.md: maintainer release steps. Push the tag, save the release as a draft (do not publish it), then run the publish workflow.README.md: the Provenance section coversgh attestation verifyandslsa-verifier, and says which past releases have which kind of provenance.Change for maintainers
Save the release as a draft instead of publishing it before running the workflow. The workflow publishes it at the end. If a run fails partway, the release stays a draft and the run can be retried.
Not changed
lbox-publish.ymlstill references the SLSA generator by SHA. It is only called from a job inpublish.ymlthat is commented out.Testing
actionlintreports nothing on the changed lines (only existing shellcheck notes on unchanged steps).generator_generic_slsa3.yml@v2.1.0called by tag withupload-assets: false,gh release upload <tag>to the draft, andgh release edit <tag> --draft=falseat the end. Their workflow also flagged two generator issues, both handled here:contents: writeeven withupload-assets: false(slsa-github-generator#2044). Correction to an earlier version of this PR: it first grantedcontents: read, which would have failed. Fixed in the latest commit.check-draft-releasejob and the workflow being dispatched fromdeveloprather than triggered by a tag push) is checked by the next real release.Type of change
🤖 Generated with Claude Code
Note
Medium Risk
Changes the critical release pipeline (PyPI, GitHub release state, and supply-chain attestation); mistakes could block releases or leave drafts unpublished, though guards and docs reduce operator error.
Overview
Prepares the publish workflow for GitHub immutable releases by requiring a draft release for the tag up front (
check-draft-release), uploading wheel, sdist, and SLSA file to that draft only after prod tests pass, and publishing the release last once GitHub asset upload, artifact attestation, and PyPI publish all succeed.Provenance fixes: SLSA Python/container generators move from a pinned SHA to
@v2.1.0(tag required for generator/verifier), withupload-assets: falsesomultiple.intoto.jsonlis attached viagh release upload --clobberinstead of the generator (avoids duplicate-draft issues on repos with 100+ releases). Addsattest_pythonusingactions/attest-build-provenancefor wheel/sdist. Container SLSA now runs when tests are skipped viaalways()on success.Docs:
CONTRIBUTING.mddocuments the maintainer flow (draft → workflow → auto-publish; retry failed jobs; manual publish if PyPI already has the version).README.mdupdates verification steps (gh attestation verify, simplifiedslsa-verifier) and notes which historical releases include which attestations.Reviewed by Cursor Bugbot for commit c2d8c8f. Bugbot is set up for automated code reviews on this repo. Configure here.