| name | release-maintainer |
| description | Maintain and execute Rudder releases across npm, GitHub Releases, and Desktop portable assets. Use this skill whenever the user asks about 发版, release, publishing to npm, canary/stable promotion, GitHub Release assets, Desktop distribution, `npx @rudderhq/cli@latest start`, `npx @rudderhq/cli start`, broken npm `latest` dist-tags, full Desktop install smoke tests, GitHub Release API/rate-limit failures, version bumps, rollback, obsolete canary GitHub Release/tag cleanup after stable promotion, first-time package bootstrap, npm token-based fallback publishing, or release workflow failures. Prefer this skill for both planning and hands-on release operations in the Rudder repository, even when the user only asks "现在要做什么" or "帮我发版".
|
Release Maintainer
Help the user ship Rudder without losing track of release surfaces.
Rudder's release model has several moving parts: npm packages, git tags, GitHub
Releases, Desktop portable assets, release notes, and smoke tests. Your job is to
turn the current repo and remote state into a concrete release plan, then
execute only the steps the user has authorized.
When the user authorizes hands-on release work, operate with local and remote
tools instead of stopping at guidance. Prefer git, gh, npm, and repository
scripts for discoverable state. Ask the user only for secrets or decisions that
cannot be safely inferred.
First Principles
- npm publishes the CLI and public runtime/workspace packages.
- Desktop binaries are GitHub Release assets, not npm packages.
- Canary git tags keep the
canary/ namespace, for example
canary/v0.1.0-canary.2, but the GitHub Release display title should be the
clean version name, for example v0.1.0-canary.2.
- The public npm scope is
@rudderhq. Treat old examples using @rudder as
stale unless the repository explicitly reintroduces that scope.
- The stable user entrypoint is
npx @rudderhq/cli@latest start. Bare
npx @rudderhq/cli start resolves npm's latest dist-tag.
- After the persistent CLI exists,
rudder <command> and
npx @rudderhq/cli@latest <command> are the same CLI surface when they resolve
to the same CLI version. The npx form is the first-run or explicit dist-tag
form.
- Canaries publish from
main automatically and use npm dist-tag canary.
- Canary git tags use
canary/vX.Y.Z-canary.N. The matching GitHub Release
display title should be clean vX.Y.Z-canary.N, not the full tag name, and
it should be marked prerelease.
- A tag pushed by GitHub Actions'
GITHUB_TOKEN does not trigger another
workflow by itself. If canary npm publish creates the tag, release.yml must
explicitly dispatch desktop-release.yml, or the maintainer must do it.
- Stables are manually promoted from an explicitly chosen source ref and use
npm dist-tag
latest.
- Stable tags point at the original source commit, not at a generated release
commit.
- After a stable
vX.Y.Z is published and verified, older canary GitHub
Releases and canary/* git tags for released-or-older base versions should
be cleaned up unless the user asks to preserve them. This cleanup does not
unpublish npm package versions; npm *-canary.* versions are immutable
package history and should remain published.
- For stable releases,
main is only a selector until you resolve it. Before
the real publish, lock the source to an immutable commit SHA or stable tag and
use that same ref for dry-run, publish, Desktop recovery, and verification.
Do not chase newer main commits or newer canaries during the stable unless
the user explicitly asks to retarget the release.
- A stable release is not done until verification, npm, GitHub Release, Desktop
assets, and public notes/announcement are all handled.
- Pre-stable public canaries may temporarily be the default
latest install
path if there is no stable npm version yet and the user explicitly wants
npx @rudderhq/cli@latest start or bare npx @rudderhq/cli start to work
immediately. Call this out as an alpha/bootstrap exception, not the normal
canary policy.
- During the pre-stable bootstrap exception, npm
latest must not move to a
canary until the matching Desktop GitHub Release has all portable assets and
SHASUMS256.txt. Otherwise npx @rudderhq/cli@latest start can resolve a CLI
version whose Desktop release is not installable yet.
- A
--dry-run smoke only proves version and asset selection. It does not prove
download, checksum, extraction, symlink preservation, quarantine cleanup, or
app launchability. Do not claim the public Desktop install path is fixed until
a non-dry-run isolated npx ... start --no-open install succeeds on the
available platform, or until you explicitly state which platform could not be
tested.
- GitHub REST
403 during release lookup can mean anonymous API rate limiting,
not a missing Release. Before declaring a release absent, verify with
authenticated gh release view and, when possible, a direct asset URL check.
- Local npm auth is not guaranteed. If
npm whoami fails and a dist-tag must be
moved, use the repository npm-dist-tag.yml workflow instead of pretending the
local shell can publish or repair npm state.
- Release-maintenance commits that should not publish another canary must
include
[skip release], then be verified as skipped in release.yml.
- If a normal
main push is already running while you make release-maintenance
changes, watch it to completion. It may publish the next canary, and that
canary still needs npm, tag, Desktop, and Release-title verification. After
any emergency dist-tag repair, recheck latest after in-progress release.yml
runs finish or explicitly report the remaining overwrite risk.
- Once a stable source ref is locked, unrelated later
main pushes are not a
reason to retarget the stable. Track them only as overwrite or verification
risk. After the first stable exists, ordinary canary promotion should not move
npm latest; verify that invariant instead of waiting indefinitely for every
unrelated canary smoke.
- A stable promotion is not proven by npm, GitHub Release assets, and dry-run
smoke alone when the changed area can affect Desktop update behavior. If the
release fixes or depends on Desktop update/install behavior, run a real
Desktop update drill from an older installed build to the candidate before
saying the stable is ready.
Required Context
Start by reading only the context needed for the user's request:
doc/RELEASING.md for the main maintainer runbook.
doc/PUBLISHING.md for npm/package internals.
doc/RELEASE-AUTOMATION-SETUP.md for one-time GitHub/npm setup.
.github/workflows/release.yml when diagnosing canary/stable workflow behavior.
.github/workflows/desktop-release.yml when diagnosing Desktop artifacts.
.github/workflows/npm-dist-tag.yml when repairing latest or canary
dist-tags through GitHub Actions.
scripts/release.sh, scripts/release-package-map.mjs,
scripts/create-github-release.sh, scripts/promote-npm-dist-tag.mjs,
scripts/wait-for-desktop-release-assets.mjs, and
scripts/rollback-latest.sh when you need exact command behavior.
Use live checks for anything that may have changed, such as npm package
versions, GitHub Actions status, tags, and Release assets. Do not rely on
memory for those.
If the docs and live workflow disagree, inspect the workflow and scripts before
acting, then report the mismatch. The workflow is the executable truth during an
active release; docs should be updated after the release if policy changed.
Fast State Check
Before giving release instructions, collect the current state when local tools
are available:
git status --short --branch
git log --oneline --decorate --graph -8
git tag --list 'v*' --sort=-version:refname | head -10
node scripts/release-package-map.mjs list
./scripts/release.sh stable --print-version
./scripts/release.sh canary --print-version
When the task depends on remote truth, also check:
gh workflow list
gh run list --workflow release.yml --limit 10
gh run list --workflow desktop-release.yml --limit 10
gh run list --workflow npm-dist-tag.yml --limit 5
gh release list --repo Undertone0809/rudder --limit 20
git ls-remote --tags origin 'refs/tags/canary/v*'
npm view @rudderhq/cli dist-tags --json
npm view @rudderhq/cli versions --json
If the worktree has unrelated dirty files, explicitly say you will ignore them
and only touch release files needed for the task.
For hands-on publishing from a dirty local repo, prefer a clean temporary clone
or worktree, then keep the user's main workspace untouched:
tmp="$(mktemp -d /tmp/rudder-release-XXXXXX)"
git clone <repo-url> "$tmp"
cd "$tmp"
git switch main
git pull --ff-only
Only stash or restore files in the user's main checkout when they explicitly
asked you to switch or sync that checkout. Never drop unrelated user changes.
Decision Flow
One-Time Setup
Use this when the user is preparing release automation for the first time.
- Confirm
.github/workflows/release.yml,
.github/workflows/desktop-release.yml, and .github/CODEOWNERS are merged
to main.
- Confirm npm package existence for every public package:
node scripts/release-package-map.mjs list.
- If packages already exist, configure npm trusted publishing for each package
with owner
Undertone0809, repository rudder, and workflow filename
release.yml. npm expects only the workflow filename, not the
.github/workflows/ path.
- If packages do not exist, explain that a bootstrap publish is needed before
trusted publishing can be attached to those package names.
- Configure GitHub environments:
npm-canary: no reviewer, selected branch main.
npm-stable: maintainer approval, selected branch main.
- If trusted publishing is not ready, add an environment secret named
NPM_TOKEN to both release environments as a temporary fallback, using an
npm automation token with publish access to the @rudderhq packages.
- Keep long-lived
NPM_TOKEN out of the steady-state workflow once trusted
publishing is verified.
First-Time npm Bootstrap
Use this when packages do not exist yet, trusted publishing cannot be attached
yet, or the user has explicitly provided a one-time npm token.
- Confirm the package names with:
node scripts/release-package-map.mjs list
- Check existing npm state for every package before publishing. Missing
packages are expected on the first release; an existing version is a hard
stop for that package/version.
- If using a token, write it only to a temporary npmrc or environment-scoped
npm config. Do not echo it, commit it, store it in shell history, or leave it
behind. Remove the temp npmrc after publish and tell the user to revoke or
rotate any token pasted into chat.
- Publish all public packages in release-package-map order using the chosen
version and dist-tag. Do not retry a package/version that npm already
accepted; continue by verifying and repairing tags/releases instead.
- For a pre-stable public canary where no stable npm version exists and the
user wants
npx @rudderhq/cli@latest start or bare npx @rudderhq/cli start
to work, move both canary and latest to the same canary version across
every public package. After the first stable release exists, ordinary
canaries should only move canary.
- Immediately verify all dist-tags across the whole package set with a script,
not just
@rudderhq/cli.
RUDDER_EXPECTED_VERSION=0.1.0-canary.1 RUDDER_VERIFY_LATEST=1 node - <<'NODE'
const { execFileSync } = require('node:child_process');
const expected = process.env.RUDDER_EXPECTED_VERSION;
const rows = execFileSync('node', ['scripts/release-package-map.mjs', 'list'], { encoding: 'utf8' }).trim().split('\n').filter(Boolean);
let failed = false;
for (const row of rows) {
const pkg = row.split(/\s+/)[1];
const tags = JSON.parse(execFileSync('npm', ['--prefer-online', 'view', pkg, 'dist-tags', '--json'], { encoding: 'utf8' }));
const ok = tags.canary === expected && (!process.env.RUDDER_VERIFY_LATEST || tags.latest === expected);
console.log(`${ok ? 'ok' : 'bad'}\t${pkg}\tlatest=${tags.latest}\tcanary=${tags.canary}`);
if (!ok) failed = true;
}
process.exit(failed ? 1 : 0);
NODE
Canary Release
Canary releases should normally be automatic.
- Confirm the change is merged to
main.
- Watch the
Release workflow canary job. If the triggering commit is a
release-maintenance commit with [skip release], verify the run is skipped
before assuming no canary was produced. For pre-stable public canaries, the
canary job is not complete until it has dispatched the Desktop release,
verified the Desktop assets, and only then promoted npm latest.
- Confirm npm
canary points at the new prerelease for every public package,
not just @rudderhq/cli:
RUDDER_EXPECTED_VERSION=0.1.0-canary.N node - <<'NODE'
const { execFileSync } = require('node:child_process');
const expected = process.env.RUDDER_EXPECTED_VERSION;
const rows = execFileSync('node', ['scripts/release-package-map.mjs', 'list'], { encoding: 'utf8' }).trim().split('\n').filter(Boolean);
let failed = false;
for (const row of rows) {
const pkg = row.split(/\s+/)[1];
const version = execFileSync('npm', ['--prefer-online', 'view', `${pkg}@${expected}`, 'version'], { encoding: 'utf8' }).trim();
const tags = JSON.parse(execFileSync('npm', ['--prefer-online', 'view', pkg, 'dist-tags', '--json'], { encoding: 'utf8' }));
const ok = version === expected && tags.canary === expected;
console.log(`${ok ? 'ok' : 'bad'}\t${pkg}\tversion=${version}\tlatest=${tags.latest}\tcanary=${tags.canary}`);
if (!ok) failed = true;
}
process.exit(failed ? 1 : 0);
NODE
- If no stable npm version exists yet, confirm npm
latest also points at the
same canary for every public package. If it does not and the matching Desktop
assets are already complete, run the npm Dist Tag workflow for that version
and verify again:
gh workflow run npm-dist-tag.yml \
--repo Undertone0809/rudder \
--ref main \
-f version=0.1.0-canary.N \
-f tag=latest \
-f dry_run=false
Watch the resulting run to completion, then verify all public packages, not
just @rudderhq/cli.
5. Confirm tag canary/vX.Y.Z-canary.N exists locally and remotely.
6. Confirm desktop-release.yml ran for the canary tag. If it did not, dispatch
it explicitly; do not rely on the tag push to trigger it:
gh workflow run desktop-release.yml \
--ref main \
-f release_tag='canary/v0.1.0-canary.N' \
-f source_ref=main
- Verify the canary GitHub Release uses the clean display title
vX.Y.Z-canary.N, is prerelease, and has all Desktop assets:
gh release view 'canary/v0.1.0-canary.N' \
--repo Undertone0809/rudder \
--json tagName,name,url,isPrerelease,isDraft,assets \
--jq '{tagName,name,url,isPrerelease,isDraft,assets:[.assets[].name]}'
Expected canary Desktop assets:
Rudder-X.Y.Z-canary.N-linux-x64.AppImage
Rudder-X.Y.Z-canary.N-macos-arm64-portable.zip
Rudder-X.Y.Z-canary.N-macos-x64-portable.zip
Rudder-X.Y.Z-canary.N-windows-x64-portable.zip
SHASUMS256.txt
- Smoke test CLI resolution with isolated HOME and npm cache:
tmp_home="$(mktemp -d /tmp/rudder-cli-smoke-canary.XXXXXX)"
tmp_cache="$(mktemp -d /tmp/rudder-npm-cache-canary.XXXXXX)"
HOME="$tmp_home" npm_config_cache="$tmp_cache" npm_config_yes=true \
npx --prefer-online --yes @rudderhq/cli@canary start --dry-run --no-open
If canary smoke fails, do not promote stable. Fix forward on main, wait for
the next canary, and smoke again.
For pre-stable alpha releases, also smoke the user-facing latest path after
confirming the latest dist-tag moved. Use a full install smoke, not only
--dry-run, before telling a user that npx @rudderhq/cli@latest start works:
tmp_home="$(mktemp -d /tmp/rudder-npx-home.XXXXXX)"
tmp_cache="$(mktemp -d /tmp/rudder-npx-cache.XXXXXX)"
tmp_prefix="$(mktemp -d /tmp/rudder-npx-prefix.XXXXXX)"
tmp_output="$(mktemp -d /tmp/rudder-npx-output.XXXXXX)"
tmp_install="$(mktemp -d /tmp/rudder-npx-install.XXXXXX)"
HOME="$tmp_home" npm_config_cache="$tmp_cache" npm_config_prefix="$tmp_prefix" npm_config_yes=true PATH="$tmp_prefix/bin:$PATH" \
npx --prefer-online --yes @rudderhq/cli@latest start --no-open \
--output-dir "$tmp_output" \
--desktop-install-dir "$tmp_install"
On macOS, also verify the installed app is structurally usable:
app="$tmp_install/Rudder.app"
test -d "$app"
xattr -dr com.apple.quarantine "$app"
link="$app/Contents/Resources/server-package/node_modules/zod"
if [ -L "$link" ]; then
target="$(readlink "$link")"
case "$target" in
/tmp/*|/private/tmp/*|/var/folders/*) echo "bad absolute temp symlink: $target"; exit 1 ;;
esac
test -e "$link"
fi
If the user reported that installation succeeds but the app is unusable, do a
controlled launch check from the temporary install path and terminate only the
processes started from that path before claiming the app opens.
Stable Release
Prefer the GitHub Actions workflow over local stable publishing.
- Pick a source ref: exact commit SHA,
main, or a trusted canary source.
If main is used, immediately resolve it to a commit SHA and record that as
the release source. From this point on, use the immutable SHA unless the user
explicitly authorizes retargeting.
- Confirm public packages all share the intended stable semver:
node scripts/release-package-map.mjs list
./scripts/release.sh stable --print-version
- Confirm
releases/vX.Y.Z.md exists on the source ref.
- Check recent and in-progress
release.yml runs. If there are unrelated
main push canaries in progress, decide whether they are true blockers:
- before npm stable exists, they can temporarily move
latest to a canary,
but the stable publish will move it to the stable version;
- after npm stable exists, ordinary canaries should leave
latest alone via
--only-if-no-stable;
- do not wait for unrelated canaries merely to adopt their newer commits.
- If the stable contains Desktop startup, install, update, profile, migration,
or release-shell changes, choose a canary candidate and run the Desktop
update drill below before real stable publish. If the drill cannot run on
the local platform, name the missing platform and treat stable readiness as
not fully proven.
- Run the
Release workflow with dry_run: true, using the locked SHA as
source_ref.
- If dry-run passes, rerun with
dry_run: false, again using the same locked
SHA as source_ref.
- Wait for or request
npm-stable approval.
- Verify npm
latest, git tag vX.Y.Z, GitHub Release notes, Desktop release
workflow, and assets.
- Clean up obsolete canary GitHub Releases and
canary/* tags for the stable
base and any older base versions after the stable is proven. Preserve the
active next-line canary, for example keep canary/v0.2.6-canary.N after
stable v0.2.5, and never unpublish npm canary package versions as part of
this cleanup.
- Smoke test:
npx @rudderhq/cli@latest start --no-open
rudder start --no-open
The second command is only expected to work after the persistent CLI exists.
If the workflow fails after npm publish, do not rerun the whole stable workflow
without first classifying the partial state. Stable npm versions are immutable;
repair the missing downstream surfaces for the same version and tag.
Post-Stable Canary Cleanup
Use this after a stable release succeeds, or when the user asks why old canary
versions still appear in GitHub Releases.
- Read the current stable and active canary dist-tags:
npm view @rudderhq/cli dist-tags --json
Treat npm latest as the current stable and npm canary as the active
prerelease line. Do not delete the canary line that matches the active npm
canary version unless the user explicitly asks for a broader purge.
2. List GitHub Releases and remote canary tags:
gh release list --repo Undertone0809/rudder --limit 100
git ls-remote --tags origin 'refs/tags/canary/v*'
- Select obsolete canaries: releases/tags whose base version is less than or
equal to the current stable base. For example, when
latest=0.2.5 and
canary=0.2.6-canary.9, delete canary/v0.2.5-canary.* and older canary
releases/tags, but preserve canary/v0.2.6-canary.*.
- Delete GitHub Releases with tag cleanup:
gh release delete 'canary/v0.2.5-canary.N' \
--repo Undertone0809/rudder \
--cleanup-tag \
-y
- Run a separate orphan-tag pass because
gh release delete --cleanup-tag only
removes tags attached to Releases that still existed:
git ls-remote --tags origin 'refs/tags/canary/v*'
git push origin ':refs/tags/canary/v0.2.5-canary.N'
- Verify the cleanup did not move npm dist-tags and did not remove the active
canary line:
gh release list --repo Undertone0809/rudder --limit 100
git ls-remote --tags origin 'refs/tags/canary/v*'
npm view @rudderhq/cli dist-tags --json
Report the exact retained active canary, deleted release/tag ranges, and the
npm dist-tag state. If the user says "delete all previous canaries", interpret
that as all canary GitHub Releases/tags older than the active canary line, not
as npm unpublish authority.
Desktop Update Drill Before Stable Promotion
Use this gate when the stable is meant to prove installed Desktop behavior, not
just package availability. A candidate canary is ready for stable only after the
operator can update an already-installed app without a user-visible main-process
error or data/profile loss.
Minimum drill on the available local platform:
- Record the starting installed app path and version, for example
/Users/zeeland/Applications/Rudder.app plus the About/settings version.
- Install or launch an older release that should discover the candidate update.
- Use the app UI, preferably with Computer Use when available, to run
"Check for update" from About/settings.
- Download the update, click "Restart to update", and wait for the app to
exit, replace itself, and reopen.
- Verify the reopened app reports the candidate version and still points at
the expected data/profile/workspace.
- Inspect logs or UI for Electron main-process errors such as
EPIPE,
ERR_STREAM_DESTROYED, broken progress pipes, failed replacement, checksum
failures, or repeated relaunch loops.
- When practical, verify active-work safeguards: an active run should block,
defer, or clearly explain update timing instead of silently killing work.
Evidence to report:
- candidate canary tag/version and stable target version
- installed app path, old version, new version, and platform
- screenshots, logs, or Computer Use notes for check/download/restart/reopen
- whether data/profile/workspace persisted
- any skipped drill item and why it could not run locally
Do not substitute a --dry-run or asset-list check for this drill when the
known risk is the in-app update path itself.
Version Bump
Use this before the next stable line.
node scripts/release-package-map.mjs set-version X.Y.Z
pnpm -r typecheck
pnpm test:run
pnpm build
Then commit only the intended version and release-note changes.
Rollback
Rollback moves npm latest; it does not unpublish packages or rewrite tags.
./scripts/rollback-latest.sh X.Y.Z --dry-run
./scripts/rollback-latest.sh X.Y.Z
After rollback, fix forward with a new stable semver.
Partial Release Failures
- npm published but tag/GitHub Release failed: do not republish npm. Push or
recreate the missing tag/release for the same version.
- npm published and the stable tag exists, but GitHub Release creation failed:
do not republish npm and do not rerun the whole stable workflow. Inspect the
job log for the failing command. If the failure is a missing
GH_TOKEN or
gh authentication problem after vX.Y.Z was pushed, create or update the
GitHub Release manually from releases/vX.Y.Z.md, then trigger
desktop-release.yml for that exact stable tag:
gh release create vX.Y.Z \
--repo Undertone0809/rudder \
--title vX.Y.Z \
--notes-file releases/vX.Y.Z.md \
--target <locked-source-sha>
gh workflow run desktop-release.yml \
--repo Undertone0809/rudder \
--ref main \
-f release_tag=vX.Y.Z \
-f source_ref=vX.Y.Z
- GitHub Release exists but Desktop assets failed: rerun
desktop-release.yml
for the same vX.Y.Z or canary/vX.Y.Z-canary.N; do not republish npm.
- A later
main canary fails because an earlier concurrent canary already
published the same prerelease version. Treat it as a canary concurrency
incident, not as evidence that the locked stable source is bad. Verify npm
dist-tags, the stable tag, and the selected release source before deciding
whether stable should proceed.
GitHub Release ... was not found (403) from the CLI: do not stop at the CLI
error. Check whether GitHub API rate limits are exhausted and whether the
Release and direct assets are actually public:
curl -sS https://api.github.com/rate_limit
gh release view 'canary/v0.1.0-canary.N' \
--repo Undertone0809/rudder \
--json tagName,name,url,isPrerelease,isDraft,assets
If authenticated gh release view succeeds and direct asset download works,
treat the problem as a CLI download/lookup bug or API-rate-limit issue, not as
a missing Release.
- GitHub Release title is
canary/vX.Y.Z-canary.N or prerelease is false:
gh release edit 'canary/vX.Y.Z-canary.N' \
--repo Undertone0809/rudder \
--title 'vX.Y.Z-canary.N' \
--prerelease
- Desktop assets exist but checksum missing or stale: rerun
desktop-release.yml
and verify SHASUMS256.txt.
- A failed or skipped run may be harmless, but only after checking whether a
newer canary was already published. Check npm dist-tags, tags, and recent
Release workflow runs before declaring no release happened.
latest is broken: if a prior stable exists, rollback the dist-tag, then fix
forward. During the pre-stable canary bootstrap exception, repair latest to
the newest canary that already has complete Desktop assets by using
npm-dist-tag.yml, then verify all package dist-tags and run the full npx
Desktop smoke.
Useful rerun command:
gh workflow run desktop-release.yml \
--ref main \
-f release_tag='canary/v0.1.0-canary.1' \
-f source_ref=main
For Desktop releases, verify the Release object directly:
gh release view 'canary/v0.1.0-canary.1' \
--repo Undertone0809/rudder \
--json tagName,name,url,isPrerelease,isDraft,assets \
--jq '{tagName,name,url,isPrerelease,isDraft,assets:[.assets[].name]}'
When Desktop packaging fails:
- macOS x64 should use the current Intel runner from the workflow, not an
unavailable legacy runner label.
- canary macOS builds may be unsigned; verify
desktop/package.json and the
release policy before assuming signing is required.
- x64 DMG collection must look for the architecture-specific Electron Builder
output such as
release/mac-x64 as well as any generic release/mac path.
- Windows builds frequently expose script portability problems; prefer Node
scripts over shell-only assumptions in packaging steps.
Final Release Verification
Before claiming a release is done, verify every surface that applies to the
channel:
git status --short --branch
node scripts/release-package-map.mjs list
npm view @rudderhq/cli dist-tags --json
gh release view '<tag>' --json tagName,url,isPrerelease,isDraft,assets
gh release list --repo Undertone0809/rudder --limit 100
git ls-remote --tags origin 'refs/tags/canary/v*'
Also check whether any release.yml run is still in progress and could publish
a newer canary or move npm tags after your verification:
gh run list --workflow release.yml --limit 10
For stable releases, report in-progress unrelated canary or smoke workflows
separately from the fixed stable result. Do not keep retargeting or revalidating
against newer main commits after vX.Y.Z points at the locked source SHA.
Also report whether obsolete canary GitHub Releases/tags were cleaned up or why
they were intentionally preserved; include the retained active canary line and
confirm npm dist-tags were not changed by cleanup.
If a supplemental cross-platform public install smoke is still running after the
stable npm/tag/Release/Desktop surfaces and at least the available local full
install smoke are verified, state that residual status explicitly instead of
blocking forever or implying the stable tag moved.
For first-public canary bootstrap where latest intentionally equals canary,
run both smoke checks. The --dry-run form is a fast resolver check; the
non-dry-run latest form is the install proof:
tmp_home="$(mktemp -d /tmp/rudder-cli-smoke-canary-start.XXXXXX)"
tmp_cache="$(mktemp -d /tmp/rudder-npm-cache-canary-start.XXXXXX)"
HOME="$tmp_home" npm_config_cache="$tmp_cache" npm_config_yes=true \
npx --prefer-online --yes @rudderhq/cli@canary start --dry-run --no-open
tmp_home="$(mktemp -d /tmp/rudder-npx-home-latest.XXXXXX)"
tmp_cache="$(mktemp -d /tmp/rudder-npx-cache-latest.XXXXXX)"
tmp_prefix="$(mktemp -d /tmp/rudder-npx-prefix-latest.XXXXXX)"
tmp_output="$(mktemp -d /tmp/rudder-npx-output-latest.XXXXXX)"
tmp_install="$(mktemp -d /tmp/rudder-npx-install-latest.XXXXXX)"
HOME="$tmp_home" npm_config_cache="$tmp_cache" npm_config_prefix="$tmp_prefix" npm_config_yes=true PATH="$tmp_prefix/bin:$PATH" \
npx --prefer-online --yes @rudderhq/cli@latest start --no-open \
--output-dir "$tmp_output" \
--desktop-install-dir "$tmp_install"
The smoke should show the resolved Rudder release tag, target platform/arch, and
the persistent CLI version it installed. If it still resolves an old npm cache
entry, rerun with an isolated npm_config_cache and --prefer-online. If the
install succeeds but the app is suspected unusable, inspect installed portable
app symlinks and perform a controlled launch check from the temporary install
path.
Safety Rules
- Do not run a real stable publish without an explicit user request.
- Do not unpublish npm packages as a rollback strategy.
- Do not unpublish npm canary package versions as a post-stable cleanup
strategy. Cleanup means GitHub Releases and git
canary/* tags unless the
user gives separate, explicit npm unpublish authority and the npm policy
allows it.
- Do not delete the active next-line canary GitHub Release/tag during
post-stable cleanup unless the user explicitly asks to remove the current
canary too.
- Do not republish an npm version that already exists.
- Do not force-push release tags unless the user explicitly approves the exact
tag and reason.
- Do not treat a canary as a stable release.
- Do not claim a stable is complete until all release surfaces are verified.
- Do not claim a canary is complete until npm, tag, and Desktop assets are
verified when the Desktop workflow is configured for canary tags. Also verify
the GitHub Release title is clean and the Release is marked prerelease.
- Do not claim
npx @rudderhq/cli@latest start is fixed from a dry-run alone.
A user-facing Desktop install fix requires a real isolated install smoke, and
when practical a minimal app-start check.
- Do not ignore already-running
release.yml runs on main; they can publish a
newer canary while you are repairing docs or automation.
- Do not edit unrelated dirty files; stage/commit only release-maintainer scope
files for skill maintenance, or only release-scope files during release work.
- Do not print npm tokens in logs or final answers. If a token was pasted into
the conversation, finish by telling the user to revoke or rotate it.
- When using relative dates like "today", include the concrete date in the
final release plan or report.
Default Answer Shape
When the user asks "what do I do now?", answer in this order:
- Current State: branch, target version, package versions, known workflow/tag/npm state.
- Blockers: missing release notes, unmerged workflow, unconfigured npm trust,
failing checks, dirty release files, or missing Desktop artifacts.
- Next Actions: numbered, executable steps with exact commands or GitHub UI
actions.
- Human Gates: approvals, npm login/trusted-publisher setup, GitHub
environment approval, announcement copy.
- Verification: exact checks that prove the release surface is complete.
For hands-on release execution, keep short status updates while working, then
finish with:
- version/ref released or prepared
- what was verified
- what failed or remains manual
- exact links or commands for the next action
- GitHub Actions run IDs for the release and Desktop workflows when publishing
was involved
- GitHub Release URL/title, npm dist-tag state, and whether Desktop assets match
the expected set
- whether the local working tree was left clean, or which unrelated files were
already dirty and preserved
- a token rotation reminder if token-based publishing was used
Examples
Stable readiness check
User: 我要发 stable,现在要做什么?
Expected behavior:
- inspect local and remote state
- identify target version with
./scripts/release.sh stable --print-version
- require
releases/vX.Y.Z.md
- recommend GitHub Actions dry-run before real publish
- include Desktop and npm verification steps
Stable source lock with moving main
User: 发 0.1.0 stable
Observed state:
- dry-run passed for
main when it resolved to abc123
- another unrelated commit later landed on
main and started a canary run
Expected behavior:
- record
abc123 as the locked stable source unless the user explicitly
retargets
- run the real stable publish with
source_ref=abc123
- monitor later canary runs only for npm tag overwrite risk
- do not wait for the newer canary merely to adopt its commit into the stable
Must not:
- silently retarget stable to the newer
main
- keep delaying stable to chase unrelated canaries
- imply the stable tag moved after
vX.Y.Z points at the locked source
Desktop failure
User: npm latest 已经发了,但是 mac/windows/linux 包没挂到 release 上。
Expected behavior:
- treat as partial stable release
- do not republish npm
- rerun
desktop-release.yml for the existing stable tag
- verify Release assets and
SHASUMS256.txt
Stable GitHub Release creation failure after npm publish
Observed state:
- stable workflow published every
@rudderhq/*@0.1.0
- workflow pushed
v0.1.0
gh release create failed because GH_TOKEN was missing in the job
Expected behavior:
- classify this as partial release recovery
- verify npm
latest=0.1.0 across all public packages
- verify
v0.1.0 points at the locked source SHA
- manually create or update the GitHub Release from
releases/v0.1.0.md
- trigger
desktop-release.yml for release_tag=v0.1.0
Must not:
- rerun the full stable workflow and attempt to republish
0.1.0
- unpublish or rewrite the stable tag
- call the release done before Desktop assets and checksums are attached
Broken npx latest install
User: npx @rudderhq/cli@latest start 还是报 GitHub Release not found (403),你自己测一下。
Expected behavior:
- check npm
latest and canary dist-tags across all public packages
- check recent
release.yml, desktop-release.yml, and npm-dist-tag.yml runs
- verify the exact GitHub Release and assets with
gh release view
- treat GitHub API
403 as possible rate limiting until proven otherwise
- if
latest points to a canary without complete Desktop assets, repair it via
npm-dist-tag.yml only after choosing a canary whose assets and checksums are
complete
- run a real isolated
npx --prefer-online --yes @rudderhq/cli@latest start --no-open
install with temporary HOME/cache/prefix/output/install directories
- on macOS, verify quarantine cleanup and that portable app symlinks resolve
- report the exact resolved version, Release tag, workflow run IDs, smoke exit
code, and any in-progress release runs that could still affect
latest
Old canary cleanup after stable
User: 发正式版的时候把之前的 Canary 版本都删掉,这个事情现在没有做到吗?为什么我能看到很多 0.2.5 版本之前的 Canary version,帮我把之前的都删了
Observed state:
- npm
latest=0.2.5
- npm
canary=0.2.6-canary.9
- GitHub Releases still include
canary/v0.2.5-canary.0..14
Expected behavior:
- explain that prior canary cleanup was not a stable-release completion gate if
old GitHub Releases/tags remain visible after the stable
- delete obsolete GitHub Releases with
gh release delete --cleanup-tag
- run a separate remote tag pass for orphaned
refs/tags/canary/*
- preserve the active next-line canary, here
canary/v0.2.6-canary.*
- verify
npm view @rudderhq/cli dist-tags --json still reports
latest=0.2.5 and canary=0.2.6-canary.9
Must not:
- unpublish npm
0.2.5-canary.* package versions
- delete the active
0.2.6 canary line without explicit user authority
- claim cleanup is complete without checking both GitHub Releases and remote
tags
Entrypoint confusion
User: npx @rudderhq/cli@latest start 和 rudder start 是什么关系?
Expected behavior:
- explain they are the same CLI surface when versions match
- explain
npx is first-run/dist-tag resolution and rudder is persistent
direct execution
- remind that Desktop binaries still come from GitHub Releases
First canary bootstrap
User: 之前没发过这些包,这是第一次发包。我要 0.1.0 canary,并且 npx @rudderhq/cli start 要能直接跑。
Expected behavior:
- use
@rudderhq/* package names from scripts/release-package-map.mjs
- detect that trusted publishing cannot exist until package names exist
- if the user provides/authorizes an npm token, use a temporary npmrc and remove
it after publishing
- publish
0.1.0-canary.1 once, under canary, without retrying already
accepted packages
- because this is first-public bootstrap and the user wants bare
npx, move
latest to the same canary across all packages and explicitly label this as
an exception
- verify all package dist-tags, the canary GitHub Release Desktop assets, and
both
npx @rudderhq/cli@canary start --dry-run --no-open and a real isolated
npx @rudderhq/cli@latest start --no-open Desktop install smoke