- 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:
```bash
gh pr list --base main --state open | grep chore/release
```
Note the PR number (e.g. `42`).
## Step 2: Approve the PR
```bash
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:
```bash
gh pr checks <PR_NUMBER>
```
All checks should show `pass`. Once green:
```bash
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.
```bash
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:
```bash
# rust/ (Cargo.toml, Cargo.lock, ...)
gh workflow run "Publish Rust Crate" \
--repo solana-foundation/solana-keychain \
--ref main \
-f publish-to-crates=true \
-f create-github-release=true
# typescript/ (packages/*/package.json, ...)
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
# python/ (pyproject.toml, ...)
gh workflow run "Publish Python Package (Manual)" \
--repo solana-foundation/solana-keychain \
--ref main \
-f publish-to-pypi=true \
-f create-github-release=true
# go/ (go.mod requires pinned to the release version)
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:
```bash
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.
عرض على GitHub