Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
137 changes: 110 additions & 27 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,24 @@ jobs:
echo "$ldd_output" | grep -qE "not a dynamic executable|statically linked"
echo "Static linking verified."

- name: Rename binary
run: cp target/${{ matrix.target }}/release/clickhousectl clickhousectl-${{ matrix.target }}
- name: Compute version
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Package archive
run: |
VERSION="${{ steps.version.outputs.version }}"
TARGET="${{ matrix.target }}"
DIR="clickhousectl-${TARGET}-v${VERSION}"
mkdir "$DIR"
cp "target/${TARGET}/release/clickhousectl" "$DIR/"
tar -czf "${DIR}.tar.gz" "$DIR"

- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: clickhousectl-${{ matrix.target }}
path: clickhousectl-${{ matrix.target }}
name: clickhousectl-${{ matrix.target }}-v${{ steps.version.outputs.version }}
path: clickhousectl-${{ matrix.target }}-v${{ steps.version.outputs.version }}.tar.gz

smoke-test:
name: Smoke test (${{ matrix.distro }})
Expand All @@ -81,41 +91,47 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- distro: ubuntu:20.04
artifact: clickhousectl-x86_64-unknown-linux-musl
- distro: ubuntu:22.04
artifact: clickhousectl-x86_64-unknown-linux-musl
- distro: ubuntu:24.04
artifact: clickhousectl-x86_64-unknown-linux-musl
- distro: debian:bullseye
artifact: clickhousectl-x86_64-unknown-linux-musl
- distro: debian:bookworm
artifact: clickhousectl-x86_64-unknown-linux-musl
- distro: centos:7
artifact: clickhousectl-x86_64-unknown-linux-musl
- distro: amazonlinux:2
artifact: clickhousectl-x86_64-unknown-linux-musl
- distro: alpine:3.18
artifact: clickhousectl-x86_64-unknown-linux-musl
distro:
- ubuntu:20.04
- ubuntu:22.04
- ubuntu:24.04
- debian:bullseye
- debian:bookworm
- centos:7
- amazonlinux:2
- alpine:3.18
env:
TARGET: x86_64-unknown-linux-musl
steps:
- name: Compute version
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Download artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ matrix.artifact }}
name: clickhousectl-${{ env.TARGET }}-v${{ steps.version.outputs.version }}

- name: Extract archive
run: |
VERSION="${{ steps.version.outputs.version }}"
DIR="clickhousectl-${TARGET}-v${VERSION}"
tar -xzf "${DIR}.tar.gz"
chmod +x "${DIR}/clickhousectl"
echo "BIN_PATH=${PWD}/${DIR}/clickhousectl" >> "$GITHUB_ENV"

- name: Run smoke test in ${{ matrix.distro }}
run: |
chmod +x ${{ matrix.artifact }}
docker run --rm \
-v "$PWD/${{ matrix.artifact }}:/usr/local/bin/clickhousectl:ro" \
-v "${BIN_PATH}:/usr/local/bin/clickhousectl:ro" \
${{ matrix.distro }} \
sh -c 'clickhousectl --version && clickhousectl --help'

release:
name: Create Release
needs: [build, smoke-test]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
steps:
Expand All @@ -128,15 +144,82 @@ jobs:
with:
path: artifacts

