Commit 9373dad
ci: use draft releases to support immutable GitHub releases (#516)
## 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:**
1. **`release-please-config.json`** — Added top-level `"draft": true` so
release-please creates draft releases for all packages. Added
`"force-tag-creation": true` to every package (not yet supported by
release-please, but included for forward compatibility).
2. **`release-please.yml` — Split release-please pattern** —
release-please is now invoked twice within the same job:
- **First call** with `skip-github-pull-request: true` — only creates
releases (no PRs).
- **Inline tag creation** — if any package was released, checks out the
repo and creates git tags manually (release-please skips tag creation
for drafts). Iterates over all 4 packages (client, server, server-redis,
server-otel) using env vars to avoid script injection. Idempotent —
skips if the tag already exists.
- **Second call** with `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.
- All downstream artifact jobs (`release-client`, `release-server`,
etc.) now depend only on `release-please` (the former separate
`create-tags` job has been removed).
- Action pinned to
`googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38`
(v4.4.0).
3. **SLSA → `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 inline `actions/attest@v4` steps in
each build job that decode the base64 hashes into a checksums file and
attest in-place.
4. **`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.
5. **`manual-sdk-release-artifacts.yml` — `publish_release` input** —
Added a `publish_release` boolean input (default: `true`) and a
`publish-release` job gated on it, so operators can optionally keep the
release in draft after manual artifact uploads.
## Review & Testing Checklist for Human
- [ ] **Split release-please ordering**: The two release-please
invocations must run in this exact order — releases first, then PRs. If
the second call (PR creation) ran when a release *was* created, it would
see no tag and open a duplicate release PR. Verify the `if` condition on
the second call correctly uses `!= 'true'` with `&&` across all 4
packages.
- [ ] **`needs` arrays in `publish-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-client` needs both `release-client` (3-OS
matrix) and `release-client-mac-arm64`.
- [ ] **server-otel has no publish-release job**: `release-please`
creates a tag for server-otel if released, and `"draft": true` means
release-please creates a draft release, but there is no
`publish-release-server-otel` job 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).
- [ ] **End-to-end test**: Trigger a test release (or review a prior
release's output) to confirm: (a) draft release is created, (b) tags are
pushed, (c) artifacts upload successfully, (d) attestation succeeds, (e)
release is un-drafted. Also test the manual workflow with
`publish_release: false` to verify the release stays in draft.
### Notes
- This follows the split release-please pattern established in
[`ld-relay` (commit
1581de9)](launchdarkly/ld-relay@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.
- The `${{ github.repository }}` expression appears in `run:` 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-creation` has 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`'s `publish_release` defaults to
`true` for `workflow_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
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes release publishing order and provenance model; mis-timed
publish jobs could finalize releases before all artifacts upload, and
otel publish runs without waiting on artifact jobs.
>
> **Overview**
> Supports **immutable GitHub releases** by having release-please create
**draft** releases (`draft` + `force-tag-creation` in
`release-please-config.json`), uploading artifacts while still draft,
then **`gh release edit --draft=false`** only after all build jobs
finish (`publish-release-*` in `release-please.yml`, optional
`publish_release` on the manual workflow).
>
> **Provenance** moves from separate **SLSA** jobs to per-platform
**`actions/attest`** steps that decode base64 artifact hashes into
`checksums.txt` (with macOS vs GNU `base64` handling). Build jobs gain
`attestations` / `id-token` permissions; matrix jobs no longer export
hash outputs for downstream SLSA.
>
> **release-please** is bumped to **v5.0.0** (single run; tags for
drafts via `force-tag-creation`). **server-sdk-dynamodb** is wired
through automated release (outputs, matrix + arm64 jobs, publish job)
and the manual artifact workflow target list.
>
> **sdk-release** sets `CURL_ROOT` / `CMAKE_PREFIX_PATH` on Linux/macOS
non-CURL builds so **aws-sdk-cpp** (dynamodb) can satisfy
`find_package(CURL)` during CMake configure.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
722ee53. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Co-authored-by: Bee Klimt <bklimt@launchdarkly.com>1 parent 18028aa commit 9373dad
4 files changed
Lines changed: 355 additions & 131 deletions
File tree
- .github
- actions/sdk-release
- workflows
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
62 | 65 | | |
63 | 66 | | |
64 | 67 | | |
| |||
253 | 256 | | |
254 | 257 | | |
255 | 258 | | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
256 | 262 | | |
257 | 263 | | |
258 | 264 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
20 | 25 | | |
21 | 26 | | |
22 | 27 | | |
| |||
41 | 46 | | |
42 | 47 | | |
43 | 48 | | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
48 | 53 | | |
49 | 54 | | |
50 | 55 | | |
| |||
59 | 64 | | |
60 | 65 | | |
61 | 66 | | |
62 | | - | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
63 | 88 | | |
64 | 89 | | |
65 | 90 | | |
66 | | - | |
67 | | - | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
68 | 95 | | |
69 | 96 | | |
70 | 97 | | |
| |||
79 | 106 | | |
80 | 107 | | |
81 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
82 | 121 | | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
102 | 126 | | |
103 | | - | |
104 | | - | |
105 | 127 | | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
0 commit comments