squid-sdk release
End-to-end release procedure for the squid-sdk Rush monorepo. Unlike a single-version repo, squid-sdk publishes ~109 packages with independent per-package versions, driven by three manual workflow_dispatch workflows. GitHub releases are date-tagged (YYYY-MM-DD), not version-tagged. Most of the heavy lifting is automated — your job is to trigger the workflows in the right order, surface failures clearly, and (optionally) wrap the auto-generated release notes for minor/major bumps.
Mental model
| Stage | Workflow | What it does |
|---|---|---|
| 1. Bump | bump.yml |
rush change --verify then rush version --bump against release/arrowsquid, merges versions + CHANGELOGs back into the current branch |
| 2. Publish to npm | release.yml (mode=release) |
From release/arrowsquid, builds and publishes via OIDC trusted publishing (NPM_CONFIG_PROVENANCE=true) |
| 3. GitHub release | github-release.yml |
Diffs **/CHANGELOG.md since the previous date tag, aggregates per-package sections + contributors, creates YYYY-MM-DD tag and release |
Per-package change tracking lives in common/changes/@subsquid/<pkg>/<branch>_<timestamp>.json. Each PR that touches a published package should add a change file via rush change — no change file means CI's verify step fails.
How release/arrowsquid is maintained (and what silently doesn't reach it)
Releases publish from release/arrowsquid (release.yml mode=release always checks it out, ignoring the caller branch — line 29), but no workflow has an explicit step that pushes to release/arrowsquid. Grep the workflows and you won't find one. The branch is advanced purely as a side effect of rush version --bump in step 1: rush internally branches version/bump-* from master, commits the version + CHANGELOG bumps, then git merges that branch into release/arrowsquid and pushes it. (The merge back step afterwards brings the same bumps into master.)
The trap: that sync fires only when there is a real version bump to apply. A change that produces no bump never reaches release/arrowsquid, so the next release publishes a stale tree. This includes:
rush.json/ version-policy edits (e.g. moving a package to thedockerpolicy so it stops publishing to npm),- workflow / CI / other config changes,
- any batch where every pending change file is
type: none.
Hence, the scripts provide a leaky abstraction: running bump then release on master will publish the changes in master, but if anything breaks in between these two and you fix the release machinery, the release workflow won't pick it up.
Consequences and remedies:
- Editing the release machinery on
masterdoes not take effect on the next release untilrelease/arrowsquidcatches up. This is the easy way to "fix" a release bug on master, run a release, and watch nothing change. - A config-only fix lands on
release/arrowsquidonly when it rides along with the next real (non-none) bump (which re-branches from master and sweeps the whole tree in), or when you sync the branch directly:# release/arrowsquid is normally a strict ancestor of master, so this fast-forwards it: git push origin master:release/arrowsquid - Before cutting a release after any machinery/config change, verify the branch actually contains it:
git fetch origin master release/arrowsquid git log --oneline origin/release/arrowsquid..origin/master # commits on master not yet on the release branch
Preconditions
Confirm before starting:
- Working tree is clean.
- You're on the feature branch where the release will be cut from (typically
master); the merged PRs since the last release are the contents of this release. - Every published package modified since the last release has a corresponding change file. Run locally:
If this fails, ask the user to author missing change files withnode common/scripts/install-run-rush.js change --verify --target-branch origin/release/arrowsquidrush change(or open a PR that does). gh auth statuspasses and the user hasActions: writeonsubsquid/squid-sdk.
Steps
1. Bump versions (bump.yml)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
gh workflow run bump.yml --ref "$BRANCH" -R subsquid/squid-sdk
gh run watch --workflow=bump.yml --exit-status -R subsquid/squid-sdk
This pushes version bumps and CHANGELOG.md updates to both release/arrowsquid and the current branch. Pull locally afterwards: git pull.
If the merge-back step fails, the current branch diverged from release/arrowsquid in a non-trivial way — surface it and let the user resolve. Do not force.
2. Publish to npm (release.yml, mode=release)
gh workflow run release.yml -f mode=release -R subsquid/squid-sdk
RUN_ID=$(gh run list --workflow=release.yml --limit 1 --json databaseId --jq '.[0].databaseId' -R subsquid/squid-sdk)
gh run watch "$RUN_ID" --exit-status -R subsquid/squid-sdk
This always runs from release/arrowsquid regardless of caller branch (see release.yml line 29). The publish step uses OIDC trusted publishing — no NPM_TOKEN.
If a single package fails to publish, the tree is now half-published. Do not retry blindly. Check what landed:
npm view @subsquid/<failed-pkg> versions --json | tail -5
Common cause: the failing package's trusted publisher is not configured on npmjs.com. Surface the error verbatim to the user and stop — do not proceed to step 3 until npm and the local CHANGELOG state agree.
3. Cut the GitHub release (github-release.yml)
gh workflow run github-release.yml -R subsquid/squid-sdk
gh run watch --workflow=github-release.yml --exit-status -R subsquid/squid-sdk
The .github/actions/publish-releases action will:
- find the previous
YYYY-MM-DD[.N]tag, - diff
**/CHANGELOG.mdsince that tag, - emit
### @subsquid/<pkg> <ver>sections in the order Git returned the changed paths, - append a Contributors footer (resolved from commit emails to GitHub logins),
- create a
YYYY-MM-DDtag (orYYYY-MM-DD.Nif today already has one), - publish a release with those notes.
If no CHANGELOG.md diff exists, the action exits cleanly without creating a release — usually a sign that step 1 was skipped or every change was type: none.
Print the release URL when done:
TAG=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' -R subsquid/squid-sdk)
echo "https://github.com/subsquid/squid-sdk/releases/tag/$TAG"
4. (Optional) Standardize release notes
Only for releases that contain at least one minor or major bump. Patch-only releases keep the bare auto-generated body — they're not worth a headline.
Rules in release-notes-template.md. The wrap-around prepends a ## <Headline> and 1–3 sentence lead and appends a compare link; the per-package ### @subsquid/... blocks are kept verbatim.
To rewrite:
TAG=2026-04-15 # or whatever date tag was created
PREV=$(gh api repos/subsquid/squid-sdk/releases --jq '.[1].tag_name')
# headline + lead come from the user-visible diff; don't restate every package bullet
gh release edit "$TAG" -R subsquid/squid-sdk --notes "$(cat <<EOF
## <Headline>
<1-3 sentence lead.>
$(gh release view "$TAG" -R subsquid/squid-sdk --json body --jq .body)
**Full Changelog**: https://github.com/subsquid/squid-sdk/compare/${PREV}...${TAG}
EOF
)"
Pick the headline from the largest user-visible change in the aggregated body. Don't restate every package's bullets. The per-package sections are already there — the headline is editorial framing, not a summary.
Prerelease path
For prereleases (e.g. previewing changes from a feature branch under a non-latest dist-tag):
gh workflow run release.yml -f mode=prerelease --ref <feature-branch> -R subsquid/squid-sdk
This runs from the caller branch (not release/arrowsquid), uses rush publish --apply --partial-prerelease --tag <branch> with a commit-hash suffix, and does not cut a GitHub release. Skip bump.yml and github-release.yml for prereleases.
Failure modes
rush change --verifyfails in CI: a published package was modified without an accompanying change file. The user must runrush changefor each missing package and push the JSON files incommon/changes/@subsquid/<pkg>/.bump.ymlmerge-back fails: branch diverged fromrelease/arrowsquidnon-trivially. Resolve by hand; do not force-push.- Partial npm publish: stop and surface. Re-running
release.ymlwill skip already-published versions (Rush checks the registry), but if the cause was a misconfigured trusted publisher, the same package fails again. Fix the npmjs.com config first. - Date tag collision: the action automatically suffixes
.2,.3, … (createDateTag()inindex.js). Nothing to do. - No CHANGELOG diff after running
bump.yml: every change file wastype: none(e.g. doc/test-only changes). Action exits cleanly without a release. Confirm this is intended. github-release.ymlfails to resolve a contributor login: not fatal — the action just skips that author. Check logs if a known contributor is missing.
Quick reference
# Verify change files locally
node common/scripts/install-run-rush.js change --verify --target-branch origin/release/arrowsquid
# Three workflow triggers
gh workflow run bump.yml --ref "$(git rev-parse --abbrev-ref HEAD)" -R subsquid/squid-sdk
gh workflow run release.yml -f mode=release -R subsquid/squid-sdk
gh workflow run github-release.yml -R subsquid/squid-sdk
# Watch the latest run of a workflow
gh run watch --workflow=<file>.yml --exit-status -R subsquid/squid-sdk
# Inspect what was published
gh release view --web -R subsquid/squid-sdk