| name | release |
| description | Cut a release of genlayer-py. Bumps version, updates CHANGELOG, tags, pushes — CI then publishes to PyPI and creates the GitHub Release. Use when a human asks "release v0.18.x" or "ship a new version". |
Release skill — genlayer-py
This repo follows a branch-per-major release model. There is no auto-bump on push. A release happens when a human (or you on their behalf) runs scripts/release.sh on the target stable branch.
When to use this skill
User asks anything like:
- "release v0.18.1"
- "ship a patch"
- "tag the latest fix as a release"
If they ask "publish to PyPI directly" — refuse and point at this flow. The repo doesn't have an unprotected PyPI push path; the tag is the only release entry point.
What this repo's release model expects
- Branches are named after the major they ship:
v0.18 (current stable). When v0.19 opens, the previous v0.18 stays read-only for back-ports.
- Tags live within those branches:
v0.18.1, v0.18.2, ...
- Semver-zero rule: this package is still on a 0.x line, so the MINOR component is the breaking-change boundary.
0.18 → 0.19 IS a major bump. scripts/release.sh refuses both minor and major keywords without --allow-major while we're on 0.x.
- A major (= minor on 0.x) bump means cutting a new branch (
v0.19) — not tagging on top of the current one.
CHANGELOG.md is updated in the release commit (python-semantic-release with explicit version).
publish.yml fires on the tag push and does the PyPI publish + GitHub Release.
Steps
-
Confirm intent with the user.
- Which version? If unspecified, ask whether it's patch or explicit.
- If they say "minor" or "major" while we're on 0.x, surface that this means cutting a new branch — confirm before proceeding.
-
Switch to the target branch + sync.
git checkout v0.18
git pull --ff-only origin v0.18
If the working tree isn't clean, stop and surface what's there.
-
Verify the head is shippable.
-
Run the release script.
scripts/release.sh <X.Y.Z>
It bumps pyproject.toml, updates CHANGELOG.md, commits chore(release): vX.Y.Z, tags vX.Y.Z, and pushes both the branch commit and the tag. It will NOT publish to PyPI — CI handles that.
-
Watch the publish workflow.
gh run watch
If publish.yml fails (typical: tag/pyproject mismatch, expired PYPI_API_TOKEN, build failure), report verbatim and stop. Don't retry blindly.
-
Confirm on PyPI.
pip index versions genlayer-py
The latest version should match. Report back with the version and the GitHub Release URL.
Things to refuse
- Minor or major bump on 0.x without
--allow-major. Those are major bumps in semver-zero and belong on a new branch.
- Releasing from
main — main is retired.
- Hand-editing
pyproject.toml to bump the version — the script keeps pyproject, the CHANGELOG entry, the commit message, and the tag in lockstep.
- Publishing a tag where
publish.yml failed — fix the underlying issue, re-cut the release (delete the bad tag locally and on origin, re-run the script).
Roll-back
If a release shipped but is broken:
- Don't yank from PyPI unless someone with elevated permissions has assessed the impact — PyPI yank is reversible but signals "skip this" to installers and you'll want a follow-up patch up first.
- Ship a follow-up patch via the same flow (
scripts/release.sh patch).
- After the fixed version is live, optionally yank the bad version:
pip install pkginfo twine
Why no auto-bump?
Previously push: main triggered python-semantic-release, which would auto-bump and tag whenever a feat:/fix: commit landed. Two failure modes that fix-on-merge can't address:
- Conflated decisions — "merge this PR" silently meant "ship to PyPI".
- Major bumps that slip through (
BREAKING CHANGE in a PR body produces a 0.X → 0.X+1 bump while on 0.x, which is a major).
Manual + scripted puts a checkpoint between the two without losing the bump-tag automation.