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. A release manager's job is to bump the version, validate with a dry run, and push a tag — the workflow does the rest.
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.
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).
apache-doris npm organization with publish rights to the @apache-doris scope.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.apache/doris-cli.Edit Cargo.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:
cargo check # rewrites Cargo.lock to match the new version git add Cargo.toml Cargo.lock && git commit -m "release X.Y.Z"
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:
gh workflow run release-npm.yml -f dry_run=true
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.
After the run is green:
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.
release-npm.yml has two jobs:
cargo build --release, then node npm/build-packages.cjs platform <key> <binary> assembles that platform package and uploads it as an artifact.npm publishes each one.⚠️ Do not remove the
chmod 0755step in the publish job.actions/upload-artifactstrips the Unix executable bit, so the prebuilt binary arrives in the publish job as0644. The publish job re-runschmod 0755on each binary beforenpm publish; without it, npm ships a non-executable binary and the CLI fails at runtime withEACCES. (This is exactly how0.1.0shipped broken.)
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.
| 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. |
A published version is immutable, so fix it by publishing the next patch version. Then steer users off the bad one:
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).