From ad496caf48f2c1560abc1a74f2af08f3f68935ce Mon Sep 17 00:00:00 2001 From: "Mingyu Chen (Rayner)" Date: Sat, 30 May 2026 17:20:15 +0800 Subject: [PATCH] docs: add release runbook (RELEASING.md) and document npm install in README Add RELEASING.md, a release-manager guide for the @apache-doris npm release process: version source, the Automation-token / scoped-name / chmod requirements, dry-run validation, tagging, verification, and troubleshooting. Add an Install section to the README presenting `npm install -g @apache-doris/doriscli` as the recommended install method, with build-from-source kept as the fallback. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 19 ++++++ RELEASING.md | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 RELEASING.md diff --git a/README.md b/README.md index f6958e0..b928c36 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,25 @@ doriscli tablet db.orders --detail --- +## Install + +The recommended way to install doris-cli is from **npm** — it ships prebuilt +binaries, so there is no Rust toolchain and no compile step: + +```bash +npm install -g @apache-doris/doriscli +doriscli --version +``` + +On install, npm pulls **only** the binary that matches your OS + CPU (via +`optionalDependencies` + `os`/`cpu` constraints). Supported platforms: macOS +(arm64), Linux (x64, arm64). The published package is scoped +(`@apache-doris/doriscli`), but the installed command is just `doriscli`. + +For any other platform — or to hack on doris-cli itself — build from source below. + +--- + ## Build Requires a recent stable Rust toolchain (verified on rustc 1.87; the crate uses the diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..a9fdb31 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,159 @@ +# Releasing Apache Doris CLI + +This is the release manager's guide to publishing a new version of the Doris CLI +to npm. The CLI is distributed as **prebuilt npm packages** under the +`@apache-doris` scope; end users install it with +`npm install -g @apache-doris/doriscli`. + +The whole build-and-publish pipeline is automated in +[`.github/workflows/release-npm.yml`](.github/workflows/release-npm.yml). A +release manager's job is to bump the version, validate with a dry run, and push +a tag — the workflow does the rest. + +## What gets published + +A release publishes four packages, all at the same version: + +| Package | Contents | +|---|---| +| `@apache-doris/doriscli` | The launcher + `optionalDependencies` on the three platform packages. **This is what users install.** | +| `@apache-doris/doriscli-darwin-arm64` | Prebuilt binary for macOS arm64 | +| `@apache-doris/doriscli-linux-x64` | Prebuilt binary for Linux x64 | +| `@apache-doris/doriscli-linux-arm64` | Prebuilt binary for Linux arm64 | + +On install, npm resolves **only** the platform package matching the user's +OS + CPU (via `optionalDependencies` plus each platform package's `os`/`cpu` +fields); the launcher then execs that binary. The set of platforms lives in +`npm/doriscli/platforms.js` — the single source of truth shared by the launcher +and the packaging script. + +## Versioning + +**The published version is read from `Cargo.toml` (`[package].version`)** by +`npm/build-packages.cjs`, so npm always matches the crate. The git tag does not +set the version — it only has to match it. + +npm versions are **immutable**: you cannot re-publish a version. Every release is +a new version number, and a broken release is fixed by shipping the next patch +(see [Fixing a broken release](#fixing-a-broken-release)). + +## One-time prerequisites + +- An npm account in the **`apache-doris`** npm organization with publish rights to + the `@apache-doris` scope. +- Repository secret **`NPM_TOKEN`** set to a **Classic _Automation_ token** + (npmjs.com → Access Tokens → Generate New Token → Classic Token → **Automation**). + The org enforces two-factor auth for writes, and an Automation token is the type + that bypasses 2FA in CI. A granular or read-only token will fail — see + [Troubleshooting](#troubleshooting). +- Permission to push tags to `apache/doris-cli`. + +## Release steps + +### 1. Bump the version + +Edit `Cargo.toml`: + +```toml +[package] +version = "X.Y.Z" +``` + +Refresh the lockfile so the CI gate (which runs `--locked`) stays green, then +commit both files and merge to `main`: + +```bash +cargo check # rewrites Cargo.lock to match the new version +git add Cargo.toml Cargo.lock && git commit -m "release X.Y.Z" +``` + +### 2. Dry-run the pipeline (recommended) + +From the GitHub UI: **Actions → release-npm → Run workflow**, leaving +**`dry_run` ☑ checked** (the default). This builds every platform, assembles all +packages, and runs `npm publish --dry-run` — **no token is used and nothing is +published.** Confirm the run is green. + +CLI equivalent: + +```bash +gh workflow run release-npm.yml -f dry_run=true +``` + +### 3. Tag and push (the real publish) + +```bash +git tag -a vX.Y.Z -m "doriscli X.Y.Z" +git push vX.Y.Z # the remote pointing at apache/doris-cli +``` + +The `v*.*.*` tag triggers `release-npm`, which builds all three platforms and +publishes. Platform packages publish first; the main package publishes last, so +its `optionalDependencies` already resolve on the registry. + +### 4. Verify + +After the run is green: + +```bash +npm view @apache-doris/doriscli dist-tags # `latest` should be X.Y.Z + +# install into a throwaway dir and actually run the binary +tmp=$(mktemp -d) && ( cd "$tmp" \ + && npm install @apache-doris/doriscli \ + && ./node_modules/.bin/doriscli --version ) +rm -rf "$tmp" +``` + +npm moves the `latest` dist-tag to the highest version automatically, so a plain +`npm install -g @apache-doris/doriscli` picks up the new release. + +## How the pipeline works + +`release-npm.yml` has two jobs: + +- **build** — a matrix over the three platforms. Each runner does + `cargo build --release`, then `node npm/build-packages.cjs platform ` + assembles that platform package and uploads it as an artifact. +- **publish** — downloads the artifacts, **re-applies the executable bit**, + assembles the main package, and `npm publish`es each one. + +> ⚠️ **Do not remove the `chmod 0755` step in the publish job.** +> `actions/upload-artifact` strips the Unix executable bit, so the prebuilt +> binary arrives in the publish job as `0644`. The publish job re-runs +> `chmod 0755` on each binary *before* `npm publish`; without it, npm ships a +> non-executable binary and the CLI fails at runtime with `EACCES`. (This is +> exactly how `0.1.0` shipped broken.) + +### Actions allowlist + +`apache/doris-cli` is under the Apache org's GitHub Actions allowlist: only +GitHub-owned actions (`actions/*`) and **local** actions (`./.github/actions/...`) +are permitted. That's why the Rust toolchain setup is a local composite action +(`.github/actions/setup-rust-toolchain`) instead of a third-party one. **Do not +add third-party actions** to the workflow — they will be rejected. + +## Troubleshooting + +| Symptom | Cause / fix | +|---|---| +| `403 Forbidden ... You may not perform that action with these credentials` | The token can't publish that name. Ensure package names are scoped to `@apache-doris` and `NPM_TOKEN` has publish rights to the scope. | +| `403 ... Two-factor authentication ... required` | `NPM_TOKEN` doesn't bypass 2FA. Use a **Classic Automation** token, not a granular or read-only one. | +| CLI fails with `EACCES` after install | The published binary isn't executable — the `chmod 0755` step in the publish job is missing or didn't match the binary path. | +| CI fails with a lockfile / `--locked` error | `Cargo.lock` is out of sync after the version bump. Run `cargo check` (rewrites the lock) and commit `Cargo.lock`. | +| Want to add a platform | Add it to `npm/doriscli/platforms.js` **and** the build matrix in `release-npm.yml`. | + +## Fixing a broken release + +A published version is immutable, so fix it by publishing the next patch version. +Then steer users off the bad one: + +```bash +for p in doriscli doriscli-darwin-arm64 doriscli-linux-x64 doriscli-linux-arm64; do + npm deprecate @apache-doris/$p@ "broken; use +" +done +``` + +If the bad version is only minutes old and almost certainly uninstalled, you can +instead `npm unpublish @apache-doris/@` for each package +(npm allows single-version unpublish within 72 hours).