| name | complete-release |
| description | Finalize a mainline solana-keychain release after the PR is approved: approve, squash-merge, and trigger the publish workflows for whichever languages changed from main via gh CLI. Use when asked to "finalize release", "merge release PR", "complete release", "publish packages", or "approve and merge release".
|
Complete Release Skill
This is the reviewer half of the mainline release flow. Run this after a release PR to main has been reviewed and is ready to merge.
Notes:
- This skill is mainline-only: it dispatches publish workflows from
main.
- Hotfix releases are published from
hotfix/* before merge-back and are handled in .claude/skills/release/SKILL.md.
- Publish workflows allow both
main and hotfix/* refs.
Prerequisites
gh CLI installed and authenticated (gh auth status)
- You are a repo collaborator with write access
Step 1: Identify the release PR
If a PR number is not provided, find it:
gh pr list --base main --state open | grep chore/release
Note the PR number (e.g. 42).
Step 2: Approve the PR
gh pr review <PR_NUMBER> --approve --body "LGTM"
Step 3: Confirm CI is green, then squash merge
Check that all required status checks have passed before merging:
gh pr checks <PR_NUMBER>
All checks should show pass. Once green:
gh pr merge <PR_NUMBER> --squash --delete-branch
Wait for merge to complete before proceeding.
Step 4: Detect what changed and trigger publish workflows from main
Check which paths the merged PR touched, then trigger only the relevant workflow(s). The four languages are independent, each with its own tag namespace and workflow.
Each language publishes through its own workflow. Trigger only the ones whose tree the release touched.
FILES=$(gh pr view <PR_NUMBER> --json files --jq '.files[].path')
for lang in rust typescript python go; do
echo "$lang changed: $(echo "$FILES" | grep -q "^${lang}/" && echo yes || echo no)"
done
Trigger one workflow per language that changed:
gh workflow run "Publish Rust Crate" \
--repo solana-foundation/solana-keychain \
--ref main \
-f publish-to-crates=true \
-f create-github-release=true
gh workflow run "Publish TypeScript Packages (Manual)" \
--repo solana-foundation/solana-keychain \
--ref main \
-f package=all \
-f publish-to-npm=true \
-f create-github-release=true
gh workflow run "Publish Python Package (Manual)" \
--repo solana-foundation/solana-keychain \
--ref main \
-f publish-to-pypi=true \
-f create-github-release=true
gh workflow run "Publish Go Modules (Manual)" \
--repo solana-foundation/solana-keychain \
--ref main \
-f version=X.Y.Z \
-f create-github-release=true
The Go workflow takes the version as an input, without the leading v, because Go has no version file: the tags are the version. It refuses to tag unless every internal go.mod require already names that version.
Step 5: Verify workflows started
Only check workflows that were triggered:
gh run list --workflow="Publish Rust Crate" --limit 1
gh run list --workflow="Publish TypeScript Packages (Manual)" --limit 1
gh run list --workflow="Publish Python Package (Manual)" --limit 1
gh run list --workflow="Publish Go Modules (Manual)" --limit 1
Each triggered workflow should show queued or in_progress.
Verification
Once workflows complete, for each language published:
| Language | Tag | Registry |
|---|
| Rust | vX.Y.Z | crates.io shows the new solana-keychain version |
| TypeScript | ts-keychain-vA.B.C | npm shows the new @solana/keychain version; a pre-release must be on the beta dist-tag, not latest |
| Python | python-vX.Y.Z | PyPI shows the new solana-keychain version |
| Go | go/<module>/vX.Y.Z, one per module | go list -m github.com/solana-foundation/solana-keychain/go/core/v2@vX.Y.Z resolves |
Each GitHub release body is generated by its workflow from the previous tag in the same language namespace, so check the notes landed and are not empty.