Skip to content

Commit 64c0140

Browse files
Wenxin-Jiangclaude
andauthored
fix: split release workflow into PR-based prep + auto-publish (#47)
* fix: split release workflow into PR-based prep + auto-publish The previous release workflow pushed version bumps directly to main, which is blocked by branch protection rules. Split into two workflows: 1. release-prep.yml (workflow_dispatch): bumps version and opens a PR 2. release.yml (on PR merge): tags, builds, and publishes when a release/v* branch is merged to main This allows releases without needing admin bypass for github-actions[bot]. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use env var to prevent template injection in release workflow Passes github.event.pull_request.head.ref through an env variable instead of direct template expansion to satisfy zizmor audit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2c78da1 commit 64c0140

File tree

2 files changed

+106
-45
lines changed

2 files changed

+106
-45
lines changed

.github/workflows/release-prep.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Prep Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
bump:
7+
description: 'Version bump type'
8+
required: true
9+
default: 'patch'
10+
type: choice
11+
options:
12+
- patch
13+
- minor
14+
- major
15+
16+
permissions: {}
17+
18+
jobs:
19+
create-release-pr:
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: write
23+
pull-requests: write
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
27+
28+
- name: Configure Git
29+
run: |
30+
git config user.name "github-actions[bot]"
31+
git config user.email "github-actions[bot]@users.noreply.github.com"
32+
33+
- name: Bump version and sync
34+
id: sync
35+
run: |
36+
CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
37+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
38+
case "${{ inputs.bump }}" in
39+
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
40+
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
41+
patch) PATCH=$((PATCH + 1)) ;;
42+
esac
43+
VERSION="${MAJOR}.${MINOR}.${PATCH}"
44+
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
45+
bash scripts/version-sync.sh "$VERSION"
46+
47+
- name: Create release branch and PR
48+
env:
49+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50+
run: |
51+
VERSION="${{ steps.sync.outputs.VERSION }}"
52+
BRANCH="release/v${VERSION}"
53+
git checkout -b "$BRANCH"
54+
git add Cargo.toml npm/ pypi/
55+
git commit -m "v${VERSION}: bump and sync package versions"
56+
git push origin "$BRANCH"
57+
gh pr create \
58+
--title "v${VERSION}: bump and sync package versions" \
59+
--body "## Release v${VERSION}
60+
61+
Automated version bump (${{ inputs.bump }}).
62+
63+
**Merge this PR to trigger the release workflow**, which will:
64+
1. Tag the merge commit as \`v${VERSION}\`
65+
2. Build binaries for all platforms
66+
3. Publish to npm, crates.io, PyPI, and GitHub Releases" \
67+
--base main \
68+
--head "$BRANCH"

.github/workflows/release.yml

Lines changed: 38 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
11
name: Release
22

33
on:
4-
workflow_dispatch:
5-
inputs:
6-
bump:
7-
description: 'Version bump type'
8-
required: true
9-
default: 'patch'
10-
type: choice
11-
options:
12-
- patch
13-
- minor
14-
- major
4+
pull_request:
5+
types: [closed]
6+
branches: [main]
157

168
permissions: {}
179

1810
jobs:
19-
sync-and-tag:
11+
check-release:
12+
if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/v')
13+
runs-on: ubuntu-latest
14+
outputs:
15+
version: ${{ steps.extract.outputs.VERSION }}
16+
steps:
17+
- name: Extract version from branch name
18+
id: extract
19+
env:
20+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
21+
run: |
22+
VERSION="${HEAD_REF#release/v}"
23+
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
24+
echo "Detected release version: $VERSION"
25+
26+
tag:
27+
needs: check-release
2028
runs-on: ubuntu-latest
2129
permissions:
2230
contents: write
23-
outputs:
24-
version: ${{ steps.sync.outputs.VERSION }}
2531
steps:
2632
- name: Checkout
2733
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -31,32 +37,19 @@ jobs:
3137
git config user.name "github-actions[bot]"
3238
git config user.email "github-actions[bot]@users.noreply.github.com"
3339
34-
- name: Bump version and sync
35-
id: sync
40+
- name: Create and push tag
3641
run: |
37-
CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
38-
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
39-
case "${{ inputs.bump }}" in
40-
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
41-
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
42-
patch) PATCH=$((PATCH + 1)) ;;
43-
esac
44-
VERSION="${MAJOR}.${MINOR}.${PATCH}"
45-
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
46-
bash scripts/version-sync.sh "$VERSION"
47-
git add Cargo.toml npm/ pypi/
48-
git commit -m "v$VERSION: bump and sync package versions"
49-
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
50-
echo "::error::Tag v$VERSION already exists."
42+
VERSION="${{ needs.check-release.outputs.version }}"
43+
TAG="v${VERSION}"
44+
if git rev-parse "$TAG" >/dev/null 2>&1; then
45+
echo "::error::Tag $TAG already exists."
5146
exit 1
5247
fi
53-
git tag "v$VERSION"
54-
55-
- name: Push changes and tag
56-
run: git push && git push --tags
48+
git tag "$TAG"
49+
git push origin "$TAG"
5750
5851
build:
59-
needs: sync-and-tag
52+
needs: [check-release, tag]
6053
strategy:
6154
matrix:
6255
include:
@@ -123,7 +116,7 @@ jobs:
123116
- name: Checkout
124117
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
125118
with:
126-
ref: v${{ needs.sync-and-tag.outputs.version }}
119+
ref: v${{ needs.check-release.outputs.version }}
127120
persist-credentials: false
128121

129122
- name: Install Rust
@@ -172,7 +165,7 @@ jobs:
172165
path: socket-patch-${{ matrix.target }}.zip
173166

174167
github-release:
175-
needs: [sync-and-tag, build]
168+
needs: [check-release, build]
176169
runs-on: ubuntu-latest
177170
permissions:
178171
contents: write
@@ -187,14 +180,14 @@ jobs:
187180
env:
188181
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
189182
run: |
190-
TAG="v${{ needs.sync-and-tag.outputs.version }}"
183+
TAG="v${{ needs.check-release.outputs.version }}"
191184
gh release create "$TAG" \
192185
--repo "$GITHUB_REPOSITORY" \
193186
--generate-notes \
194187
artifacts/*
195188
196189
cargo-publish:
197-
needs: [sync-and-tag, build]
190+
needs: [check-release, build]
198191
runs-on: ubuntu-latest
199192
permissions:
200193
contents: read
@@ -203,7 +196,7 @@ jobs:
203196
- name: Checkout
204197
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
205198
with:
206-
ref: v${{ needs.sync-and-tag.outputs.version }}
199+
ref: v${{ needs.check-release.outputs.version }}
207200
persist-credentials: false
208201

209202
- name: Install Rust
@@ -232,7 +225,7 @@ jobs:
232225
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}
233226

234227
npm-publish:
235-
needs: [sync-and-tag, build]
228+
needs: [check-release, build]
236229
runs-on: ubuntu-latest
237230
permissions:
238231
contents: read
@@ -241,7 +234,7 @@ jobs:
241234
- name: Checkout
242235
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
243236
with:
244-
ref: v${{ needs.sync-and-tag.outputs.version }}
237+
ref: v${{ needs.check-release.outputs.version }}
245238
persist-credentials: false
246239

247240
- name: Configure git for HTTPS
@@ -308,7 +301,7 @@ jobs:
308301
run: npm publish ./npm/socket-patch --provenance --access public
309302

310303
pypi-publish:
311-
needs: [sync-and-tag, build]
304+
needs: [check-release, build]
312305
runs-on: ubuntu-latest
313306
permissions:
314307
contents: read
@@ -317,7 +310,7 @@ jobs:
317310
- name: Checkout
318311
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
319312
with:
320-
ref: v${{ needs.sync-and-tag.outputs.version }}
313+
ref: v${{ needs.check-release.outputs.version }}
321314
persist-credentials: false
322315

323316
- name: Download all artifacts
@@ -336,7 +329,7 @@ jobs:
336329

337330
- name: Build platform wheels
338331
run: |
339-
VERSION="${{ needs.sync-and-tag.outputs.version }}"
332+
VERSION="${{ needs.check-release.outputs.version }}"
340333
python scripts/build-pypi-wheels.py --version "$VERSION" --artifacts artifacts --dist dist
341334
342335
- name: Publish to PyPI

0 commit comments

Comments
 (0)