CLI Release Guide
This skill walks through the OpenHands CLI release process step by step.
🚨 CRITICAL: NEVER merge the release PR, create/push tags, or publish a GitHub release without the human's explicit approval. Release is the last line of human defense. Always present the current status and ask for confirmation before performing any irreversible action.
⚠️ Tag format: This project uses tags without a
vprefix (e.g.,1.13.0, notv1.13.0).
Phase 1: Trigger the Version Bump Workflow
Determine the target version (SemVer X.Y.Z). Then trigger the
bump-version.yml workflow, which creates a branch and draft PR
automatically.
Via GitHub UI
Navigate to
https://github.com/OpenHands/OpenHands-CLI/actions/workflows/bump-version.yml,
click Run workflow, enter the version (e.g., 1.14.0), and run it.
Via GitHub API
curl -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/OpenHands/OpenHands-CLI/actions/workflows/bump-version.yml/dispatches" \
-d '{
"ref": "main",
"inputs": {
"version": "<version>"
}
}'
The workflow will:
- Validate version format (
X.Y.Z) - Create branch
bump-version-<version> - Update version in
pyproject.toml - Regenerate
uv.lock - Update snapshot tests (version appears in splash screen SVGs)
- Open a draft PR titled "Bump version to <version>"
Once the workflow completes, retrieve the PR number for later phases:
gh pr list --repo OpenHands/OpenHands-CLI \
--head "bump-version-<version>" --json number,title,url
Phase 2: Wait for CI — Tests Must Pass
The bump PR triggers the standard CI suite. All checks must pass.
Monitor status:
gh pr checks <PR_NUMBER> --repo OpenHands/OpenHands-CLI
Key checks to verify:
- Lint
- Unit / integration tests
- Snapshot tests (updated by the bump workflow)
- Binary build
⏸ Checkpoint — Human Judgment on Failures
Decide with the team whether each failure is:
- Blocking — must fix before release
- Known / pre-existing — acceptable to release with a follow-up issue
- Flaky — re-run the workflow
Re-run failed jobs:
gh run list --repo OpenHands/OpenHands-CLI \
--branch "bump-version-<version>" --limit 5
gh run rerun <RUN_ID> --repo OpenHands/OpenHands-CLI --failed
Phase 3: Tag the Release
🚨 STOP — Do NOT tag without explicit human approval. Present the CI status summary and ask the human to confirm.
Tag the latest commit on the version bump branch. No v prefix.
git fetch origin
git checkout bump-version-<version>
git reset --hard origin/bump-version-<version> # ensure we're at latest
# verify tag doesn't already exist
if git rev-parse <version> >/dev/null 2>&1; then
echo "Error: Tag <version> already exists"
exit 1
fi
git tag <version>
git push origin --tags
Pushing the tag triggers two automated workflows:
cli-build-binary-and-optionally-release.yml— builds platform binaries and creates a draft GitHub release with assetspypi-release.yml— builds and publishes theopenhandspackage to PyPI
Phase 4: Merge the PR
🚨 STOP — Do NOT merge without explicit human approval.
Wait for CI to complete after tagging. Verify all checks passed, then merge:
gh pr checks <PR_NUMBER> --repo OpenHands/OpenHands-CLI
gh pr merge <PR_NUMBER> --repo OpenHands/OpenHands-CLI --merge
Phase 5: Edit and Publish the GitHub Release
🚨 STOP — Do NOT publish without explicit human approval. Publishing finalises the release for users.
Navigate to https://github.com/OpenHands/OpenHands-CLI/releases and edit the draft release created by CI:
- Click Auto-generate release notes (or write custom notes)
- Review the notes
- Click Publish release
⏸ Checkpoint — Verify Release Assets
After publishing, confirm all platform binaries were uploaded:
| Platform | Asset name |
|---|---|
| Linux x86_64 | openhands-linux-x86_64 |
| Linux ARM64 | openhands-linux-arm64 |
| macOS Apple Silicon | openhands-macos-arm64 |
| macOS Intel | openhands-macos-intel |
gh release view <version> --repo OpenHands/OpenHands-CLI --json assets \
--jq '.assets[].name'
⏸ Checkpoint — Verify PyPI Publication
curl -s -o /dev/null -w "openhands: %{http_code}\n" \
"https://pypi.org/pypi/openhands/<version>/json"
Should return 200. Allow a few minutes for PyPI indexing.
Phase 6: Update Install Website
After publishing the release, wait a couple of minutes. The
update-install-website.yml workflow automatically opens a PR in
install-openhands-website.
gh pr list --repo All-Hands-AI/install-openhands-website \
--search "update-version-<version>" --json number,title,url
Approve and merge the PR so users get the latest version via:
curl -fsSL https://install.openhands.dev/install.sh | sh
If the automated PR was not created, trigger the workflow manually:
curl -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/OpenHands/OpenHands-CLI/actions/workflows/update-install-website.yml/dispatches" \
-d '{
"ref": "main",
"inputs": {
"version": "<version>"
}
}'
Phase 7: Post-Release — Notify the Team
Compose a Slack message for the human to post:
🚀 *CLI v<version> released!*
• PyPI: https://pypi.org/project/openhands/<version>/
• Release: https://github.com/OpenHands/OpenHands-CLI/releases/tag/<version>
• Install website PR: https://github.com/All-Hands-AI/install-openhands-website/pulls
See references/post-release-checklist.md for additional verification
steps and troubleshooting.
Quick Reference — Full Checklist
- Trigger
bump-version.ymlwith target version - All CI checks pass on the bump PR
- 🚨 Get human approval, then tag the release (no
vprefix) - 🚨 Get human approval, then merge the PR
- 🚨 Get human approval, then publish the GitHub release
- Verify platform binaries are attached to the release
- Verify
openhandspackage is available on PyPI - Approve and merge the install website PR
- Notify the team on Slack