Skip to content

Commit 04ce31e

Browse files
authored
Merge pull request #6 from proxymesh/ci/workflows-and-release-skill
CI: align workflows with python-proxy-headers and add release skill
2 parents e57c1e8 + 074ce06 commit 04ce31e

4 files changed

Lines changed: 197 additions & 11 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+
Bumps RubyProxyHeaders::VERSION in lib/ruby_proxy_headers/version.rb, opens a PR from
5+
branch release/VERSION toward main with auto-merge, and coordinates with CI that
6+
publishes a GitHub Release when that branch merges. Use when the user invokes /release,
7+
/release VERSION, asks 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 gem 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 `lib/ruby_proxy_headers/version.rb` (`RubyProxyHeaders::VERSION`, 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 the existing file 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** `lib/ruby_proxy_headers/version.rb`: set `VERSION = 'NEW_VERSION'` (single-quoted string).
56+
57+
5. **Commit and push** (never push to `main`; push only the release branch)
58+
59+
```bash
60+
git add lib/ruby_proxy_headers/version.rb
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 gem version to ${NEW_VERSION} for release."
71+
```
72+
73+
7. **Enable auto-merge** after the PR exists. In non-interactive mode, `gh` requires an explicit merge strategy with `--auto` (use the repository default: usually **`--merge`** for a merge commit, or **`--squash`** / **`--rebase`** if that is what the repo uses).
74+
75+
```bash
76+
gh pr merge <PR_NUMBER_OR_URL> --auto --merge
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 --merge`, or merge manually.
80+
81+
## After merge
82+
83+
Merging the PR into `main` runs **Release on merge** (`.github/workflows/github_release_on_release_branch_merge.yml`), which creates a **GitHub Release** for tag `v{version}` from the merge commit. Because releases created with the default `GITHUB_TOKEN` do not trigger other workflows, **Push Gem** (`push_gem.yml`) is also started via **`workflow_run`** when that release workflow finishes. Manual or API-created releases still match the `release: published` trigger on `push_gem.yml`.
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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# When a release/* PR merges into main, create a GitHub release (tag vX.Y.Z).
2+
# Events from GITHUB_TOKEN do not start other workflows; push_gem.yml is triggered via
3+
# workflow_run when this workflow completes (see push_gem.yml).
4+
5+
name: Release on merge
6+
7+
on:
8+
pull_request:
9+
types: [closed]
10+
branches:
11+
- main
12+
13+
concurrency:
14+
group: release-on-merge-${{ github.event.pull_request.number }}
15+
cancel-in-progress: false
16+
17+
permissions:
18+
contents: write
19+
20+
jobs:
21+
github-release:
22+
if: >-
23+
github.event.pull_request.merged == true &&
24+
startsWith(github.head_ref, 'release/') &&
25+
github.event.pull_request.head.repo.full_name == github.repository
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout merge commit
29+
uses: actions/checkout@v6
30+
with:
31+
ref: ${{ github.event.pull_request.merge_commit_sha }}
32+
33+
- name: Read version from lib/ruby_proxy_headers/version.rb
34+
id: meta
35+
run: |
36+
VERSION=$(ruby -r ./lib/ruby_proxy_headers/version.rb -e 'puts RubyProxyHeaders::VERSION')
37+
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
38+
39+
- name: Create GitHub Release
40+
env:
41+
GH_TOKEN: ${{ github.token }}
42+
run: |
43+
set -euo pipefail
44+
VERSION="${{ steps.meta.outputs.version }}"
45+
TAG="v${VERSION}"
46+
MERGE_SHA="${{ github.event.pull_request.merge_commit_sha }}"
47+
if gh release view "${TAG}" --repo "${{ github.repository }}" >/dev/null 2>&1; then
48+
echo "Release ${TAG} already exists; skipping."
49+
exit 0
50+
fi
51+
gh release create "${TAG}" \
52+
--repo "${{ github.repository }}" \
53+
--target "${MERGE_SHA}" \
54+
--title "${TAG}" \
55+
--generate-notes

.github/workflows/proxy_integration_tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ jobs:
1616
runs-on: ubuntu-latest
1717

1818
steps:
19-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
19+
- uses: actions/checkout@v6
2020
with:
2121
persist-credentials: false
2222

2323
- name: Set up Ruby
24-
uses: ruby/setup-ruby@2e007403fc1ec238429ecaa57af6f22f019cc135 # v1.234.0
24+
uses: ruby/setup-ruby@v1
2525
with:
2626
bundler-cache: true
2727
ruby-version: ruby

.github/workflows/push_gem.yml

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,70 @@ on:
1010
release:
1111
types: [published]
1212

13+
workflow_dispatch:
14+
15+
# GitHub does not start new workflow runs for events caused by the default
16+
# GITHUB_TOKEN (e.g. gh release create in another workflow). After
17+
# "Release on merge" creates a release, trigger this workflow instead.
18+
workflow_run:
19+
workflows: [Release on merge]
20+
types: [completed]
21+
1322
permissions:
1423
contents: read
1524

1625
jobs:
26+
gate:
27+
runs-on: ubuntu-latest
28+
outputs:
29+
publish: ${{ steps.decide.outputs.publish }}
30+
steps:
31+
- uses: actions/checkout@v6
32+
if: github.event_name == 'workflow_run'
33+
with:
34+
ref: main
35+
36+
- id: decide
37+
env:
38+
GH_TOKEN: ${{ github.token }}
39+
run: |
40+
set -euo pipefail
41+
if [[ "${{ github.event_name }}" != "workflow_run" ]]; then
42+
echo "publish=true" >> "${GITHUB_OUTPUT}"
43+
exit 0
44+
fi
45+
if [[ "${{ github.event.workflow_run.conclusion }}" != "success" ]]; then
46+
echo "publish=false" >> "${GITHUB_OUTPUT}"
47+
exit 0
48+
fi
49+
VERSION="$(ruby -r ./lib/ruby_proxy_headers/version.rb -e 'puts RubyProxyHeaders::VERSION')"
50+
TAG="v${VERSION}"
51+
if gh release view "${TAG}" --repo "${{ github.repository }}" >/dev/null 2>&1; then
52+
echo "publish=true" >> "${GITHUB_OUTPUT}"
53+
else
54+
echo "No GitHub release ${TAG} yet (or release job was skipped); skipping gem push."
55+
echo "publish=false" >> "${GITHUB_OUTPUT}"
56+
fi
57+
1758
push:
18-
if: github.repository == 'proxymesh/ruby-proxy-headers'
59+
needs: gate
60+
if: >-
61+
github.repository == 'proxymesh/ruby-proxy-headers' &&
62+
needs.gate.outputs.publish == 'true'
1963
runs-on: ubuntu-latest
2064

2165
permissions:
2266
contents: write
2367
id-token: write
2468

2569
steps:
26-
- name: Harden Runner
27-
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
28-
with:
29-
egress-policy: audit
30-
31-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
70+
- uses: actions/checkout@v6
3271
with:
72+
ref: ${{ github.event_name == 'workflow_run' && 'main' || github.ref }}
3373
persist-credentials: false
3474

3575
- name: Verify release tag matches gem version
76+
if: github.event_name == 'release'
3677
env:
3778
REF_NAME: ${{ github.ref_name }}
3879
run: |
@@ -43,9 +84,9 @@ jobs:
4384
fi
4485
4586
- name: Set up Ruby
46-
uses: ruby/setup-ruby@2e007403fc1ec238429ecaa57af6f22f019cc135 # v1.234.0
87+
uses: ruby/setup-ruby@v1
4788
with:
4889
bundler-cache: true
4990
ruby-version: ruby
5091

51-
- uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1
92+
- uses: rubygems/release-gem@v1

0 commit comments

Comments
 (0)