mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +02:00
fix(release): reject promotion sources that predate their channel tooling (#11197)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The release subsystem promotes builds along canary → nightly → beta → stable, and each publish job checks out the promotion's source commit and runs that tree's release tooling > - The first beta dispatch failed with `unexpected argument: beta`: the selected nightly's source predated the beta channel, so its `release.sh` did not know the argument > - The failure was clean (argument parsing, nothing published) but cryptic, and the same trap waits for any promotion of a source older than its target channel's tooling > - This pull request makes the selection jobs reject such sources with an actionable error and documents the property > - The benefit is that a bootstrapping or old-source promotion fails in seconds with instructions, instead of mid-publish with a parser error ## Linked Issues or Issue Description Refs #11008 — the guard hardens the beta promotion flow introduced there, after its first dispatch surfaced the gap described below. **Subsystem affected** Release automation: `.github/workflows/release.yml`, `doc/RELEASING.md`, workflow wiring tests. **Problem or motivation** Run 31444045044 (first beta dispatch) failed in `publish_beta` with `unexpected argument: beta`. Promotions deliberately build from the pinned source commit, which means they also run that commit's `scripts/release.sh` — and a source that predates the target channel's introduction cannot publish it. Nothing guards this today; the error surfaces deep in the publish job with no explanation. **Proposed solution** Guard at selection time: `select_nightly` requires the source canary's `release.sh` to know the nightly channel, and `select_beta` requires the source nightly's `release.sh` to know the beta channel. Each guard literally matches the channel case arm and fails closed with a clear message naming the remedy (promote a newer source). Document the tooling-era property in `RELEASING.md` and pin the guards with a wiring test. ## What Changed - `.github/workflows/release.yml`: tooling-era guards in `select_nightly` and `select_beta` - `doc/RELEASING.md`: documents that promotions run the source commit's release tooling - `scripts/__tests__/release-verify-workflow.test.mjs`: wiring test pinning both guards ## Verification - Wiring tests: 5 pass - Guard expressions exercised against real commits: accepts the beta-capable merge commit of the beta-channel change, rejects a pre-beta commit - YAML parse of the workflow - After merge: the next beta dispatch selects a beta-capable nightly and passes the guard ## Risks - Low. Selection-time check only; the guards match the channel case arm literally and fail closed (with the same actionable message) if that line is ever reformatted ## Model Used Claude Fable 5 (`claude-fable-5`, Anthropic) in Claude Code, with extended thinking and full tool use. All changes model-authored under human direction. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green (pending — will confirm before merge) - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups (pending — will confirm before merge) - [x] I will address all Greptile and reviewer comments before requesting merge
This commit is contained in:
1 parent
35aaaa0bd0
commit
6601014898
3 files changed
+31
No files matched your search
@@ -192,6 +192,15 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Promotions run the release tooling of the source commit, so the
|
||||||
|
# source must already understand the nightly channel. (Literal match
|
||||||
|
# of release.sh's channel case arm; if that line is reformatted this
|
||||||
|
# fails closed and should be updated alongside it.)
|
||||||
|
if ! git show "${sha}:scripts/release.sh" | grep -qF 'canary|nightly'; then
|
||||||
|
echo "Error: source commit $sha predates nightly release tooling; promote a newer canary." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
||||||
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
||||||
echo "canary_version=$canary_version" >> "$GITHUB_OUTPUT"
|
echo "canary_version=$canary_version" >> "$GITHUB_OUTPUT"
|
||||||
@@ -380,6 +389,15 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Promotions run the release tooling of the source commit, so the
|
||||||
|
# source must already understand the beta channel. (Literal match of
|
||||||
|
# release.sh's channel case arm; if that line is reformatted this
|
||||||
|
# fails closed and should be updated alongside it.)
|
||||||
|
if ! git show "${sha}:scripts/release.sh" | grep -qF 'canary|nightly|beta|stable)'; then
|
||||||
|
echo "Error: source commit $sha predates beta release tooling; promote a newer nightly whose source contains the beta channel." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
||||||
echo "nightly_version=$nightly_version" >> "$GITHUB_OUTPUT"
|
echo "nightly_version=$nightly_version" >> "$GITHUB_OUTPUT"
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -148,6 +148,10 @@ Betas are manual promotions. Dispatch
|
|||||||
it does not exist or already shipped as a beta
|
it does not exist or already shipped as a beta
|
||||||
- the publish waits for approval in the **`npm-beta` environment** — its
|
- the publish waits for approval in the **`npm-beta` environment** — its
|
||||||
required reviewers are the promotion gate
|
required reviewers are the promotion gate
|
||||||
|
- promotions run the release tooling of the source commit, so the source
|
||||||
|
nightly must postdate the beta channel's introduction; the selection job
|
||||||
|
rejects older sources with a clear error (in practice every nightly cut
|
||||||
|
after the beta tooling merged qualifies)
|
||||||
- the same commit is republished as `YYYY.MDD.P-beta.N` under the npm
|
- the same commit is republished as `YYYY.MDD.P-beta.N` under the npm
|
||||||
dist-tag `beta`, tagged `beta/vYYYY.MDD.P-beta.N`, and `docker.yml` is
|
dist-tag `beta`, tagged `beta/vYYYY.MDD.P-beta.N`, and `docker.yml` is
|
||||||
dispatched at that tag to publish the `:beta` images
|
dispatched at that tag to publish the `:beta` images
|
||||||
|
|||||||
@@ -38,6 +38,15 @@ test("onboard smoke container binds beyond loopback so the mapped port is reacha
|
|||||||
assert.match(dockerfile, /onboard --yes --bind lan/);
|
assert.match(dockerfile, /onboard --yes --bind lan/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("promotion selection guards against sources that predate their channel tooling", () => {
|
||||||
|
const releaseWorkflow = readWorkflow("release.yml");
|
||||||
|
|
||||||
|
// Promotions run the source commit's release.sh, so selection must reject
|
||||||
|
// sources whose tooling does not know the target channel yet.
|
||||||
|
assert.match(releaseWorkflow, /git show "\$\{sha\}:scripts\/release\.sh" \| grep -qF 'canary\|nightly'/);
|
||||||
|
assert.match(releaseWorkflow, /git show "\$\{sha\}:scripts\/release\.sh" \| grep -qF 'canary\|nightly\|beta\|stable\)'/);
|
||||||
|
});
|
||||||
|
|
||||||
test("release smoke workflow extends the container readiness budget for CI", () => {
|
test("release smoke workflow extends the container readiness budget for CI", () => {
|
||||||
const smokeWorkflow = readWorkflow("release-smoke.yml");
|
const smokeWorkflow = readWorkflow("release-smoke.yml");
|
||||||
const harness = readFileSync(path.join(repoRoot, "scripts/docker-onboard-smoke.sh"), "utf8");
|
const harness = readFileSync(path.join(repoRoot, "scripts/docker-onboard-smoke.sh"), "utf8");
|
||||||
|
|||||||
Reference in new issue
Block a user