ci: use draft releases to support immutable GitHub releases#516
ci: use draft releases to support immutable GitHub releases#516
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| run: > | ||
| gh release edit "$TAG_NAME" | ||
| --repo ${{ github.repository }} | ||
| --draft=false |
There was a problem hiding this comment.
Server-otel draft release never gets published
High Severity
The global "draft": true setting causes release-please to create draft releases for all packages, and the create-tags job explicitly creates a tag for server-otel when released. However, there is no publish-release-server-otel job to un-draft the release. The server-otel release will remain permanently invisible as a draft on GitHub, which is a regression from pre-PR behavior where release-please published it directly.
Additional Locations (2)
| fi | ||
| shell: bash | ||
| - name: Attest build provenance | ||
| uses: actions/attest@v4 |
There was a problem hiding this comment.
Attest action unpinned unlike all other actions
Medium Severity
All 8 instances of actions/attest@v4 are referenced by a mutable major-version tag, while every other action in both workflows (actions/checkout, googleapis/release-please-action) is pinned to a specific commit SHA. A compromised or force-pushed v4 tag could inject malicious code into the release pipeline, which runs with contents: write, attestations: write, and id-token: write permissions.
Additional Locations (1)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| "plugins": [ | ||
| "node-workspace" | ||
| ], | ||
| "draft": true, |
There was a problem hiding this comment.
Global draft config strands auxiliary packages without tags or publishing
Medium Severity
Top-level "draft": true applies to all 8 packages, but libs/common, libs/internal, libs/networking, and libs/server-sent-events have no entries in the tag-creation loop, no job outputs, and no publish-release-* jobs. Since release-please skips tag creation for drafts, these packages will receive draft releases with no tags and no way to publish. Previously they got non-draft releases with tags automatically. Setting "draft" per-package on only the packages that have publish infrastructure would avoid this regression.


Summary
Migrates the release workflow to support GitHub's immutable releases feature. Once a release is published it can no longer be modified, so we now create releases in draft state, upload all artifacts, and only then publish.
Changes across three files:
release-please-config.json— Added top-level"draft": trueso release-please creates draft releases for all packages. Added"force-tag-creation": trueto every package (not yet supported by release-please, but included for forward compatibility).release-please.yml— Split release-please pattern — release-please is now invoked twice within the same job:skip-github-pull-request: true— only creates releases (no PRs).skip-github-release: true— only creates/updates PRs, and only runs if no releases were created. This ordering ensures tags exist before release-please checks whether a release PR is still needed.release-client,release-server, etc.) now depend only onrelease-please(the former separatecreate-tagsjob has been removed).googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38(v4.4.0).actions/attest@v4— Removed all 7 SLSA provenance jobs (release-{client,server,server-redis}-provenance,release-{client,server,server-redis}-mac-arm64-provenance, plus 2 in the manual workflow). Replaced with inlineactions/attest@v4steps in each build job that decode the base64 hashes into a checksums file and attest in-place.publish-release-*jobs — Three new jobs (publish-release-client,publish-release-server,publish-release-server-redis) that un-draft their respective release only after all artifact jobs complete.manual-sdk-release-artifacts.yml—publish_releaseinput — Added apublish_releaseboolean input (default:true) and apublish-releasejob gated on it, so operators can optionally keep the release in draft after manual artifact uploads.Review & Testing Checklist for Human
ifcondition on the second call correctly uses!= 'true'with&&across all 4 packages.needsarrays inpublish-release-*jobs: Verify each publish job waits on ALL artifact-uploading jobs for that package before un-drafting. A missing dependency means the release gets published before all artifacts are uploaded — the exact problem we're solving. For example,publish-release-clientneeds bothrelease-client(3-OS matrix) andrelease-client-mac-arm64.release-pleasecreates a tag for server-otel if released, and"draft": truemeans release-please creates a draft release, but there is nopublish-release-server-oteljob to un-draft it. If server-otel has no artifact uploads this is fine — but confirm the draft release won't stay stuck.actions/attest@v4(unpinned): The attest action is referenced by major version tag, not a pinned SHA. Verify this aligns with the repo's policy on action pinning (other actions like checkout are SHA-pinned).publish_release: falseto verify the release stays in draft.Notes
ld-relay(commit 1581de9). The key insight is that release-please depends on the tag existing when determining if a release PR is still needed — so tags must be created between the release step and the PR step.${{ github.repository }}expression appears inrun:blocks (tag creation and publish-release jobs). This value is GitHub-controlled (not user input) so script injection risk is negligible, but worth noting since tag names are deliberately routed through env vars.force-tag-creationhas no effect with the current release-please version — it is a forward-compatibility placeholder that will take effect once release-please supports it, at which point the inline tag creation steps can be removed.manual-sdk-release-artifacts.yml'spublish_releasedefaults totrueforworkflow_dispatch, matching the expectation that manual runs typically want to finalize the release.Link to Devin session: https://app.devin.ai/sessions/7d5bda4d9dbe4ae0b950b30a50485e60
Requested by: @keelerm84
Note
Medium Risk
Changes the release automation flow (draft/publish, tag creation, and provenance attestation), so misconfigurations could publish too early or leave releases/tags in an inconsistent state.
Overview
Moves GitHub releases to a draft-first model so artifacts can be uploaded before publishing, aligning with GitHub’s immutable release behavior.
Updates
release-pleaseto create draft releases without PRs first, then manually creates/pushes tags when a release is cut, and only runs the PR-creation step when no releases were produced. Replaces SLSA generator provenance workflows with per-buildactions/attest@v4steps that decode thesdk-releasehash outputs intochecksums.txtand attest in-place.Adds gated publish steps: the manual artifacts workflow gains a
publish_releaseinput and a final job to un-draft, and the main workflow addspublish-release-*jobs that publish each package release only after its artifact builds complete. Also sets"draft": trueand addsforce-tag-creationflags across packages inrelease-please-config.json.Written by Cursor Bugbot for commit 2e85850. This will update automatically on new commits. Configure here.