* refactor(cli): improve publish-cli script reliability - Move version computation and pre-flight checks before build-and-test to fail fast on conflicts (existing branch/tag) instead of wasting minutes on lint/test/build - Add INT/TERM signal handlers to cleanup trap so Ctrl+C during build properly restores working tree state - Update Makefile help text to reflect PR-based workflow * fix(cli): use git checkout -f for robust cleanup Address code review feedback from gemini-code-assist bot: - Use `git checkout -f` in on-release and committed cleanup stages to ensure reliable branch switching even when files are staged but not committed (e.g., interrupted after `git add` but before `git commit`) - Remove redundant `git checkout -- <file>` in on-release stage since `-f` already discards all local changes This prevents cleanup failures when the script is interrupted between staging and committing. * fix(cli): address PR #441 review findings - Fix ERR trap bypass: remove `if !` wrapper around `gh pr create` so set -e triggers the trap and prints pushed-stage recovery instructions - Fix command injection: all node -e/-p calls now use process.env instead of interpolating shell variables into JS string literals - Rewrite cli/RELEASE.md to document the new PR-based release flow - Rewrite scripts/tests/publish-cli-test.sh with 10 tests covering the new flow (stubs for bun/gh, pre-flight checks, happy path, cleanup state machine stages) * fix(cli): address PR #441 review findings from @dongmucat - Bind release tag to origin/main: PR body, end-of-run hint, and cli/RELEASE.md now use `git tag $TAG origin/main` so the tag is always placed on the merged commit, regardless of local branch state - Reject prerelease tags in version computation: if the latest cli-v* tag contains non-X.Y.Z characters (e.g., -rc.1), exit with a clear message instead of crashing in node parsing - Add pr-scripts.yml workflow: runs publish-cli-test.sh on scripts/** changes so the release script regression suite gates PRs - Add Test 11 covering prerelease tag rejection * fix(cli): compute publish baseline from origin tags only A failed `git push origin cli-vX.Y.Z` after a successful local tag leaves an orphan tag locally. The previous `git tag --list` baseline would then treat it as the latest release, causing skipped versions or publishes based on an unreleased tag. Switch to `git ls-remote --tags --refs origin 'cli-v*' | sort -V` so the baseline reflects only what is actually on origin. Local orphan tags can still collide with the computed target tag, which fails fast with a clear message as before. Adds test 12 covering the orphan-tag scenario.
6.2 KiB
CLI Release Guide
Overview
CLI releases use a PR-based flow. Running make publish-cli on a clean main branch:
- Runs local build-and-test (lint, typecheck, test, build)
- Computes the next version from the latest
cli-v*tag onorigin - Creates a
release/cli-vX.Y.Zbranch with the version bump committed - Pushes the branch and opens a PR to
main
After the PR is merged, you manually tag and push — the tag triggers release-cli.yml which builds, publishes to npm, and creates a GitHub Release.
Prerequisites
Repository Secrets
Configure in GitHub repository → Settings → Secrets and variables → Actions:
NPM_TOKEN: npm token with publish permissions- Generate at https://www.npmjs.com/settings/YOUR_USERNAME/tokens
- Use Classic Automation Token (bypasses 2FA automatically), or
- Granular Access Token with "Allow bypass 2FA" enabled, scoped to the package
Repository Variables (optional)
NPM_REGISTRY: npm registry URL (default:https://registry.npmjs.org)
Local Environment
nodeandbuninstalledghCLI installed and authenticated (gh auth login)gitwith push access to the repository- On the
mainbranch with a clean working tree
Package Configuration
In cli/package.json:
{
"name": "@astron-team/skillhub",
"publishConfig": {
"access": "public"
}
}
Release Process
Step 1: Run the publish script
From the repository root, on a clean main branch:
make publish-cli # patch: 0.1.5 -> 0.1.6
make publish-cli-minor # minor: 0.1.5 -> 0.2.0
make publish-cli-major # major: 0.1.5 -> 1.0.0
scripts/publish-cli.sh performs the following steps:
- Verify
ghCLI is installed and authenticated - Verify the working tree is clean and on
main git pull --ff-onlyfromorigin/main- Run full local build-and-test (lint, typecheck, test, build)
- Compute the next version from the latest
cli-v*tag onorigin(viagit ls-remote, so local orphan tags from a failedgit push origin cli-vX.Y.Zare ignored) - Verify the tag and release branch don't already exist
- After interactive confirmation: create release branch, commit version bump, push, and open PR
Step 2: Merge the PR
Review and merge the PR on GitHub as usual.
Step 3: Tag and push
After the PR is merged:
git fetch origin main
git tag cli-vX.Y.Z origin/main # replace with the actual version
git push origin cli-vX.Y.Z
This ensures the tag is always placed on the merge commit on origin/main, regardless of your local branch state.
Pushing the tag triggers CI which builds, publishes to npm, and creates a GitHub Release.
CI Workflow
release-cli.yml contains three jobs:
-
build-and-test
- Extract version from tag name (
cli-v0.1.6→0.1.6) and write it intocli/package.json - Install deps, lint, typecheck, test, build
- Verify the built CLI's runtime version matches the tag
- Extract version from tag name (
-
publish-npm
- Skip if the target version already exists on the registry
- Configure
~/.npmrcand runnpm publish --access public
-
create-release
- Package
dist/+ README + LICENSE astar.gzandzip - Generate SHA256 checksums
- Create a GitHub Release and upload artifacts
- Package
Verify Release
- Workflow: https://github.com/iflytek/skillhub/actions/workflows/release-cli.yml
- Release: https://github.com/iflytek/skillhub/releases
- npm:
npm view @astron-team/skillhub@<version>
Release Audit Trail
GitHub Actions automatically records on each workflow run page:
- Triggering user (the developer who pushed the tag, i.e.
github.actor) - Trigger event (
pushtag orworkflow_dispatch) - Tag name and commit SHA
The team can review the full audit trail in the Actions tab without any extra configuration.
Manual Trigger
From the Actions UI:
- Actions → Release CLI → "Run workflow"
- Enter an existing tag name matching
cli-vX.Y.Z - Optionally enable skip npm publish
Error Recovery
The script uses a cleanup state machine. If it fails at different stages:
- Before push: release branch is deleted locally, you're returned to
main - After push, before PR: the script prints recovery instructions (open PR manually or delete the remote branch)
- After PR opened: success — no cleanup needed
If you need to manually clean up a failed release:
git checkout main
git branch -D release/cli-vX.Y.Z # delete local branch
git push origin --delete release/cli-vX.Y.Z # delete remote branch (if pushed)
Troubleshooting
releases must be cut from 'main'
Switch back to main, pull the latest, and retry.
git working tree is not clean
Commit or stash local changes first.
tag cli-vX.Y.Z already exists
The previous release didn't clean up, or someone else released the same version. Check git tag --list 'cli-v*' and remote tags, then retry with a higher version.
branch release/cli-vX.Y.Z already exists
A previous release attempt left a stale branch. Delete it locally and/or on origin, then retry.
npm Publish Fails
- 403 with 2FA message:
NPM_TOKENis not an Automation Token, or bypass 2FA is not enabled — regenerate with the correct type - 403 Forbidden: Package scope doesn't match token permissions — confirm publish rights for the
@astron-teamorg - E404: The registry doesn't host this scope — check
NPM_REGISTRY
Build / Test Fails
Reproduce locally:
make lint-cli && make typecheck-cli && make test-cli && make build-cli
Confirm the Bun version matches packageManager in cli/package.json.
Version Mismatch (runtime ≠ tag)
CI runs node dist/index.js version and requires the output to match the tag. If the CLI's version command implementation changes, update the verification logic in release-cli.yml accordingly.
Tag Naming Convention
- CLI releases:
cli-v*(e.g.,cli-v0.1.6) - Repository releases:
v*(e.g.,v0.3.0)
The two tag namespaces are independent, allowing CLI and server to version separately.