Commit 29d2f9c
W2-1 · Release pipeline: JuliaRegistrator + TagBot + SBOM (#43)
## What this does
Implements the wave-2 release pipeline (work order
`.claude/tasks/prod-readiness/w2-1-release-pipeline.md`): release via
**JuliaRegistrator + TagBot** to the Julia General registry, with a
source tarball, an SPDX SBOM, and **real** SLSA provenance attached to
each GitHub Release. The pipeline stays inert until a maintainer
registers a version — no tag is created by this PR.
### Files
- **`.github/workflows/TagBot.yml`** (new) —
`JuliaRegistries/TagBot@e4c81aa…` (SHA-pinned `# v1.25.9`), triggered by
`issue_comment` from `JuliaTagBot` or `workflow_dispatch`; default
`GITHUB_TOKEN` flow (no SSH deploy key, since there is no Documenter
site). Job-level `if` uses only `github.*` context (no
`hashFiles()`/`secrets`), per the repo rule.
- **`.github/workflows/release.yml`** (rewritten) — on `v*` tag push /
`workflow_dispatch(tag)`: build a `git archive` source tarball, generate
an SPDX SBOM with `anchore/sbom-action` (syft, SHA-pinned), compute
**real** `base64-subjects` (`sha256sum … | base64 -w0`) and feed them to
`slsa-framework/slsa-github-generator` (no more `base64-subjects: ""`,
which used to error on every tag), and attach tarball + SBOM +
provenance to the release. All template `TODO` residue removed.
- **`docs/RELEASING.adoc`** (new) — end-to-end maintainer runbook:
version bump → merge → `@JuliaRegistrator register` → registry AutoMerge
→ TagBot → attach-artifacts, with AutoMerge/first-registration caveats
and a first-release checklist.
## Changes in this revision (addressing review must-fix)
1. **BLOCKER — changelog job could never run (so no release was ever
produced).** The `Install git-cliff` step fetched
`releases/latest/download/git-cliff-$(uname
-m)-unknown-linux-gnu.tar.gz`, but git-cliff assets are
**version-stamped** — the current latest asset is
`git-cliff-2.13.1-x86_64-unknown-linux-gnu.tar.gz`, and the versionless
path returns **HTTP 404** (verified below). Under the default `bash -eo
pipefail` shell that failed the step → failed the `changelog` job → and
because `release: needs: [build, changelog]` the release job was
**skipped**, so nothing ever attached. Fixed by pinning
`GIT_CLIFF_VERSION=2.13.1`, building the real versioned asset URL, and
adding `git-cliff --version` to fail fast if the binary is bad
(`release.yml` lines ~121-137).
2. **MISLEADING PIN — `softprops/action-gh-release`.** The SHA
`718ea10b132b3b2eba29c1007bb80653f286566b` was commented `# v2`, but
that SHA is actually tag **v3.0.1 / v3** (the real `v2` points at
`3bb12739…`). Corrected the comment to `# v3.0.1` (`release.yml` line
~177). Confirmed v3.0.1 behaviour is intended, not an accident: at that
SHA the runner is `node24` and every input the workflow uses
(`tag_name`, `files`, `body`, `draft`, `prerelease`,
`generate_release_notes`) exists.
3. **PR now opened** with the SLSA dry-run output and the first-release
checklist below (the two acceptance criteria that require PR-body
content).
## Verification
**`actionlint` 1.7.7 — clean** on both workflows (WSL Debian):
```
$ actionlint .github/workflows/release.yml .github/workflows/TagBot.yml
EXIT=0
```
**git-cliff URL facts (verified via GitHub API + curl):**
```
$ gh api repos/orhun/git-cliff/releases/latest --jq .tag_name
v2.13.1 # asset: git-cliff-2.13.1-x86_64-unknown-linux-gnu.tar.gz
$ curl -sSL -o /dev/null -w "%{http_code}\n" \
https://github.com/orhun/git-cliff/releases/latest/download/git-cliff-x86_64-unknown-linux-gnu.tar.gz
404
$ curl -sSfL https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-unknown-linux-gnu.tar.gz | tar -tz | grep '/git-cliff$'
git-cliff-2.13.1/git-cliff # versioned URL downloads; --strip-components=1 lands the binary
```
**softprops SHA fact (verified via GitHub API):** tags at `718ea10b…` =
`v3.0.1`, `v3`; real `v2` = `3bb12739…`; `action.yml@718ea10b…` has
`using: "node24"`.
**SLSA `base64-subjects` dry-run** — ran the exact `release.yml` snippet
on two scratch files (`statistikles-0.1.0.tar.gz`,
`statistikles-0.1.0.spdx.json`) and round-tripped it back:
```
$ sha256sum statistikles-0.1.0.tar.gz statistikles-0.1.0.spdx.json > checksums.txt
$ cat checksums.txt
8f51e5a4968e4a01e5dd77b720ecf8e3a4d2bd5eb01e9a8357b0b6f787a674f3 *statistikles-0.1.0.tar.gz
033f748d28edb19db9e73d8b31ea18fe6f08d668ee43c202e1c20fe5ffc2e28f *statistikles-0.1.0.spdx.json
$ HASHES=$(base64 -w0 checksums.txt) # this is the base64-subjects value handed to the generator
$ echo "$HASHES"
OGY1MWU1YTQ5NjhlNGEwMWU1ZGQ3N2I3MjBlY2Y4ZTNhNGQyYmQ1ZWIwMWU5YTgzNTdiMGI2Zjc4N2E2NzRmMyAqc3RhdGlzdGlrbGVzLTAuMS4wLnRhci5negowMzNmNzQ4ZDI4ZWRiMTlkYjllNzNkOGIzMWVhMThmZTZmMDhkNjY4ZWU0M2MyMDJlMWMyMGZlNWZmYzJlMjhmICpzdGF0aXN0aWtsZXMtMC4xLjAuc3BkeC5qc29uCg==
$ base64 -d <<<"$HASHES" | sha256sum -c -
statistikles-0.1.0.tar.gz: OK
statistikles-0.1.0.spdx.json: OK
```
The decoded value is exactly the `sha256sum` manifest the generator
expects, and it verifies OK against the files.
## First release checklist
(Also lives in `docs/RELEASING.adoc` for the maintainer.)
- [ ] `version` bumped in `Project.toml` (semver; `0.x` breaking-change
rule — pre-1.0, breaking → minor, non-breaking → patch).
- [ ] `[compat]` complete for every non-stdlib dependency **and**
`julia`, each with an upper bound (`Statistics = "1"` was added in wave
1).
- [ ] PR merged to `main` with **"E2E — Julia Test Suite"** green (Base
ruleset blocks a red merge).
- [ ] Registrator GitHub App installed on the repo (one-time).
- [ ] `@JuliaRegistrator register` commented on the **merge commit on
`main`**.
- [ ] Registry PR AutoMerged — **watch for**: the first-ever
registration sits in a **3-day new-package waiting period**; a missing
`[compat]` upper bound is the usual AutoMerge decline (its PR comment
names the exact guideline; fix in-repo and re-comment).
- [ ] TagBot created the `vX.Y.Z` tag + GitHub Release.
- [ ] **Release** workflow run (*Actions → Release → tag = `vX.Y.Z`*) so
the tarball, SBOM, and SLSA provenance attach. This manual step is
required by default because a `GITHUB_TOKEN`-created tag does not fire
the downstream `push` event; the optional deploy-key upgrade in
`docs/RELEASING.adoc` automates it.
## Skipped / not verified by execution
- Not run: `slsa-github-generator`, `anchore/sbom-action`, TagBot, and
JuliaRegistrator themselves — they only run in GitHub Actions on a real
`v*` tag, and the task forbids creating tags. They are verified by
careful reading + SHA-pinning; the SLSA subject computation (the one
novel shell bit) is dry-run-verified above.
- Out of scope per the work order: actually registering the package,
creating any tag, and container image releases.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 37bd237 commit 29d2f9c
3 files changed
Lines changed: 423 additions & 41 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | | - | |
6 | | - | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
7 | 22 | | |
8 | 23 | | |
9 | 24 | | |
10 | 25 | | |
11 | 26 | | |
12 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
13 | 34 | | |
14 | 35 | | |
15 | 36 | | |
16 | 37 | | |
17 | 38 | | |
18 | 39 | | |
19 | | - | |
| 40 | + | |
20 | 41 | | |
21 | 42 | | |
22 | 43 | | |
23 | 44 | | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
24 | 49 | | |
25 | 50 | | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
26 | 79 | | |
27 | | - | |
| 80 | + | |
| 81 | + | |
28 | 82 | | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
42 | 99 | | |
43 | 100 | | |
44 | 101 | | |
| |||
52 | 109 | | |
53 | 110 | | |
54 | 111 | | |
| 112 | + | |
55 | 113 | | |
56 | 114 | | |
57 | 115 | | |
58 | 116 | | |
59 | | - | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
60 | 120 | | |
61 | 121 | | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
62 | 131 | | |
63 | | - | |
64 | | - | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
65 | 138 | | |
66 | 139 | | |
67 | 140 | | |
| |||
87 | 160 | | |
88 | 161 | | |
89 | 162 | | |
90 | | - | |
| 163 | + | |
91 | 164 | | |
92 | 165 | | |
93 | 166 | | |
94 | 167 | | |
95 | 168 | | |
96 | 169 | | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
104 | 175 | | |
105 | | - | |
106 | | - | |
| 176 | + | |
| 177 | + | |
107 | 178 | | |
| 179 | + | |
108 | 180 | | |
109 | 181 | | |
110 | | - | |
| 182 | + | |
111 | 183 | | |
112 | | - | |
113 | | - | |
114 | | - | |
| 184 | + | |
| 185 | + | |
115 | 186 | | |
116 | 187 | | |
117 | 188 | | |
118 | 189 | | |
119 | 190 | | |
120 | | - | |
| 191 | + | |
121 | 192 | | |
122 | 193 | | |
123 | 194 | | |
124 | 195 | | |
125 | | - | |
| 196 | + | |
126 | 197 | | |
| 198 | + | |
127 | 199 | | |
128 | 200 | | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
0 commit comments