docs(release): make the two-PR flow, smoke, ledger and UI tag explicit (#2298)
The release skill described a v2 release as three steps and left four things a release actually depends on implicit or absent: that it is two pull requests against two different bases, the production smoke of the release candidate, the ledger artifact the maintainers review, and where a smoke finding gets fixed. It also gave only the `git tag` path, which is the fallback rather than the norm. Restructure it around PR 1 -> PR 2 -> Release: - a "The shape" table contrasting the two PRs by branch, base, contents, verification and merge condition, plus why they must not be folded together and why PR 1 merges before PR 2 is opened; - step 2 broken into the tree-hash check and production smoke, the ledger's structure, and the fix-on-`v2/main` rule that keeps the merge tree byte-identical to `origin/v2/main`; - step 3 leading with the GitHub UI path, keeping the CLI commands as the by-hand equivalent. Docs only. The `npm audit fix`, `--no-git-tag-version`, tag-`origin/main`, no-`v`-prefix and #2010 warnings are all preserved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsmR1iQstcrEzJFmgZXGLi Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
fb1dd1f280
commit
440a8e24b2
1 changed files with 110 additions and 15 deletions
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
name: release
|
||||
description: Cut an Inspector v2 release — run npm audit and bump the version on v2/main first, merge the milestone into main, tag origin/main with a bare x.y.z, and publish via the GitHub Release. Also covers the v1 line and what the publish jobs gate on.
|
||||
description: "Cut an Inspector v2 release — two PRs and then a GitHub Release. PR 1 puts the npm audit, any fixes it forces, and the version bump on v2/main; PR 2 merges v2/main into main and is smoke-tested from the production build with a ledger artifact for the maintainers; the maintainer then tags and publishes through the GitHub UI. Also covers the v1 line and what the publish jobs gate on."
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
|
|
@ -24,14 +24,35 @@ job or the coverage gate red:
|
|||
--access public --provenance`.
|
||||
- **`publish-github-container-registry`** — the GHCR image.
|
||||
|
||||
## The shape: two PRs, then the Release
|
||||
|
||||
There is **one version number** (only the root `package.json` has one — the
|
||||
clients carry none), so the flow is three steps.
|
||||
clients carry none), and the release moves through **two pull requests** in
|
||||
order. They are not interchangeable and neither one's content belongs on the
|
||||
other.
|
||||
|
||||
## 1. `npm audit`, then bump, on `v2/main` — before the milestone merge
|
||||
| | PR 1 — prep | PR 2 — the milestone merge |
|
||||
| --- | --- | --- |
|
||||
| Branch | `v2/chore/<ISSUE>-bump-<X-Y-Z>`, cut from `origin/v2/main` | the milestone-merge branch, cut from `origin/main` |
|
||||
| Base | **`v2/main`** | **`main`** |
|
||||
| Carries | the `npm audit` report, **any fixes the audit forces**, and the **version bump** — all three, one PR | the milestone's work, arriving whole from `v2/main`. **No commits of its own.** |
|
||||
| Verified by | `npm run local:gate` | `npm run local:gate` **plus** a hand-driven smoke of every contribution in the milestone, from the **production build**, written up as a **ledger artifact** |
|
||||
| Merged when | reviewed and green | the ledger is reviewed by the maintainers and clean |
|
||||
|
||||
Both are part of the milestone's work, so both belong on the develop branch and
|
||||
flow into `main` together, in the same PR — audit first, so the bump sits on top
|
||||
of a tree you have just checked.
|
||||
Then, and only then, a maintainer tags and publishes the **GitHub Release**
|
||||
(step 3), which is what triggers the publish jobs.
|
||||
|
||||
⚠️ **Do not fold the two together.** The bump must exist on `v2/main` before the
|
||||
merge (see [Why the bump goes on `v2/main` first](#why-the-bump-goes-on-v2main-first-2010)),
|
||||
and PR 2 must stay a pure merge — a commit authored on the merge branch is a
|
||||
change that exists downstream of `v2/main` and nothing carries it back.
|
||||
|
||||
## 1. PR 1 — audit, audit fixes and the bump, on `v2/main`
|
||||
|
||||
All three are part of the milestone's work, so all three belong on the develop
|
||||
branch and flow into `main` together, **in the same PR** — audit first, so the
|
||||
bump sits on top of a tree you have just checked, and so a reviewer sees the
|
||||
report and the fixes it forced as one change.
|
||||
|
||||
```sh
|
||||
# Branch from the REMOTE ref, and read the version only once you are on it.
|
||||
|
|
@ -92,7 +113,11 @@ and the tag would land on a `v2/main` commit — but the release must be cut fro
|
|||
`main`, so the tag has to point at the merge commit there (step 3). Tagging here
|
||||
creates a tag on a commit that is never released.
|
||||
|
||||
## 2. Merge `v2/main` → `main`
|
||||
**PR 1 merges before PR 2 is opened.** The merge branch is cut from `main` and
|
||||
takes `v2/main` whole, so opening it early means merging a `v2/main` that does
|
||||
not yet carry the bump.
|
||||
|
||||
## 2. PR 2 — merge `v2/main` → `main`, smoke-test it, and write the ledger
|
||||
|
||||
Through the usual milestone-merge branch. It now carries the bump, so the
|
||||
release lands on `main` with the version already correct.
|
||||
|
|
@ -104,10 +129,78 @@ milestone merge lands they agree again, and `v2/main` is never left **behind**
|
|||
`main`. If you see `v2/main` ahead of `main`, a release is in flight; if you see
|
||||
it behind, something went wrong.
|
||||
|
||||
## 3. Tag `origin/main` and draft the Release
|
||||
### 2a. Smoke-test the release candidate from the production build
|
||||
|
||||
Derive the tag from the version that just landed, rather than typing one — a
|
||||
hard-coded tag is either already taken (so `git tag` aborts) or, worse, wrong:
|
||||
The merge branch's tree **is** the release candidate. Check that rather than
|
||||
assume it — the merge commit's tree and `origin/v2/main`'s must be identical:
|
||||
|
||||
```sh
|
||||
git rev-parse origin/v2/main^{tree}
|
||||
git rev-parse <merge-commit>^{tree} # must print the same hash
|
||||
```
|
||||
|
||||
Then drive it. Work from a **dedicated worktree** with its own full
|
||||
`npm install` (a symlinked `node_modules` passes lint and tests and then fails
|
||||
every story file), run `npm run local:gate` there, and exercise the app from the
|
||||
**production build** — the packaged bin and the built bundles, not `vite dev`.
|
||||
The `local-dev`, `test-servers` and `pre-push-gate` skills cover the mechanics;
|
||||
`pack:verify` inside the gate is what proves the tarball a consumer installs
|
||||
actually resolves.
|
||||
|
||||
**Every contribution closed in the milestone gets driven, not read.** The bar is
|
||||
observed behavior from the running app — a rendered panel, a status attribute, a
|
||||
server's own stderr — against a real test server, through whichever clients the
|
||||
change touches (web, CLI, TUI). "Its tests pass" is not evidence for this step;
|
||||
the gate already said that. For a change with no observable surface, the
|
||||
evidence is the thing that holds it — a probe that makes the guard fire, a
|
||||
counted before/after, a resolved binary path.
|
||||
|
||||
### 2b. The ledger artifact
|
||||
|
||||
Write the results up as a **published artifact** for the maintainers to review,
|
||||
and link it from PR 2. Shape it like the
|
||||
[v2.5.0 ledger](https://claude.ai/code/artifact/6f25d292-3623-419f-af7f-26aba57247ef):
|
||||
|
||||
- **Masthead** — repo, PR number and merge commit, version, date; and a
|
||||
standfirst saying what tree was tested and that its hash matches
|
||||
`origin/v2/main`, plus whether the milestone payload is complete (the only
|
||||
issue left open should be the merge itself).
|
||||
- **Verdict band** — `local:gate` result, milestone issues verified as `N / N`,
|
||||
distinct test count, regressions found.
|
||||
- **The automated gate** — one cell per stage with its number (file counts,
|
||||
test counts, smoke count, `pack:verify` size), and a note on what is new this
|
||||
milestone.
|
||||
- **One section per theme**, each a table of *Issue · What was driven ·
|
||||
Observed · Status*. One row per closed issue, issue-linked, with the actual
|
||||
output in the Observed cell.
|
||||
- **Notes / findings** — anything that is a caveat rather than a pass, called
|
||||
out rather than folded into a row.
|
||||
|
||||
A row that says "verified" without saying what was run is not a ledger entry.
|
||||
|
||||
### 2c. When the smoke finds something
|
||||
|
||||
**The fix goes on `v2/main`, never on the merge branch.** File the issue, fix it
|
||||
through an ordinary PR against `v2/main`, then merge `v2/main` into the merge
|
||||
branch again so the fix arrives the same way everything else did. That keeps the
|
||||
merge tree byte-identical to `origin/v2/main` — which is both the invariant
|
||||
checked in 2a and the reason a finding here does not create a commit that only
|
||||
exists downstream (#2000 → #2092; #2215 → #2216–2224).
|
||||
|
||||
Re-run the affected part of the smoke afterwards and update the ledger; it is
|
||||
the artifact the maintainers approve the merge on.
|
||||
|
||||
## 3. Tag and publish the Release
|
||||
|
||||
**Normally this is done by a maintainer through the GitHub UI**, after PR 2 has
|
||||
merged: *Releases → Draft a new release → Choose a tag → type the bare `x.y.z`
|
||||
→ Create new tag on publish*, with **Target: `main`**, then generate the notes
|
||||
and publish. Publishing the Release is what fires the `publish` and
|
||||
`publish-github-container-registry` jobs.
|
||||
|
||||
The equivalent by hand, for when the UI is not an option — derive the tag from
|
||||
the version that just landed rather than typing one, since a hard-coded tag is
|
||||
either already taken (so `git tag` aborts) or, worse, wrong:
|
||||
|
||||
```sh
|
||||
git fetch origin main
|
||||
|
|
@ -122,13 +215,15 @@ resolves through whatever merge-or-rebase strategy you have configured, so a
|
|||
divergent local `main` can quietly produce or replay local commits. Tagging
|
||||
`HEAD` there tags a commit that is not on `origin/main`, and `git push origin
|
||||
<tag>` pushes only the tag — leaving a release whose commit was never published.
|
||||
The UI path avoids this by construction: the target is `main` itself.
|
||||
|
||||
⚠️ **No `v` prefix.** This repo's release tags are bare `x.y.z` — which is why
|
||||
the command above tags `$VERSION` and not `v$VERSION`. npm's own `tag-version-prefix` defaults to `v` and the repo
|
||||
sets no `.npmrc`, so a bare `npm version` would have produced a mismatched tag;
|
||||
tagging by hand is what keeps it right. (The workflow's assert step strips a
|
||||
leading `v` before comparing, so a `v`-prefixed tag would still publish — it
|
||||
would just be inconsistent with every previous release.)
|
||||
the command above tags `$VERSION` and not `v$VERSION`, and why the tag typed
|
||||
into the UI carries no prefix either. npm's own `tag-version-prefix` defaults to
|
||||
`v` and the repo sets no `.npmrc`, so a bare `npm version` would have produced a
|
||||
mismatched tag; tagging by hand is what keeps it right. (The workflow's assert
|
||||
step strips a leading `v` before comparing, so a `v`-prefixed tag would still
|
||||
publish — it would just be inconsistent with every previous release.)
|
||||
|
||||
The release's target commit selects which workflow runs, so this only publishes
|
||||
when a release is cut from a commit carrying the v2 workflow.
|
||||
|
|
|
|||
Loading…
Reference in a new issue