Releasing coderef
Three independent distribution channels, each tied to a credentialed
external service. The release workflow plumbs them but the actual
triggers stay manual on purpose — every channel is publicly visible
and the consequences of a bad publish are slow to reverse.
Always do the channels in this order: GitHub Release → npm wrapper
→ VSCode marketplace. The npm wrapper downloads from the GitHub
Release at install time; the marketplace extension bundles the WASM
locally so it doesn't depend on the others, but a misordered publish
makes the npm wrapper unusable until the GitHub Release exists.
0. Pre-flight (every release)
git checkout main && git pull --ff-only
git status # must be clean
cargo test --all-features --locked # all unit + integration
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --all -- --check
./target/release/coderef doctor . # 0 errors
./target/release/coderef check . # 0 brokenMake sure every package agrees on the version:
grep -rn '"version": "[0-9]' --include='package.json' .
grep -n 'version = "[0-9]' Cargo.toml
grep -n 'html_root_url' crates/coderef-core/src/lib.rsAll five should show the same MAJOR.MINOR.PATCH (no trailing -rc
unless this is a pre-release).
1. GitHub Release (CLI binaries)
Triggers the cross-build in .github/workflows/release.yml. Produces
the four platform tarballs the npm wrapper downloads at install time.
git tag v0.5.0 -m "coderef v0.5.0"
git push origin v0.5.0Then watch gh run watch --branch v0.5.0 until the release job
shows green. Verify:
gh release view v0.5.0 --json assets --jq '.assets[].name'Expect 8 entries (4 platforms × 2 files: archive + .sha256).
2. npm wrapper
Automated by .github/workflows/npm_publish.yml. The workflow fires
on the same tag push that drives step 1 and waits (polls up to 30
minutes) for release.yml to create the GitHub Release with assets
before calling npm publish. There's no window where the wrapper
exists on npm but the binaries it tries to download don't.
(We originally wired this on release: types: [published], but
that event is suppressed by GitHub Actions when the originating
gh release create ran with the workflow's built-in GITHUB_TOKEN
— an anti-recursion safeguard. The tag-push trigger + wait loop
avoids that gotcha without needing a separate PAT.)
One-time setup
- npm account + scope: sign up at npmjs.com with username
helly25(auto-creates the@helly25scope under which
@helly25/codereflives). - 2FA: enable on the publisher account with mode
Auth Only— the CI token then bypasses interactive OTP
for automation. (Auth-and-Writes mode forces OTP prompts and
won't work in CI.) - Mint the token: Profile → Access Tokens → Generate New
Token → Granular Access Token. Permission: Read and
write. Packages and scopes:@helly25. Expiration: your
rotation horizon (365d is a reasonable default). Copy the
token immediately — npm shows it once. - Store as repo secret: GitHub → repo → Settings → Secrets
and variables → Actions → New repository secret. Name
NPM_TOKEN, value the token.
Per-release flow
The workflow runs automatically on the GitHub Release-published
event from step 1 — no extra action needed for normal releases.
Watch progress at
https://github.com/helly25/coderef/actions/workflows/npm_publish.yml.
For ad-hoc / retro-publish (e.g. a tag pushed before this workflow
existed, or a re-publish after a transient registry failure):
gh workflow run npm_publish.yml -f tag=v<X.Y.Z>
gh run watch # follow the runVerify on npm:
npm view @helly25/coderef versionManual fallback
If the workflow is broken and you need to publish from a local
machine, the NPM_TOKEN is in keychain on the publisher's box;
retrieve and use it for a one-off:
export NODE_AUTH_TOKEN=$(security find-generic-password -a "$USER" -s NPM_TOKEN -w)
cd "$REPO_ROOT/npm/coderef"
echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > ~/.npmrc
npm publish --access public
rm ~/.npmrc # don't leave it lying aroundTest the install against the local checkout first if you want a
dry run:
cd /tmp && rm -rf coderef-npm-test && mkdir coderef-npm-test
cd coderef-npm-test && npm init -y >/dev/null
npm install --no-audit --no-fund "$REPO_ROOT/npm/coderef"
./node_modules/.bin/coderef --version # must print engine version3. VSCode marketplace (extension VSIX)
Automated by .github/workflows/vscode_marketplace.yml. The workflow fires
on the same tag push that drives step 1, builds the VSIX in CI (Rust
- wasm32 + wasm-pack + Node), uploads it as a downloadable artifact,
and callsvsce publishusing theVSCE_PATrepo secret. This
channel is independent of step 1 — the VSIX bundles its own WASM
viascripts/bundle-wasm.cjs.
One-time setup
- Mint the PAT: https://dev.azure.com → User Settings → Personal
Access Tokens → New Token. Organization: All accessible
organizations. Scopes: Custom defined → Marketplace → Manage
(nothing else — least privilege). - Store as repo secret: GitHub → repo → Settings → Secrets and
variables → Actions → New repository secret. NameVSCE_PAT,
value is the token. Once stored, GitHub redacts it from all logs.
Per-release flow
The workflow runs automatically on the tag push from step 1 — no
extra action needed for normal releases. Watch progress at
https://github.com/helly25/coderef/actions/workflows/vscode_marketplace.yml.
For ad-hoc / retro-publish (e.g. a tag pushed before this workflow
existed, or a re-publish after a marketplace-side issue), trigger
via workflow_dispatch:
gh workflow run vscode_marketplace.yml -f tag=v<X.Y.Z>
gh run watch # follow the runVerify on the marketplace:
npx vsce show helly25.coderef --json | jq '.versions[0]'Manual fallback
If the workflow is broken and you need to publish from a local
machine:
export VSCE_PAT=$(security find-generic-password -a "$USER" -s VSCE_PAT -w)
cd "$REPO_ROOT/extension"
npm install --no-audit --no-fund
npm run package # produces coderef-<ver>.vsix
ls -la coderef-*.vsix
npx vsce publish --packagePath coderef-<ver>.vsixNote: npm run package calls scripts/bundle-wasm.cjs, which
requires rustup-managed rustc with wasm32-unknown-unknown on
$PATH. If which rustc resolves to a brew-managed binary, the
build will fail; either brew unlink rust or prepend the rustup
toolchain bin:
export PATH="$HOME/.rustup/toolchains/$(rustup show active-toolchain | awk '{print $1}')/bin:$PATH"Tag → publish window: minutes, not hours
The npm wrapper's install fetches https://github.com/helly25/coderef/releases/download/v<X>/....
If npm publishes before the GitHub Release is fully populated,
npm install -g @helly25/coderef@<X> fails with a 404 download error
until the assets land. Don't let users hit that window — finish all
three channels in one sitting, or hold off on the npm publish until
step 1's release page is fully populated.
Rollback
- GitHub Release:
gh release delete v<X>+git tag -d v<X> && git push --delete origin v<X>. - npm: a published version is permanent (npm rejects republishing
the same number). Usenpm deprecate '@helly25/coderef@<X>' "reason"
and publish a patch with the fix. - VSCode marketplace:
vsce unpublish helly25.coderef@<X>works
but is visible in the extension's history.
Pre-release / rc
For a release candidate, use the SemVer pre-release form: tag as
v0.2.0-rc1, bump all five version fields to 0.2.0-rc.1 (note
the . before the number for npm/SemVer; Cargo accepts both). Test
publish to a private npm scope or skip npm entirely for rcs.