Routa Release Guide
This guide covers the process for releasing new Routa artifacts to multiple distribution channels: crates.io (Cargo), npm, and GitHub Releases.
Overview
The release process publishes to three channels simultaneously:
- crates.io - Rust users can
cargo install routa-cli,cargo install harness-monitor, andcargo install entrix - npm - Node.js users can
npm install -g routa-cli,npm install -g harness-monitor, andnpm install -g entrix - GitHub Releases - Desktop binaries and release notes
Prerequisites
Repository Secrets
Ensure these GitHub secrets are configured:
CRATE_TOKEN- Get from crates.io/me → API Tokens (Note: The workflow usesCRATE_TOKEN, notCARGO_REGISTRY_TOKEN)NPM_TOKEN- Get from npmjs.com → Access Tokens → Generate New Token → AutomationROUTA_GITHUB_TOKEN- Preferred token for release baseline fetches against the GitHub Actions APIGITHUB_TOKEN- Automatically provided by GitHub Actions and used as fallback whenROUTA_GITHUB_TOKENis not configured
Local Setup
# Ensure you're on main branch with latest code
git checkout main
git pull origin main
# Verify no uncommitted changes
git status
Release Methods
Method 1: Automated Script (Recommended)
Use the release helper script:
# Interactive mode - prompts for version
./scripts/release/publish.sh
# Direct mode - specify version
./scripts/release/publish.sh 0.2.5
# Dry run - test without publishing
./scripts/release/publish.sh 0.2.5 --dry-run
The script will:
- Sync version across all packages
- Generate a release notes preview under
dist/release/release-notes.md - Show you the changes
- Create commit and tag
- Push to trigger GitHub Actions
Method 1.5: Prepare Release Artifacts And Blog Draft
Use the preparation helper when you want the release notes/blog files written into docs/releases/ before deciding whether to publish:
# Prepare release notes, changelog, and docs/releases blog draft
npm run release:prepare -- 0.2.5
# Use a specific range and AI summary generation
npm run release:prepare -- 0.2.5 --from v0.2.4 --ai --ai-provider claude
This helper:
- Syncs version fields across the repo
- Generates preview files under
dist/release/ - Copies the generated release notes to
docs/releases/v<version>-release-notes.md - Copies the technical changelog to
docs/releases/v<version>-changelog.md - Leaves commit, tag, and push decisions to a later explicit step
If you are using the repo skill system, the same workflow is exposed through .agents/skills/release/.
Generate Release Notes
Tauri draft releases use commit-derived release notes. Generate the same markdown locally before publishing:
npm run release:changelog -- \
--from v0.2.5 \
--to v0.2.6 \
--out dist/release/release-notes.md \
--changelog-out dist/release/CHANGELOG.generated.md
For the hybrid workflow, generate the AI prompt package, ask the bundled specialist for a curated summaryMarkdown, then re-run the changelog with that curated summary:
# deterministic technical changelog + prompt package
npm run release:changelog -- \
--from v0.2.5 \
--to v0.2.6 \
--prompt-out dist/release/changelog-summary-prompt.json \
--changelog-out dist/release/CHANGELOG.generated.md \
--out dist/release/release-notes.md
# optional one-step specialist run; requires a configured ACP provider
npm run release:changelog -- \
--from v0.2.5 \
--to v0.2.6 \
--ai \
--ai-provider claude \
--out dist/release/release-notes.md \
--changelog-out dist/release/CHANGELOG.generated.md
The generated release notes contain a user-facing Summary, a technical changelog, commit links, install instructions, and range metadata. --changelog-out writes the same tag range as a standalone # Changelog entry. If you want manual curation without running a specialist, write the curated summary in Markdown and pass it with --summary-file.
Method 2: Manual Process
# 1. Update version in all packages
node scripts/release/sync-release-version.mjs --version 0.2.5
# 2. Review changes
git diff
# 3. Commit and tag
git commit -am "chore: release v0.2.5"
git tag v0.2.5
# 4. Push
git push origin main --tags
Method 3: GitHub UI Dispatch
Manually trigger from GitHub:
- Go to Actions
- Click "Run workflow"
- Enter version (e.g.,
0.2.5orv0.2.5) - Configure publish options:
publish_cargo: Publish to crates.iopublish_cli: Publish npm packagespublish_desktop: Create GitHub Release with desktop binariesdry_run: Test without publishing
Release Workflow
Once you push a tag (e.g., v0.2.5), GitHub Actions automatically:
1. Cargo Publish (.github/workflows/cargo-release.yml)
Publishes these crates in order:
routa-core- Core domain logicrouta-rpc- RPC layerrouta-scanner- Repository scannerrouta-server- HTTP serverentrix- Entrix fitness engine shared by Harness Monitorrouta-cli- CLI binaryharness-monitor- terminal watch and attribution tool
Note: Each crate waits for the previous one to be indexed on crates.io before publishing.
2. CLI Release (.github/workflows/cli-release.yml)
Builds platform-specific binaries:
linux-x64- Linux x86_64darwin-x64- macOS Inteldarwin-arm64- macOS Apple Siliconwin32-x64- Windows x64
Then publishes to npm as:
routa-cli- Main package with platform detectionrouta-cli-linux-x64- Linux binaryrouta-cli-darwin-x64- macOS Intel binaryrouta-cli-darwin-arm64- macOS ARM binaryrouta-cli-windows-x64- Windows binary
3. Desktop Release (.github/workflows/tauri-release.yml)
Creates GitHub Release with:
- Tauri desktop app installers for macOS, Linux, and Windows
- Auto-generated release notes from
scripts/release/generate-changelog.mjs - CLI install instructions
- Automatic code signing for macOS (if configured)
Platform Matrix:
macos-latest- Builds.dmgand.appfor both Intel and Apple Siliconubuntu-22.04- Builds.deband.AppImagefor Linuxwindows-latest- Builds.msiand.exefor Windows
Important: Desktop release requires all version fields to be synchronized:
apps/desktop/package.jsonapps/desktop/src-tauri/Cargo.tomlapps/desktop/src-tauri/tauri.conf.json
If Tauri fails with version must be a semver string, check that all three files have matching versions.
Verification
After the release completes (~15-30 minutes), verify:
Crates.io
cargo search routa-cli
cargo install routa-cli@0.2.5
routa --version
cargo search harness-monitor
cargo install harness-monitor@0.2.5
harness-monitor --version
NPM
npm view routa-cli versions
npm install -g routa-cli@0.2.5
routa --version
GitHub Release
Check Releases page for the new version.
Troubleshooting
Version Already Published on crates.io
If a crate version already exists on crates.io, the workflow will skip it and continue. This is normal for patch re-releases.
NPM Publish Fails
Check that NPM_TOKEN is valid:
- Token must have "Automation" access
- Token must not be expired
- You must be a maintainer of the
routa-clinpm organization
Known Issue: The main routa-cli package may not be published even when platform packages succeed. If this happens:
- Manually update
packages/routa-cli/package.jsonoptionalDependencies to the new version - Publish manually:
cd packages/routa-cli
npm publish --access public
Root Cause: The stage-routa-cli-npm.mjs script was missing main package staging logic (fixed in v0.2.9+).
Cargo Publish Fails
Common issues:
- Missing dependency version: Ensure all workspace crates use the same version
- API token expired: Regenerate token at crates.io/settings/tokens
- Network timeout: Re-run the workflow
- Wrong secret name: The workflow expects
CRATE_TOKEN, notCARGO_REGISTRY_TOKEN
Known Issue: Cargo crates may not be published automatically. If this happens:
-
Manually update all release crate versions:
for crate in crates/routa-core crates/routa-rpc crates/routa-scanner crates/routa-server crates/entrix crates/routa-cli crates/harness-monitor; do
sed -i '' 's/version = "OLD_VERSION"/version = "NEW_VERSION"/g' "$crate/Cargo.toml"
done -
Publish in dependency order:
cargo login YOUR_CRATE_TOKEN
cd crates/routa-core && cargo publish --no-verify
cd ../routa-rpc && cargo publish --no-verify
cd ../routa-scanner && cargo publish --no-verify
cd ../routa-server && cargo publish --no-verify
cd ../entrix && cargo publish --no-verify
cd ../routa-cli && cargo publish --no-verify
cd ../harness-monitor && cargo publish --no-verify
Root Cause: The sync-release-version.mjs script doesn't sync Rust crate versions (only Desktop Tauri and npm packages).
Version Bump Types
Follow Semantic Versioning:
- Patch (0.2.4 → 0.2.5): Bug fixes, no breaking changes
- Minor (0.2.5 → 0.3.0): New features, backward compatible
- Major (0.3.0 → 1.0.0): Breaking changes
Rollback
If you need to rollback a release:
# Delete the tag locally and remotely
git tag -d v0.2.5
git push origin :refs/tags/v0.2.5
Note: You cannot unpublish from crates.io, but you can yank a version:
cargo yank routa-cli@0.2.5
Known Issues & Gotchas
1. Main Release Workflow Doesn't Trigger Cargo Publish
Issue: .github/workflows/release.yml doesn't call cargo-release.yml, so Rust crates won't be published automatically.
Workaround: Manually trigger the Cargo release workflow or publish locally as described in "Troubleshooting > Cargo Publish Fails".
Permanent Fix: Add Cargo release job to main release workflow.
2. Version Sync Script Incomplete
Issue: scripts/release/sync-release-version.mjs only syncs:
- Desktop:
apps/desktop/package.json,apps/desktop/src-tauri/Cargo.toml,apps/desktop/src-tauri/tauri.conf.json - CLI npm:
packages/routa-cli/package.json
It does not sync:
- Rust crates:
crates/*/Cargo.toml - CLI npm optionalDependencies versions
Workaround: Manually update Rust crate versions and npm optionalDependencies as described in "Troubleshooting".
Permanent Fix: Extend sync-release-version.mjs to handle all version fields.
3. CLI Artifact Naming Convention
Issue: Build jobs must use consistent artifact names (without version strings) so the staging job can find them.
Fixed in: v0.2.9 - artifact names standardized to routa-cli-{platform} format.
4. Windows PowerShell vs Bash
Issue: Windows runners default to PowerShell, which doesn't handle ${RELEASE_VERSION} environment variables the same way as bash, causing version parsing errors.
Fixed in: v0.2.9 - all version sync steps now explicitly use shell: bash.
5. macOS Bash 3 Compatibility
Issue: macOS runners use Bash 3.x which doesn't support mapfile command.
Fixed in: v0.2.9 - replaced mapfile with while read loops.