- name: Collect binaries
- name: Collect archives
run: |
mkdir release
for dir in artifacts/clickhousectl-*/; do
cp "$dir"/* release/
for dir in artifacts/clickhousectl-*-v*/; do
cp "$dir"/*.tar.gz release/
done
ls -l release/

- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
run: gh release create "$TAG" --generate-notes release/*

publish-crates:
name: Publish to crates.io
needs: release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable @ 2026-03-27

- name: Publish clickhouse-cloud-api (skip if version already on crates.io)
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
VERSION=$(cargo pkgid -p clickhouse-cloud-api | sed 's/.*[#@]//')
if curl -fsSI "https://crates.io/api/v1/crates/clickhouse-cloud-api/${VERSION}" >/dev/null 2>&1; then
echo "clickhouse-cloud-api@${VERSION} already published; skipping."
echo "SKIP_API_PROPAGATION=1" >> "$GITHUB_ENV"
else
cargo publish -p clickhouse-cloud-api
fi

- name: Wait for crates.io index propagation
if: env.SKIP_API_PROPAGATION != '1'
run: sleep 30

- name: Publish clickhousectl
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish -p clickhousectl

publish-npm:
name: Publish to npm
needs: release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
# `id-token: write` is required for npm OIDC trusted publishing. Do NOT
# set NODE_AUTH_TOKEN below — per npm docs, even an empty value forces
# token-based auth and short-circuits the OIDC flow.
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'

- name: Align package version with tag
working-directory: npm
run: |
VERSION="${GITHUB_REF_NAME#v}"
npm version "$VERSION" --no-git-tag-version --allow-same-version

- name: Publish
working-directory: npm
run: npm publish --provenance --access public
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
/target
/npm/node_modules/
/npm/vendor/
/npm/package-lock.json
/npm/*.tgz
.env
/.env
/.env.local
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ cargo add -p clickhouse-cloud-api url
- If the user references a GitHub issue (e.g. "work on issue 3"), use `gh issue view 3` to get the details, then create a branch like `issue-3-short-description`.
- Update `README.md` and any relevant documentation as part of the change — PRs should include doc updates for new or changed functionality.
- Commit to the branch, push, and create a PR with `gh pr create`.
- Releases are done by tagging `main` (e.g. `git tag v0.1.4 && git push origin v0.1.4`), which triggers the GitHub Actions release workflow. Ensure version is updated in `crates/clickhousectl/Cargo.toml`.
- Releases are done by tagging `main` (e.g. `git tag v0.1.4 && git push origin v0.1.4`), which triggers the GitHub Actions release workflow. Before tagging: bump `version` in `crates/clickhousectl/Cargo.toml` (always) and `crates/clickhouse-cloud-api/Cargo.toml` (if the API client changed — the publish step skips crates.io if the version is unchanged). `npm/package.json` is bumped automatically by the workflow.
- Release-time secrets/config: `CARGO_REGISTRY_TOKEN` repo secret for crates.io; npm publishing uses OIDC trusted publishing (configured one-time on npmjs.com against this repo + `release.yml` + the `publish-npm` job). For the very first npm publish, seed the package by running `npm publish` locally with a granular token, then configure the trusted publisher and revoke the token.

## Testing locally

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ cargo add -p clickhouse-cloud-api url
- If the user references a GitHub issue (e.g. "work on issue 3"), use `gh issue view 3` to get the details, then create a branch like `issue-3-short-description`.
- Update `README.md` and any relevant documentation as part of the change — PRs should include doc updates for new or changed functionality.
- Commit to the branch, push, and create a PR with `gh pr create`.
- Releases are done by tagging `main` (e.g. `git tag v0.1.4 && git push origin v0.1.4`), which triggers the GitHub Actions release workflow. Ensure version is updated in `crates/clickhousectl/Cargo.toml`.
- Releases are done by tagging `main` (e.g. `git tag v0.1.4 && git push origin v0.1.4`), which triggers the GitHub Actions release workflow. Bump `version` in `crates/clickhousectl/Cargo.toml` (always) and `crates/clickhouse-cloud-api/Cargo.toml` (only when the API client changed; publish step skips if unchanged). `npm/package.json` is bumped automatically by the workflow. Release-time secrets: `CARGO_REGISTRY_TOKEN` for crates.io; npm uses OIDC trusted publishing (configured one-time on npmjs.com against this repo, `release.yml`, and the `publish-npm` job).

## Testing locally

Expand Down
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,40 @@ curl https://clickhouse.com/cli | sh

The install script will download the correct version for your OS and install to `~/.local/bin/clickhousectl`. A `chctl` alias is also created automatically for convenience.

### From source
### `cargo binstall`

If you already have [`cargo-binstall`](https://github.com/cargo-bins/cargo-binstall), this pulls the prebuilt binary from GitHub Releases:

```bash
cargo binstall clickhousectl
```

### npm

```bash
cargo install --path .
npm install -g clickhousectl
```

This installs an npm wrapper package that downloads the matching prebuilt binary from GitHub Releases at install time. Both `clickhousectl` and `chctl` are exposed as commands. If you use `npm install --ignore-scripts`, the download is skipped — fall back to one of the other install paths.

### From crates.io

Builds from source:

```bash
cargo install clickhousectl
```

### From this repo

```bash
cargo install --path crates/clickhousectl
```

### Direct download

Prebuilt archives for each release are attached to the [GitHub release](https://github.com/ClickHouse/clickhousectl/releases). Archives are named `clickhousectl-{target}-v{version}.tar.gz` and contain a single directory of the same name with the `clickhousectl` binary inside. Supported targets: `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl`, `x86_64-apple-darwin`, `aarch64-apple-darwin`.

## Local

### Installing and managing ClickHouse versions
Expand Down
6 changes: 5 additions & 1 deletion crates/clickhouse-cloud-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ name = "clickhouse-cloud-api"
version = "0.2.0"
edition = "2024"
description = "Typed Rust client for the ClickHouse Cloud API"
license = "MIT"
license = "Apache-2.0"
repository = "https://github.com/ClickHouse/clickhousectl"
homepage = "https://github.com/ClickHouse/clickhousectl"
keywords = ["clickhouse", "cloud", "api", "client"]
categories = ["api-bindings", "database"]

[dependencies]
reqwest = { version = "0.13", default-features = false, features = ["json", "query", "rustls"] }
Expand Down
11 changes: 11 additions & 0 deletions crates/clickhousectl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
name = "clickhousectl"
version = "0.2.0"
edition = "2024"
description = "The CLI for ClickHouse: local and cloud."
license = "Apache-2.0"
repository = "https://github.com/ClickHouse/clickhousectl"
homepage = "https://github.com/ClickHouse/clickhousectl"
keywords = ["clickhouse", "cli", "database", "cloud"]
categories = ["command-line-utilities", "database"]

[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }{ archive-suffix }"
pkg-fmt = "tgz"
bin-dir = "{ name }-{ target }-v{ version }/{ bin }{ binary-ext }"

[dependencies]
clap = { version = "4", features = ["derive"] }
Expand Down
12 changes: 9 additions & 3 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,18 @@ if [ -z "$LATEST" ]; then
fi
echo "Latest release: $LATEST"

# Download binary
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${LATEST}/clickhousectl-${TARGET}"
# Download archive
ARCHIVE_NAME="clickhousectl-${TARGET}-${LATEST}.tar.gz"
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${LATEST}/${ARCHIVE_NAME}"
echo "Downloading ${DOWNLOAD_URL}..."

mkdir -p "$INSTALL_DIR"
curl -fsSL "$DOWNLOAD_URL" -o "${INSTALL_DIR}/${BINARY_NAME}"
TMPDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR"' EXIT

curl -fsSL "$DOWNLOAD_URL" -o "${TMPDIR}/${ARCHIVE_NAME}"
tar -xzf "${TMPDIR}/${ARCHIVE_NAME}" -C "$TMPDIR"
mv "${TMPDIR}/clickhousectl-${TARGET}-${LATEST}/${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}"
chmod +x "${INSTALL_DIR}/${BINARY_NAME}"

echo "Installed ${BINARY_NAME} to ${INSTALL_DIR}/${BINARY_NAME}"
Expand Down
27 changes: 27 additions & 0 deletions npm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# clickhousectl

The official ClickHouse CLI: local ClickHouse version manager and ClickHouse Cloud control plane.

```bash
npm install -g clickhousectl
clickhousectl --help
```

This npm package is a thin wrapper that downloads the prebuilt `clickhousectl` binary for your platform from [GitHub Releases](https://github.com/ClickHouse/clickhousectl/releases) at install time. It is published in lock-step with the release tag.

Supported platforms:

- Linux x86_64 (musl)
- Linux aarch64 (musl)
- macOS x86_64
- macOS arm64

For full documentation, see the [project repository](https://github.com/ClickHouse/clickhousectl).

## Installation flags

`clickhousectl` requires npm's postinstall step to fetch the binary. If you use `npm install --ignore-scripts`, the binary won't be downloaded and the `clickhousectl` command will fail with a helpful error. Re-run install without `--ignore-scripts`, or grab the binary directly from GitHub Releases.

## License

Apache-2.0
29 changes: 29 additions & 0 deletions npm/bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node
// Thin shim that execs the platform-specific clickhousectl binary that
// install.js downloaded into ../vendor at npm install time.

const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');

const binaryName = process.platform === 'win32' ? 'clickhousectl.exe' : 'clickhousectl';
const binaryPath = path.join(__dirname, '..', 'vendor', binaryName);

if (!fs.existsSync(binaryPath)) {
console.error(
`clickhousectl: binary not found at ${binaryPath}.\n` +
`The postinstall step may have been skipped (e.g. npm --ignore-scripts) ` +
`or failed. Reinstall with scripts enabled, or download manually from ` +
`https://github.com/ClickHouse/clickhousectl/releases`
);
process.exit(1);
}

const child = spawn(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
child.on('exit', (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
} else {
process.exit(code ?? 1);
}
});
Loading