Skip to content

Commit 45fdc5e

Browse files
committed
ci: adopt release branch flow (release-start / release-retry / FF merge to main)
# ── git-warden policy guide ── # Write the commit message in English # AI co-author trailers will be removed before commit
1 parent e99da2c commit 45fdc5e

3 files changed

Lines changed: 78 additions & 36 deletions

File tree

.github/workflows/release-publish.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,26 @@ jobs:
161161
fi
162162
git commit -m "chore: bump gitversion-rs to ${VERSION}"
163163
git push
164+
165+
merge-to-main:
166+
name: Merge release into main
167+
needs: [publish, crates-io, homebrew]
168+
if: always() && needs.publish.result == 'success' && needs.crates-io.result == 'success' && (needs.homebrew.result == 'success' || needs.homebrew.result == 'skipped')
169+
runs-on: ubuntu-latest
170+
permissions:
171+
contents: write
172+
steps:
173+
- uses: actions/checkout@v4
174+
with:
175+
ref: main
176+
fetch-depth: 0
177+
token: ${{ secrets.GITHUB_TOKEN }}
178+
179+
- name: Fast-forward main to release
180+
run: |
181+
git fetch origin release
182+
git merge --ff-only origin/release
183+
git push origin main
184+
185+
- name: Delete release branch
186+
run: git push origin --delete release

AGENTS.md

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,33 +63,33 @@ cargo clippy --all-targets -- -D warnings # lint (keep zero warnings)
6363
### Justfile commands
6464

6565
```bash
66-
just version # show current FullSemVer (gitversion-rs)
67-
just check # dry-run: see what cargo-release would do (patch)
68-
just check minor # dry-run for a minor bump
69-
just bump # bump patch: updates Cargo.toml, commits, tags, pushes (no crates.io publish)
70-
just bump minor # bump minor
71-
just bump major # bump major
72-
just publish # publish to crates.io locally (manual fallback)
73-
just gh-publish # trigger release-publish.yml: publish GitHub release + crates.io
74-
just gh-retag # delete draft release/tag and re-tag HEAD to re-trigger CI
75-
# blocked if GitHub release is published or crates.io already has the version
66+
just version # show current FullSemVer (gitversion-rs)
67+
just check # dry-run: see what cargo-release would do (patch)
68+
just check minor # dry-run for a minor bump
69+
just release-start # create release branch from main, bump patch, commit, tag, push
70+
just release-start minor # bump minor
71+
just release-start major # bump major
72+
just publish # publish to crates.io locally (manual fallback)
73+
just gh-publish # trigger release-publish.yml: publish GitHub release + crates.io + FF merge release->main
74+
just release-retry # reset a failed release: delete draft/tag/branch, recreate from latest main
75+
# blocked if GitHub release is published or crates.io already has the version
7676
```
7777

7878
### Release procedure
7979

