Repository navigation
ci(release)!: release through Changesets with an alpha prerelease channel - #2430
Conversation
…nnel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The changesets action leaves the Version PR's bump in the working tree; plan.py now reads package.json from git and checks the build commit carries the version. Head-ref exemptions apply only to same-repo branches, and the release job can read PR comments again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Docs review
Verdict: looks-good - the docs changes are accurate, well-voiced, and consistent with the workflow changes they describe.
Checked and clean:
- Accuracy. The Changesets flow, the two-channel (
main/alpha) model, therelease-guardsgate, the shipped-files allowlist, and the re-run / promotion / restart lifecycle inRELEASING.mdall matchrelease.yml,ci.yml, and thescripts/release/guards. Referenced source files (src/system/update_service.py,scripts/version-sync.js, and the threescripts/releasePython guards) exist; the package namefiestaboardmatches the changeset frontmatter. - Links.
.changeset/README.md,CONTRIBUTING.md, andRELEASE_CHECKLIST.mdall point toRELEASING.mdvia relative paths that resolve. The deletedRELEASE_CHANNELS.mdhad exactly one referrer (CONTRIBUTING.md), and this PR rewrites that reference, so no dangling link remains. - Voice / branding / privacy. No marketing filler, no FiestaBoard vs Vestaboard confusion, no real PII. (The
Fiestaboard/FiestaBoardoccurrences are the literal GitHub org and repo slug, correct as-is.)
Minor, non-blocking, no action needed: the plan doc (docs/internal/plans/2026-11-07-changesets-release.md) lists RASPBERRY_PI.md among docs to update for the Pi-trigger change, but this PR does not touch it. That is fine - its wording (published by build-fiestapi.yml after each app release, roughly 45-60 min) describes the outcome, not the workflow_run-to-dispatch mechanism, so it stays accurate.
No inline suggestions - nothing warranted a one-line fix.
Auto-review by claude-docs-review.yml.
ubuntu-latest moved to ubuntu-26.04 (Docker 28.0.4 -> 29.4.2) between Nov 4 and Nov 6. Docker 29 defaults to the containerd image store, where a loaded image's ID is its manifest digest, not the config digest buildx reports as imageid, so every E2E job failed "Loaded image does not match the producer's image ID". The producer now also exports its digest and consumers accept either of this run's two identities, and nothing else. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Docs review
Verdict: looks-good with one nit.
Scope: the docs changes only (.changeset/README.md, PULL_REQUEST_TEMPLATE.md, CLAUDE.md, CONTRIBUTING.md, docs/internal/development/RELEASING.md, the removed RELEASE_CHANNELS.md, docs/internal/plans/2026-11-07-changesets-release.md, RELEASE_CHECKLIST.md). The workflow/script/test changes were skimmed for context only.
Verified against the PR head:
- Referenced scripts exist: scripts/release/changeset_check.py, plan.py, pre_mode.py, scripts/version-sync.js.
- package.json has the changeset and version-packages scripts and @changesets/cli v3, matching RELEASING.md and the plan doc.
- The CI job is named Release guards (ci.yml), matching every doc reference.
- All relative links resolve: the .changeset README, CONTRIBUTING.md, and RELEASE_CHECKLIST.md all point to the new RELEASING.md.
- No real PII, no FiestaBoard/Vestaboard confusion, no claims about Vestaboard hardware internals.
One inline suggestion below - click Apply suggestion to commit it.
Auto-review by claude-docs-review.yml.
|
|
||
| ## After merge (each repo-settings change confirmed with the user first) | ||
|
|
||
| 1. Leave the frozen `next` branch alone for now (decided 2026-11-07: the prerelease branch is `alpha`). Archiving or deleting `next`, and removing the disabled "Release: Publish Beta Image" workflow entry, is a separate, confirmed step. |
There was a problem hiding this comment.
Grammar: use "an" before the vowel sound in "alpha".
| 1. Leave the frozen `next` branch alone for now (decided 2026-11-07: the prerelease branch is `alpha`). Archiving or deleting `next`, and removing the disabled "Release: Publish Beta Image" workflow entry, is a separate, confirmed step. | |
| 2. Create an `alpha-protection` ruleset, modeled on FiestaUI's `alpha-protection` |
|
SKIP: CI/release infrastructure change — migrates to Changesets; no user-facing behavior change |
What this changes
Releases now go through Changesets, ported from Fiestaboard/FiestaUI#375. Every merge to
mainthat touched code used to cut a release. Now:npx changeset, pick patch/minor/major, and write one user-facing line. CI's new Release guards job (part ofCI Success) fails a PR that changes shipped files without one.main/alpha, the Release workflow runschangesets/action@v2and maintains a single "chore: release" Version PR. That PR bumpspackage.json, both lockfiles,web/package.jsonandsrc/__init__.py, and writesCHANGELOG.md.There are two channels.
mainreleasesX.Y.Zto:latest.alphais a long-lived branch in pre mode with tagbeta. It releasesX.Y.Z-beta.Nto:betaas a GitHub prerelease, which only beta-channel installs see. Both names were confirmed with the maintainer.Design and lifecycle:
docs/internal/development/RELEASING.md. Plan:docs/internal/plans/2026-11-07-changesets-release.md.Key decisions
scripts/release/plan.pyreleases whenpackage.json's version has no GitHub Release yet. Re-runs after a failed build resume, and the action's "empty changesets" no-op can't strand a release.plan.pyrefuses to cut a prerelease frommainor a stable release fromalpha.pushon[main, alpha], with no path filters. Concurrency is per branch and never cancels. Version PRs are opened with the Claude bot app token, so their CI isn't held inaction_required.scripts/release/pre_mode.py):maincan't bring pre mode; exit mode is allowed, because that's the promotion.alphamust keep thebetapre tag.alphain exit mode and fails withoutpre.json.scripts/release/changeset_check.py):COPYsources pluspi-image/. A test parses the Dockerfile so a newCOPYcan't slip past.fiestaupdater/, tests, Markdown, docs and CI don't need a changeset. This keeps the feat(fiestaupdater): install a named tag, so a box can change channel #1969 lesson.no-changesetlabel (read live, so a re-run picks it up), or by head refschangeset-release/*,main(syncs),alpha(promotion) anddependabot/*. Dependency bumps ride along with the next release.build-fiestapi.ymlno longer listens for the release workflow's completion, because most runs only update the Version PR. It takes arelease_taginput and builds from that tag.Removed
release-beta.ymland its tests.release.yml:chore: bump versionpush tomainwithRELEASE_PAT;revert-on-failure, which force-pushedmain.pr-label.yml. Area labels stay..github/release.yml.version-sync.js. It now only syncs, and it accepts prerelease versions, which are now committed.RELEASE_CHANNELS.md, replaced byRELEASING.md.nextin CodeQL, replaced byalpha.Behaviour changes worth knowing:
plugin-previews.jsonpush from Scheduled: Maintenance no longer cuts a patch release. It rides along with the next one.Also fixes: E2E image check on Docker 29
E2E has failed on every PR that builds the test image since Nov 6,
fiestaui-upgradeincluded, with "Loaded image does not match the producer's image ID".ubuntu-latestmoved to ubuntu-26.04, which brings Docker 29. Docker 29 defaults to the containerd image store, wheredocker image inspect .Idafterdocker loadis the image's manifest digest. The old check compared it against buildx'simageid, which is the config digest.build-test-imagenow also exportsdigest, and both E2E jobs accept either of this run's two identities and nothing else.docker:29-dind(containerd store): real digests are accepted, wrong or missing ones rejected.tests/ci/test_image_handoff.pycovers both stores.Downstream
FiestaUI's upgrade PRs (
fiestaui-upgrade) changeweb/package*.json, which is shipped. They are deliberately not exempt. The companion PR makes them carry.changeset/fiestaui-upgrade.md: Fiestaboard/FiestaUI#376. It's a no-op for repos without.changeset/, so the two PRs can merge in either order. Until it lands, an open upgrade PR needs a changeset (or the label) added by hand.claude-issue-triage.ymlandCLAUDE.mdtell Claude to write changesets.Verification
pytest: the newtests/test_release_guards.pyandtests/test_release_workflow.py, plus the 24 existing test files that read workflows, versions or package files (846 tests in all). All pass.ruff check/ruff format,npm run docs:lintandactionlintreport nothing new. The only actionlint findings are in older steps.npm run version-packagesin a scratch clone:10.3.2→10.4.0, with all five version files andCHANGELOG.md;pre enter beta→10.4.0-beta.0→10.4.0-beta.1, thenpre exit→10.4.0with a merged changelog andpre.jsondeleted.plan.pyandpre_mode.pygave the expected decision at each step, against the live GitHub Releases.act pull_request -j release-guardsran four scenarios: no changeset fails, with a changeset passes, Version PR head ref passes, and a PR intoalphawithoutpre.jsonfails.actneeded the GitHub remote set forGITHUB_REPOSITORY. Labels are now fetched with the Python standard library because the act image has nogh.act -nonrelease.ymlshows the expected job plan.versionjob end to end, which needs the app token and live repository writes, and the Docker build and release jobs. These are first exercised by the first Version PR after merge.plan.pyreadspackage.jsonfrom git rather than from disk, and it checks that the build commit carries the version.mainis still checked.README.mdchanges are deliberately not "shipped", matching the oldpaths-ignore: '**.md'.After merge (each setting confirmed first)
alpha-protectionruleset: PR plusCI Success, no merge queue, merge commits allowed, deletion allowed, and bypass for RepositoryRole 5 and Integration 4139279.alphafrommain, runnpx changeset pre enter beta, and push.nextbranch is untouched. Archiving it is a separate decision.🤖 Generated with Claude Code