flisk-release
Guides an AI assistant through cutting a Flisk CLI release end to end - picking the commit, running the release candidate, dispatching the release workflow, finishing the GitHub releases, npm (@flisk-
SKILL.md.claude/skills/flisk-release/SKILL.md
GitHubReleasing Flisk
The human version of this guide is docs/releasing.md. It holds the background, every troubleshooting row and the settings and secrets. This skill is the procedure. Read the doc's Troubleshooting table before you diagnose any red run.
Workflows (IDs are stable, names have emoji, so use the file name):
| File | What it does |
|---|---|
release-candidate.yml |
Packs the CLI, runs quickstart (Q1-Q4) and every release-blocking resource-type suite on that tarball, the longest suites first. Runs only on release/* branches and rc-* tags. ~6-10 min. With -f release=true it also dispatches release-binaries.yml on its own branch right after the plan; the release builds alongside the suites and its publish job waits for this candidate, publishing only if every job passed. |
release-binaries.yml |
build → windows-binary → publish (gate, binaries to flisk-sh/releases, npm, signing, bump or tag) → publish-github-packages. ~6 min. |
sign-release.yml |
Signs an already published version after the fact. |
publish-existing-npm-to-github-packages.yml |
Backfills GitHub Packages for a version already on npm. |
1. Check the state before touching anything
gh run list -R flisk-sh/flisk -w release-binaries.yml -L 5
gh run list -R flisk-sh/flisk -w release-candidate.yml -L 5
gh release list -R flisk-sh/releases -L 3 # binaries: the real "latest"
gh release list -R flisk-sh/flisk -L 3 # checksums; a Draft here means a half-finished release
curl -s https://registry.npmjs.org/@flisk-sh%2fflisk | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s)["dist-tags"]))'
If recent release runs are red, read why (gh run view <id> --log-failed | grep -E "##\[error\]|FAIL") before dispatching again. A red publish gate (No passing Release candidate covers <sha>) is fixed by a candidate, never by re-dispatching. If a version is already on npm but its flisk-sh/flisk release is a draft or has no tag, go to step 6. Do not cut a new version.
2. Pick the commit
Use the newest main commit whose push CI and E2E | Journeys are green:
gh run list -R flisk-sh/flisk -w e2e-journeys.yml -b main -L 3 --json headSha,conclusion
Do not reuse an older release/* branch. Fixes land on main daily, and a candidate on a stale branch fails on bugs main already fixed (1.4.5 lost four dispatches this way).
3. Run the release candidate
SHA=<commit>; V=<next version, e.g. 1.4.6>; N=1 # N: attempt number, new for every retry on a new commit
gh api repos/flisk-sh/flisk/git/refs -f ref=refs/heads/release/$V-rc$N -f sha=$SHA
gh workflow run release-candidate.yml -R flisk-sh/flisk --ref release/$V-rc$N -f release=true # also starts the release now; its publish job waits for this candidate
gh run watch <run-id> -R flisk-sh/flisk --exit-status
Never dispatch the candidate on main: its package job refuses refs/heads/main. If the run fails:
- Read the failing case first. Look up its message in the troubleshooting table.
- Retry only when every failure is the same infrastructure readiness timeout. Say so explicitly.
- A real failure is a bug. Fix it on
mainthrough a PR, then cut a new branch frommainwith the nextN(the old ref already exists, and moving it would rewrite what an earlier run tested). Never weaken or skip the gate.
4. Dispatch the release
RC=release/$V-rc$N
GH_TOKEN="$(gh auth token)" node scripts/check-release-candidate.mjs --sha "$(gh api repos/flisk-sh/flisk/git/refs/heads/$RC --jq .object.sha)" --repo flisk-sh/flisk
gh workflow run release-binaries.yml -R flisk-sh/flisk --ref $RC
gh run watch <run-id> -R flisk-sh/flisk --exit-status
Dispatch on the candidate's branch, not on main: it releases exactly the commit the candidate tested, and other merges into main during the candidate no longer void it (1.4.24 lost rc5, rc6 and rc7 that way). The version bump PR still targets main. The workflow picks the version itself. Do not edit package.json to choose one.
Publishing happens inside publish. Once that job is green, npm and flisk-sh/releases are live even if later jobs are red. Never re-dispatch after publish succeeded: it would publish the next version.
5. Finish the bookkeeping
mainbehind the published version: the run opensRelease <version> (automatic version bump). Approve it:gh pr checks <n> --watch && gh pr review <n> --approve. Its merge push runstag-bumped-release, which tagsv<version>and publishes the draft.mainalready at the version: the publish job tags the released commit and publishes the draft itself (Tag the released commit and publish the GitHub release).
6. Verify every channel
V=<version>
curl -s https://registry.npmjs.org/@flisk-sh%2fflisk/$V | grep -o '"version":"[^"]*"' # scoped npm
gh release view v$V -R flisk-sh/releases --json isDraft,assets --jq '{draft:.isDraft,n:(.assets|length)}'
gh release view v$V -R flisk-sh/flisk --json isDraft,assets --jq '{draft:.isDraft,assets:[.assets[].name]}'
gh api repos/flisk-sh/flisk/git/refs/tags/v$V --jq .object.sha # tag exists
Expected:
- the scoped npm version exists;
- the binaries release is not a draft and has 5 binaries, the engine, the zip, the checksums and the signature;
- the
flisk-sh/fliskrelease is not a draft and haschecksums.txt,.sigstore.jsonand.intoto.jsonl; - the tag exists.
GitHub Packages: docs/github-packages.md#maintainer-release-checks.
Repairs, all without a new release:
- Draft or tag missing on
flisk-sh/flisk: check each piece, then create only what is missing.- Pick the tag target. The tag must point at a commit whose
package.jsonsays$V:- the run bumped the version: the merged bump commit (
Release $V (automatic version bump)onmain); - no bump: the run's
headSha. Confirm withgh api "repos/flisk-sh/flisk/contents/package.json?ref=<commit>" --jq .content | base64 -d | grep '"version"'.
- the run bumped the version: the merged bump commit (
- Tag, only if
gh api repos/flisk-sh/flisk/git/refs/tags/v$Vreturns 404:gh api repos/flisk-sh/flisk/git/refs -f ref=refs/tags/v$V -f sha=<commit>. - Release: if
gh release view v$V -R flisk-sh/fliskfails, there is none. Create an empty draft (gh release create v$V -R flisk-sh/flisk --draft --latest=false --title v$V --notes "Signed checksums for flisk $V.") and runsign-release.yml(next item). - Publish only once the draft has
checksums.txtandchecksums.txt.sigstore.json:gh release edit v$V -R flisk-sh/flisk --tag v$V --draft=false --latest=false. Never publish an unsigned release.
- Pick the tag target. The tag must point at a commit whose
- Signature missing:
gh workflow run sign-release.yml -f version=$V. It attacheschecksums.txtand its signature to both releases. - GitHub Packages missing:
gh workflow run publish-existing-npm-to-github-packages.yml -f version=$V. - No unscoped
fliskon npm: expected. npm rejects the name as too similar toplist(E403), so the release no longer publishes it (docs/releasing.md#unscoped-npm-package-flisk).
7. Report and improve
Tell the user:
- the version and the release run URL;
- each channel's state from step 6;
- anything finished by hand and anything still open.
If this release hit a failure the troubleshooting table does not cover, add a row to docs/releasing.md, a case-study line, and, if the workflow can prevent it, a fix to release-binaries.yml, in a PR. Add one line to .claude/memory/MEMORY.md when it is a trap other agents will hit.