clickhouse/release
> version, sync the protected `release` branch from `main`, watch the npm publish (which is gated by a manual approval on the `npm-publish` environment), and create the GitHub Release from the CHANGELOG. Use this skill whenever the task is to "release", "cut a release", "publish a new version", or "ship" one (Web), `@clickhouse/client-common` (deprecated, rarely released), the standalone `@clickhouse/datatype-parser` (the type parser, under `packages/datatype-parser`), or `@clickhouse/rowbinary` (the RowBinary codec skill/package, under `skills/clickhouse-js-node-rowbinary`). The agent drives the GitHub Actions workflows (`gh workflow run`), watches CI, pauses at the human-judgment points (PR review, the approval gate, GitHub Release text), and hands the deployment-approval link back to the human. Do NOT use this for fixing a failing release PR — that is the `fix-release-pr` skill.
npx skills add https://github.com/ClickHouse/clickhouse-js --skill release
clickhouse-js packageThis skill is the source of truth for the release process. It supersedes the old
RELEASING.md (which now just points here).
user which package(s) they want to release and confirm — the packages are
versioned independently and ship on independent cadences:
@clickhouse/client — Node.js client (packages/client-node)@clickhouse/client-web — Web client (packages/client-web)@clickhouse/client-common — deprecated, effectively frozen; only cut afinal standalone release if explicitly asked.
@clickhouse/datatype-parser — standalone type parser (packages/datatype-parser)@clickhouse/rowbinary — standalone RowBinary codec skill/package(skills/clickhouse-js-node-rowbinary)
The flow forks into two families — workspace client packages (client /
client-web / client-common) and standalone packages (datatype-parser /
rowbinary) — which differ in version bumping and publish workflow. Pick the
right section below.
release is a long-lived, protected branch. It is _not_ a per-releasebranch. The npm-publish GitHub Actions environment only permits the
release branch to deploy, and it additionally requires a manual approval
before any publish job runs. You will hand the human an approval link and wait.
gh, watch CI, andpause at: (a) reviewing the release PR, (b) the npm-publish approval gate,
(c) the GitHub Release text. Never push to release directly — if the release
PR needs fixes, route them through main (see the fix-release-pr skill).
> One workflow per package. Each package has its own publish workflow:
>
> - @clickhouse/client → publish-client.yml
> - @clickhouse/client-web → publish-client-web.yml
> - @clickhouse/client-common → publish-client-common.yml
> - @clickhouse/datatype-parser → publish-datatype-parser.yml
> - @clickhouse/rowbinary → publish-skill-rowbinary.yml
>
> Each client workflow has two triggers: an automatic head publish on push to
> release and a manual latest publish via workflow_dispatch. The standalone
> packages have the manual trigger only.
>
> The client head triggers are path-scoped: a change touching only one client's
> own sources no longer republishes the others. Both @clickhouse/client and
> @clickhouse/client-web bundle the shared common sources via the src/common
> symlink (packages/*/src/common → packages/client-common/src), so
> packages/client-common/** is also an input to both client workflows — a
> change to the common sources publishes a new head for every client that
> bundles them.
@clickhouse/client, -web, -common)mainEach package now keeps its own CHANGELOG.md (the repo-wide root
CHANGELOG.md is frozen). The package you're releasing maps to:
@clickhouse/client → packages/client-node/CHANGELOG.md@clickhouse/client-web → packages/client-web/CHANGELOG.md@clickhouse/client-common → packages/client-common/CHANGELOG.mdThese are normally updated inside feature PRs as they merge to main, under
a top # <version> header. A common mistake: the in-progress entries sit under
the wrong header — e.g. still under the previously released version, or under
a version that doesn't match the one you're about to cut. (Note: shared/common
code is bundled into both clients, so such changes should appear in both the
client-node and client-web changelogs.)
bump_type you'll pick. Current version:
node -p "require('./packages/client-node/package.json').version" # or client-web / client-common
CHANGELOG.md has a top # <new-version> section thatactually contains this release's notes.
main and merge it quicklybefore bumping — do not bundle the changelog fix into the bump PR.
main)Dispatch the bump-version workflow. It bumps the selected package's
package.json + src/version.ts and opens a release-<pkg>-<ver> PR **against
main**:
gh workflow run bump-version.yml --ref main \
-f package='@clickhouse/client' \
-f bump_type=patch # patch | minor | major
Find and watch the resulting PR, get it reviewed, and merge it into main:
gh run list --workflow=bump-version.yml --limit 1
gh pr list --search 'head:release-client-' --state open # locate the bump PR
release from main (the "release PR")Open a real PR with base release, head main. This PR is the snapshot
that gets reviewed once more _as a whole_ (code-review tooling runs on the full
diff):
gh pr create --base release --head main \
--title "<version> release" \
--body "Sync release from main to cut <package> <version>."
release. Fix on main via aseparate PR (use the fix-release-pr skill), then the release PR picks the fix
up automatically. Keep release and main from drifting.
head publish + e2eMerging into release pushes to release, which triggers the path-scoped
head publish for each package whose sources changed (publish-client.yml,
publish-client-web.yml, publish-client-common.yml). It publishes a head
pre-release build (an internal beta — not tracked as a GitHub Release); the
client workflows then run an automatic e2e job against the published version
and a live ClickHouse — the Node.js client installs it across Node 20/22/24/26
and runs integration tests, and the Web client runs a real-browser smoke
(chromium + firefox) via Playwright.
npm-publish environment → **they pause formanual approval**. Find the run(s) — one per package that changed — give the
human the approval link, and wait:
gh run list --workflow=publish-client.yml --branch release --limit 3
gh run view <run-id> --json url -q .url # hand this URL to the human to approve
gh run watch <run-id> # waits for completion
(Use publish-client-web.yml / publish-client-common.yml for the other
packages.)
e2e job in publish-client.yml / publish-client-web.yml is sufficientverification — no manual @head testing is needed. Just watch it.
head tag.Suggest moving that tag out of the way so nobody installs it, e.g.:
npm dist-tag add @clickhouse/client@<broken-version> debugging
Then fix forward on main and re-sync release.
latest (manual dispatch, per package)Once the head e2e is green, dispatch the package's publish workflow manually
from the release branch (no inputs). This builds, publishes to the latest tag
(npm OIDC + provenance), and pushes a git tag (client-<ver>, client-web-<ver>,
or client-common-<ver>):
gh workflow run publish-client.yml --ref release # @clickhouse/client
# gh workflow run publish-client-web.yml --ref release # @clickhouse/client-web
# gh workflow run publish-client-common.yml --ref release # @clickhouse/client-common (deprecated)
This also runs under npm-publish → approval gate again. Same drill: hand
over the approval URL, then watch:
gh run list --workflow=publish-client.yml --branch release --event workflow_dispatch --limit 1
gh run view <run-id> --json url -q .url
gh run watch <run-id>
Repeat for each package being released, dispatching its own workflow.
For the deprecated @clickhouse/client-common, if you ever cut one, also confirm
its npm deprecation notice is in place (it should already be).
The auto-published head betas are not tracked as Releases. The latest
release is. Create a GitHub Release from the matching section of **that
package's** CHANGELOG.md (e.g. packages/client-node/CHANGELOG.md for
@clickhouse/client).
client-<ver> / client-web-<ver>/ client-common-<ver> (note: client tags have no v prefix).
(the lines under # <version> up to the next # header):
awk -v v="1.23.0" '$0=="# "v{f=1;next} /^# /{f=0} f' \
packages/client-node/CHANGELOG.md > /tmp/notes.md
--title — GitHub renders the title from the tag itself. Theseare not pre-releases, so do not pass --prerelease:
gh release create client-1.23.0 --notes-file /tmp/notes.md
Compare pushed tags against existing releases and create any that are missing,
pulling each one's notes from the matching CHANGELOG section:
gh release list --limit 50
git ls-remote --tags origin | grep -oE 'client(-web|-common)?-[0-9.]+'
@clickhouse/datatype-parser, @clickhouse/rowbinary)These ship independently. **There is no bump-version workflow and no head
beta** for them.
mainEdit the version in the package's package.json (these have no src/version.ts):
@clickhouse/datatype-parser → packages/datatype-parser/package.json@clickhouse/rowbinary → skills/clickhouse-js-node-rowbinary/package.jsonDo this in a normal PR to main, together with the relevant entry in **that
package's own** CHANGELOG.md (verify the changelog as in Part A, Step 1):
@clickhouse/datatype-parser → packages/datatype-parser/CHANGELOG.md@clickhouse/rowbinary → skills/clickhouse-js-node-rowbinary/CHANGELOG.mdMerge it.
release from mainSame as Part A, Step 3: open a base release ← head main PR, get it reviewed as
a whole, merge it. (Pushing to release does not auto-publish these
standalone packages — only the client packages have an auto head publish.)
latest (manual dispatch, no inputs)Dispatch the package's own publish workflow from the release branch. These take
no inputs; the release-branch ref is required by the workflow's if guard:
# type parser:
gh workflow run publish-datatype-parser.yml --ref release
# rowbinary codec skill/package:
gh workflow run publish-skill-rowbinary.yml --ref release
Each workflow builds, packs + smoke-tests the tarball, publishes it to latest
(OIDC + provenance), pushes a git tag (datatype-parser-v<ver> /
rowbinary-v<ver> — note the v prefix here), then runs the e2e job
across Node 20/22/24/26.
Same npm-publish approval gate applies — hand over the approval URL and
watch:
gh run list --workflow=publish-datatype-parser.yml --branch release --limit 1
gh run view <run-id> --json url -q .url
gh run watch <run-id>
Same as Part A, Step 6, using the standalone tag (datatype-parser-v<ver> /
rowbinary-v<ver>) and the notes from that package's own CHANGELOG.md
(packages/datatype-parser/CHANGELOG.md or
skills/clickhouse-js-node-rowbinary/CHANGELOG.md). No --title, no
--prerelease. Backfill any missing releases.
Client packages:
CHANGELOG.md (e.g. packages/client-node/CHANGELOG.md) on main (quick PR if wrong).gh workflow run bump-version.yml --ref main -f package=… -f bump_type=… → review & merge the bump PR to main.gh pr create --base release --head main → review the whole release PR → merge.head publish (per-package publish-client*.yml, path-scoped) + e2e on push to release → approve at npm-publish, watch e2e. (If e2e fails: retag the bad build to debugging, fix forward.)gh workflow run publish-client.yml --ref release (or -web / -common) per package → approve, watch.gh release create <client-…-tag> --notes-file <changelog-section> (no --title, not prerelease); backfill missing releases.Standalone packages (datatype-parser / rowbinary):
package.json + its own CHANGELOG.md via a PR to main → merge.gh pr create --base release --head main → review → merge.gh workflow run publish-datatype-parser.yml --ref release (or publish-skill-rowbinary.yml) → approve, watch.gh release create <…-v…-tag> --notes-file <changelog-section>; backfill.Always: per package · each package has its own CHANGELOG.md (root is frozen) · release is protected (fix via main) · every publish needs npm-publish approval · head betas are untracked · GitHub Release notes come from the package's CHANGELOG.md with no explicit --title.
Take clickhouse/release from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.