8080
1. Ensure `main` is green.
81-
2. Bump the version and push:
81+
2. Start the release from `main`:
8282
```bash
83-
just bump minor # or patch / major
83+
just release-start minor # or patch / major
8484
```
85-
This runs `cargo release minor --execute --no-publish` which:
85+
This creates a `release` branch and runs `cargo release minor --execute --no-publish` which:
86+
- Switches to the new `release` branch
8687
- Updates `version` in `Cargo.toml`
87-
- Commits `"chore: release 0.2.0"`
88-
- Creates annotated tag `v0.2.0`
88+
- Commits `"chore: release 0.3.0"`
89+
- Creates annotated tag `v0.3.0`
8990
- Pushes commit + tag to origin
9091
3. The tag push triggers `.github/workflows/release-draft.yml`:
91-
- Builds 6 cross-compiled targets (version read directly from `Cargo.toml` via `CARGO_PKG_VERSION`)
92-
- Generates changelog with git-cliff, signs artifacts with cosign
92+
- Builds 6 cross-compiled targets, generates changelog with git-cliff, signs with cosign
9393
- Creates a GitHub **draft** release (no crates.io publish at this stage)
9494
4. Review the draft release, then publish:
9595
```bash
@@ -99,11 +99,13 @@ just gh-retag # delete draft release/tag and re-tag HEAD to re-trigger CI
9999
- Marks the GitHub release as published
100100
- Publishes to crates.io
101101
- Updates the Homebrew tap formula
102+
- Fast-forward merges `release → main` and deletes the `release` branch
102103
5. If CI failed at step 3 (e.g. workflow file was stale on the tag):
103104
```bash
104-
just gh-retag # re-tags HEAD and pushes to re-trigger release-draft.yml
105+
just release-retry minor # same level as step 2
105106
```
106-
Blocked if the release is already published or the version is already on crates.io.
107+
Deletes the draft release, tag, and `release` branch, then recreates from the latest `main`.
108+
Blocked if the release is already published or the version is on crates.io.
107109

108110
### Homebrew tap
109111

justfile

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,49 +14,66 @@ install:
1414
check level="patch":
1515
cargo release {{level}}
1616

17-
# Bump version, commit Cargo.toml, create annotated tag, push
18-
# Usage: just bump (patch)
19-
# just bump minor
20-
# just bump major
21-
bump level="patch":
17+
# Create release branch from main, bump version, commit, tag, push to origin
18+
# Usage: just release-start (patch)
19+
# just release-start minor
20+
# just release-start major
21+
release-start level="patch":
22+
#!/usr/bin/env bash
23+
set -euo pipefail
24+
CURRENT=$(git rev-parse --abbrev-ref HEAD)
25+
if [[ "${CURRENT}" != "main" ]]; then
26+
echo "Error: must be on main branch (currently on ${CURRENT})"
27+
exit 1
28+
fi
29+
if git show-ref --verify refs/heads/release >/dev/null 2>&1; then
30+
echo "Error: release branch already exists. Use 'just release-retry' to reset it."
31+
exit 1
32+
fi
33+
git pull --ff-only
34+
git checkout -b release
2235
cargo release {{level}} --execute --no-publish
2336
24-
# Publish the tagged commit to crates.io
25-
# Run after `just bump` has pushed the tag
37+
# Publish the tagged commit to crates.io (manual fallback)
2638
publish:
2739
cargo release publish --execute
2840

29-
# Trigger release-publish.yml workflow to publish draft release and push to crates.io
41+
# Trigger release-publish.yml: publish GitHub release, crates.io, FF merge release->main
3042
gh-publish:
3143
#!/usr/bin/env bash
3244
set -euo pipefail
3345
VERSION=$(yq -p toml -oy '.package.version' Cargo.toml)
3446
gh workflow run release-publish.yml -f tag="v${VERSION}"
3547
36-
# Delete existing draft release + tag, re-tag HEAD, push to re-trigger CI
37-
# Refuses to run if the release is already published (non-draft) or on crates.io
38-
gh-retag:
48+
# Reset a failed release: delete draft/tag/release branch and recreate from latest main
49+
# Blocked if the GitHub release is published or the version is already on crates.io
50+
# Usage: just release-retry (patch)
51+
# just release-retry minor
52+
release-retry level="patch":
3953
#!/usr/bin/env bash
4054
set -euo pipefail
4155
VERSION=$(yq -p toml -oy '.package.version' Cargo.toml)
4256
TAG="v${VERSION}"
4357
CRATE="gitversion-rs"
44-
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -A "gh-retag/1.0" \
58+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -A "release-retry/1.0" \
4559
"https://crates.io/api/v1/crates/${CRATE}/${VERSION}")
4660
if [[ "${STATUS}" == "200" ]]; then
47-
echo "Error: ${CRATE} v${VERSION} is already published to crates.io. Refusing to retag."
61+
echo "Error: ${CRATE} v${VERSION} is already on crates.io. Cannot retry."
4862
exit 1
4963
fi
5064
IS_DRAFT=$(gh release view "${TAG}" --json isDraft -q '.isDraft' 2>/dev/null || echo "none")
5165
if [[ "${IS_DRAFT}" == "false" ]]; then
52-
echo "Error: ${TAG} is not a draft release. Refusing to retag."
66+
echo "Error: ${TAG} is already published. Cannot retry."
5367
exit 1
5468
fi
55-
echo "Re-tagging ${TAG} at HEAD"
5669
if [[ "${IS_DRAFT}" == "true" ]]; then
5770
gh release delete "${TAG}" --yes --cleanup-tag
5871
fi
5972
git tag -d "${TAG}" 2>/dev/null || true
6073
git push origin --delete "${TAG}" 2>/dev/null || true
61-
git tag -a "${TAG}" -m "release: ${TAG#v}"
62-
git push origin "${TAG}"
74+
git push origin --delete release 2>/dev/null || true
75+
git checkout main
76+
git pull --ff-only
77+
git branch -D release 2>/dev/null || true
78+
git checkout -b release
79+
cargo release {{level}} --execute --no-publish

0 commit comments

Comments
 (0)