Skip to content

Commit daa5132

Browse files
author
Cursor
committed
chore: add release skill and GitHub Release workflow for release/* merges
Made-with: Cursor
1 parent 5ad26e9 commit daa5132

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

.cursor/skills/release/SKILL.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
name: release
3+
description: >-
4+
Prepares a version bump in pyproject.toml, opens a PR from branch release/VERSION
5+
toward main with auto-merge, and coordinates with CI that publishes a GitHub Release
6+
when that branch merges. Use when the user invokes /release, /release VERSION, asks
7+
for a release PR, version bump, or release automation.
8+
---
9+
10+
# Release (`/release` and optional VERSION)
11+
12+
## When this applies
13+
14+
- User message starts with **`/release`** or **`/release VERSION`** (VERSION optional).
15+
- User asks to cut a release, bump the package version, or open a release PR with auto-merge.
16+
17+
## Preconditions
18+
19+
- Working tree clean (`git status`); stash or commit unrelated work first.
20+
- `gh` CLI authenticated (`gh auth status`).
21+
- Remote `origin` is GitHub.
22+
- Repository allows **auto-merge** (Settings → General → Pull Requests → Allow auto-merge). If auto-merge is unavailable, open the PR anyway and tell the user to merge manually after checks pass.
23+
24+
## Version selection
25+
26+
1. Read the current version from `pyproject.toml` under `[project]``version` (PEP 440 / semver `MAJOR.MINOR.PATCH`).
27+
2. If **VERSION was provided**: set the new version to that string (must match `^\d+\.\d+\.\d+` unless the project already uses a different scheme—then follow existing `pyproject.toml` format).
28+
3. If **VERSION was omitted**: bump the **patch** segment only (e.g. `0.2.1``0.2.2`). If the current value is not `x.y.z`, stop and ask the user for an explicit VERSION.
29+
30+
## Git identity (this repo)
31+
32+
Configure once if needed:
33+
34+
```bash
35+
git config user.email "cursor@proxymesh.com"
36+
git config user.name "Cursor"
37+
```
38+
39+
## Steps
40+
41+
1. **Sync main**
42+
43+
```bash
44+
git fetch origin main
45+
```
46+
47+
2. **Compute** `NEW_VERSION` (per rules above). **Branch name** is `release/${NEW_VERSION}` (no `v` prefix in the branch name).
48+
49+
3. **Create branch from latest main**
50+
51+
```bash
52+
git checkout -B "release/${NEW_VERSION}" origin/main
53+
```
54+
55+
4. **Edit** `pyproject.toml`: set `version = "NEW_VERSION"` in `[project]`.
56+
57+
5. **Commit and push** (never push to `main`; push only the release branch)
58+
59+
```bash
60+
git add pyproject.toml
61+
git commit -m "chore: bump version to ${NEW_VERSION}"
62+
git push -u origin "release/${NEW_VERSION}"
63+
```
64+
65+
6. **Open PR** into `main` with a short body (no Cursor boilerplate). Example:
66+
67+
```bash
68+
gh pr create --base main --head "release/${NEW_VERSION}" \
69+
--title "Release ${NEW_VERSION}" \
70+
--body "Bumps the package version to ${NEW_VERSION} for release."
71+
```
72+
73+
7. **Enable auto-merge** after the PR exists (merge method: repository default—omit `--merge` / `--squash` / `--rebase` unless the user specified one).
74+
75+
```bash
76+
gh pr merge <PR_NUMBER_OR_URL> --auto
77+
```
78+
79+
If `--auto` fails (permissions, auto-merge disabled, or pending checks), leave the PR open and report the error; the user can merge manually after CI passes. You can poll with `gh pr checks <PR_NUMBER_OR_URL> --watch` then retry `gh pr merge ... --auto`, or merge manually.
80+
81+
## After merge
82+
83+
Merging the PR into `main` triggers `.github/workflows/github_release_on_release_branch_merge.yml`, which creates a **GitHub Release** for tag `v{version}` from the merge commit. That **published** release event runs the existing PyPI **publish** workflow.
84+
85+
## Quick reference
86+
87+
| Input | Result |
88+
|--------------------|---------------------------------------------|
89+
| `/release` | Patch bump, branch `release/x.y.(z+1)` |
90+
| `/release 1.4.0` | Version `1.4.0`, branch `release/1.4.0` |
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# When a PR from release/* is merged into main, create a GitHub Release (tag vX.Y.Z).
2+
# The existing publish.yml workflow runs on release: published and uploads to PyPI.
3+
4+
name: GitHub Release on release branch merge
5+
6+
on:
7+
pull_request:
8+
types: [closed]
9+
branches:
10+
- main
11+
12+
permissions:
13+
contents: write
14+
15+
jobs:
16+
create-release:
17+
if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/')
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout merge commit
21+
uses: actions/checkout@v4
22+
with:
23+
ref: ${{ github.event.pull_request.merge_commit_sha }}
24+
25+
- name: Set up Python
26+
uses: actions/setup-python@v5
27+
with:
28+
python-version: "3.12"
29+
30+
- name: Read version from pyproject.toml
31+
id: meta
32+
run: |
33+
python3 <<'PY'
34+
import os
35+
import tomllib
36+
37+
with open("pyproject.toml", "rb") as f:
38+
data = tomllib.load(f)
39+
version = data["project"]["version"]
40+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as out:
41+
out.write(f"version={version}\n")
42+
PY
43+
44+
- name: Create GitHub Release
45+
env:
46+
GH_TOKEN: ${{ github.token }}
47+
run: |
48+
set -euo pipefail
49+
VERSION="${{ steps.meta.outputs.version }}"
50+
TAG="v${VERSION}"
51+
MERGE_SHA="${{ github.event.pull_request.merge_commit_sha }}"
52+
if gh release view "${TAG}" --repo "${{ github.repository }}" >/dev/null 2>&1; then
53+
echo "Release ${TAG} already exists; skipping."
54+
exit 0
55+
fi
56+
gh release create "${TAG}" \
57+
--repo "${{ github.repository }}" \
58+
--target "${MERGE_SHA}" \
59+
--title "${TAG}" \
60+
--generate-notes

0 commit comments

Comments
 (0)