Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
dbbe8ca
ci: raise npm fetch retries to de-flake installs (#902)
peter-leonov-ch Jun 27, 2026
7f10fc7
fix(e2e): keep post-publish integration.ts free of TS-only syntax (No…
peter-leonov-ch Jun 27, 2026
33bba98
test(upstream): honor serverError/clientError hints in the SQL test r…
peter-leonov-ch Jun 27, 2026
5ef1dcf
docs(rowbinary): document using the RowBinary parser with the JS clie…
Copilot Jun 27, 2026
e1c9271
ci: support Node.js 26, drop Node.js 18 (#906)
peter-leonov-ch Jun 27, 2026
3fb23cc
test(upstream): run upstream SQL tests through the RowBinary parser (…
peter-leonov-ch Jun 28, 2026
4c883b6
feat(rowbinary)!: resolve Enum8/Enum16 to their names (#912)
peter-leonov-ch Jun 28, 2026
6453285
fix(rowbinary): prettier-format SKILL.md (unbreak code-quality on mai…
peter-leonov-ch Jun 28, 2026
8e170d0
test(upstream): expand rowbinary allowlist after float/datetime fixes…
peter-leonov-ch Jun 28, 2026
db39d59
Per-package changelogs, per-package publish workflows, and a release …
peter-leonov-ch Jun 28, 2026
6abb4e2
Add RowBinary writers mirroring the existing readers (#911)
Copilot Jun 28, 2026
ce9e39c
fix(rowbinary): make writeRows a streaming generator over a fixed buf…
peter-leonov-ch Jun 28, 2026
30a0bf6
fix(rowbinary): harden writer edge cases from #911 review (#916)
peter-leonov-ch Jun 28, 2026
b8c65ad
docs(agents): split AGENTS.md into folder-specific files (#917)
peter-leonov-ch Jun 28, 2026
6af20ca
Rename rowbinary-parser skill to a read+write codec; split docs and s…
peter-leonov-ch Jun 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 306 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
---
name: release
description: >
Drive a package release of `ClickHouse/clickhouse-js` end to end: bump the
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
of the packages: `@clickhouse/client` (Node.js), `@clickhouse/client-web`
(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.
---

# Releasing a `clickhouse-js` package

This skill is the source of truth for the release process. It supersedes the old
`RELEASING.md` (which now just points here).

## Before you start — read this

1. **Releases are per package.** There is no "release everything" button. Ask the
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 a
final 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.

2. **`release` is a long-lived, protected branch.** It is _not_ a per-release
branch. 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.

3. **You drive, the human judges.** Run the workflows with `gh`, watch CI, and
pause 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.

---

## Part A — Workspace client packages (`@clickhouse/client`, `-web`, `-common`)

### Step 1 — Verify the package CHANGELOG on `main`

Each 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.md`

These 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.)

- Compute the version this release will produce: current version + the
`bump_type` you'll pick. Current version:
```bash
node -p "require('./packages/client-node/package.json').version" # or client-web / client-common
```
- Confirm that package's `CHANGELOG.md` has a top `# <new-version>` section that
actually contains this release's notes.
- If it's wrong, **fix it in a small separate PR to `main` and merge it quickly**
before bumping — do not bundle the changelog fix into the bump PR.

### Step 2 — Bump the version (opens a PR to `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`**:

```bash
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`**:

```bash
gh run list --workflow=bump-version.yml --limit 1
gh pr list --search 'head:release-client-' --state open # locate the bump PR
```

### Step 3 — Sync `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):

```bash
gh pr create --base release --head main \
--title "<version> release" \
--body "Sync release from main to cut <package> <version>."
```

- If review surfaces issues, **do not push to `release`**. Fix on `main` via a
separate PR (use the `fix-release-pr` skill), then the release PR picks the fix
up automatically. Keep `release` and `main` from drifting.
- When it's clean, **merge the release PR**.

### Step 4 — Watch the automatic `head` publish + e2e

Merging 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.

- These publishes run under the `npm-publish` environment → **they pause for
manual approval**. Find the run(s) — one per package that changed — give the
human the approval link, and wait:
```bash
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.)
- The `e2e` job in `publish-client.yml` / `publish-client-web.yml` is sufficient
verification — **no manual `@head` testing is needed**. Just watch it.
- **If e2e fails:** the broken build is already on npm under the `head` tag.
Suggest moving that tag out of the way so nobody installs it, e.g.:
```bash
npm dist-tag add @clickhouse/client@<broken-version> debugging
```
Then fix forward on `main` and re-sync `release`.

### Step 5 — Publish to `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>`):

```bash
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:

```bash
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).

### Step 6 — Create the GitHub Release

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`).

- The tag was pushed by the publish workflow: `client-<ver>` / `client-web-<ver>`
/ `client-common-<ver>` (note: client tags have **no** `v` prefix).
- Use the CHANGELOG section that documents this release as the body. Extract it
(the lines under `# <version>` up to the next `# ` header):
```bash
awk -v v="1.23.0" '$0=="# "v{f=1;next} /^# /{f=0} f' \
packages/client-node/CHANGELOG.md > /tmp/notes.md
```
- **Do not pass `--title`** — GitHub renders the title from the tag itself. These
are not pre-releases, so do not pass `--prerelease`:
```bash
gh release create client-1.23.0 --notes-file /tmp/notes.md
```
- **Backfill check:** sometimes earlier releases are missing their GitHub Release.
Compare pushed tags against existing releases and create any that are missing,
pulling each one's notes from the matching CHANGELOG section:
```bash
gh release list --limit 50
git ls-remote --tags origin | grep -oE 'client(-web|-common)?-[0-9.]+'
```

---

## Part B — Standalone packages (`@clickhouse/datatype-parser`, `@clickhouse/rowbinary`)

These ship independently. **There is no `bump-version` workflow and no `head`
beta** for them.

### Step 1 — Bump the version manually on `main`

Edit 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.json`

Do 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.md`

Merge it.

### Step 2 — Sync `release` from `main`

Same 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.)

### Step 3 — Publish to `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:

```bash
# 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:

```bash
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>
```

### Step 4 — Create the GitHub Release

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.

---

## Quick reference

**Client packages:**

1. Verify/fix the top section of the package's own `CHANGELOG.md` (e.g. `packages/client-node/CHANGELOG.md`) on `main` (quick PR if wrong).
2. `gh workflow run bump-version.yml --ref main -f package=… -f bump_type=…` → review & merge the bump PR to `main`.
3. `gh pr create --base release --head main` → review the whole release PR → merge.
4. Auto `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.)
5. `gh workflow run publish-client.yml --ref release` (or `-web` / `-common`) per package → **approve**, watch.
6. `gh release create <client-…-tag> --notes-file <changelog-section>` (no `--title`, not prerelease); backfill missing releases.

**Standalone packages (datatype-parser / rowbinary):**

1. Bump version in the package's `package.json` + its own `CHANGELOG.md` via a PR to `main` → merge.
2. `gh pr create --base release --head main` → review → merge.
3. `gh workflow run publish-datatype-parser.yml --ref release` (or `publish-skill-rowbinary.yml`) → **approve**, watch.
4. `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`.
2 changes: 1 addition & 1 deletion .claude/skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Use this skill before running any of the `npm run test:*`, `npm run lint`, `npm

## Prerequisites

- **Node.js 22 recommended** (matches `.nvmrc`). The root `package.json` declares `"engines": { "node": ">=20.19.0" }`, and CI tests Node 20, 22, and 24.
- **Node.js 22 recommended** (matches `.nvmrc`). The root `package.json` declares `"engines": { "node": ">=20.19.0" }`, and CI tests Node 20, 22, 24, and 26.
- **Docker** with the Compose plugin (`docker compose ...`). Required only for integration tests and any example that talks to a real server.

## 1. Install dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/instructions/review.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ When reviewing PRs to the `release` branch focus on flagging breaking changes an

## CHANGELOG

Every PR that adds a feature, fixes a bug, or changes observable behavior or the public API **must update `CHANGELOG.md` in the same PR**. If such a change does not touch `CHANGELOG.md`, flag it and ask the author to add an entry (a note in the PR description alone is not sufficient). Verify the entry follows the conventions in [`AGENTS.md`](../../AGENTS.md) ("API quality and stability"): placed under the top-most version heading (a new `# x.y.z` heading matching the unreleased `package.json` version when the latest heading is already released), grouped under a lowercase section heading (`## New features` / `## Improvements` / `## Bug fixes`), and ending with a `([#<n>])` PR reference link. Pure refactors, test-only changes, docs, and CI/tooling changes do not require a CHANGELOG entry.
Every PR that adds a feature, fixes a bug, or changes observable behavior or the public API **must update the changelog of every affected package in the same PR**. Each package keeps its own `CHANGELOG.md` (the repository-wide root `CHANGELOG.md` is **frozen** — new entries must not go there); see the package → changelog mapping in [`AGENTS.md`](../../AGENTS.md) ("API quality and stability"). A change to shared/common code bundled into both clients affects `@clickhouse/client` and `@clickhouse/client-web`, so expect both of their changelogs to be updated. If such a change does not touch the relevant package changelog(s), flag it and ask the author to add an entry (a note in the PR description alone is not sufficient). Verify the entry follows the `AGENTS.md` conventions: placed under the top-most version heading (a new `# x.y.z` heading matching that package's unreleased `package.json` version when the latest heading is already released), grouped under a lowercase section heading (`## New features` / `## Improvements` / `## Bug fixes`), and ending with a `([#<n>])` PR reference link. Pure refactors, test-only changes, docs, and CI/tooling changes do not require a changelog entry.

## Breaking changes

Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ A short description of the changes with a link to an open issue.
Delete items not relevant to your PR:

- [ ] Unit and integration tests covering the common scenarios were added
- [ ] A human-readable description of the changes was provided to include in CHANGELOG
- [ ] A human-readable changelog entry was added to every affected package's `CHANGELOG.md` (e.g. `packages/client-node/CHANGELOG.md`; a shared/common change updates both client packages — `client-node` and `client-web`; the root `CHANGELOG.md` is frozen)
- [ ] For significant changes, documentation in https://github.com/ClickHouse/clickhouse-docs was updated with further explanations or tutorials
8 changes: 8 additions & 0 deletions .github/workflows/e2e-skills.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ on:
branches:
- release

env:
# Network resilience: npm's default of 2 fetch retries is not enough for
# the transient registry errors (ECONNRESET) we regularly hit in CI.
NPM_CONFIG_FETCH_RETRIES: "5"
NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: "10000"
NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: "60000"
NPM_CONFIG_FETCH_TIMEOUT: "600000"

jobs:
skills-packaging:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ env:
OTEL_RESOURCE_ATTRIBUTES: "service.namespace=clickhouse-js,deployment.environment=ci"
VITEST_OTEL_ENABLED: "true"
VITEST_COVERAGE: "true"
# Network resilience: npm's default of 2 fetch retries is not enough for
# the transient registry errors (ECONNRESET) we regularly hit in CI.
NPM_CONFIG_FETCH_RETRIES: "5"
NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: "10000"
NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: "60000"
NPM_CONFIG_FETCH_TIMEOUT: "600000"

jobs:
code-quality:
Expand Down
Loading