blob: a9fdb3155fefc9d9c6b3a6a1bb8096a43578bbfd [file] [view]
# 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 <apache-remote> 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 <key> <binary>`
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@<bad-version> "broken; use <good-version>+"
done
```
If the bad version is only minutes old and almost certainly uninstalled, you can
instead `npm unpublish @apache-doris/<pkg>@<bad-version>` for each package
(npm allows single-version unpublish within 72 hours).