Skip to content

Commit 948a16b

Browse files
Auto-tag releases from main when version bumps
Release workflow now runs on push to main as well as on release-* tags. A new prepare job reads the canonical version from src-tauri/Cargo.toml, fails if the CLI Cargo.toml, tauri.conf.json, or package.json disagree, and creates/pushes the matching release-X.Y.Z tag when one doesn't already exist. Downstream build and release jobs are gated on that prepare output and read the tag/version from its outputs. Also bumps ralph-cli from 0.2.2 to 0.2.3 to align with the rest of the workspace. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 358bfc9 commit 948a16b

4 files changed

Lines changed: 104 additions & 14 deletions

File tree

.github/workflows/release.yml

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,88 @@ name: Release
22

33
on:
44
push:
5+
branches: [main]
56
tags:
67
- "release-[0-9]+.[0-9]+.[0-9]+*"
78

89
permissions:
910
contents: write
1011

1112
jobs:
13+
prepare:
14+
runs-on: ubuntu-latest
15+
outputs:
16+
version: ${{ steps.decide.outputs.version }}
17+
tag: ${{ steps.decide.outputs.tag }}
18+
should_release: ${{ steps.decide.outputs.should_release }}
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Determine version and tag
25+
id: decide
26+
shell: bash
27+
run: |
28+
set -euo pipefail
29+
30+
extract_toml_version() {
31+
# Read the first `version = "X.Y.Z"` line from the [package] table.
32+
awk -F'"' '/^version = /{print $2; exit}' "$1"
33+
}
34+
35+
DESKTOP_VER=$(extract_toml_version src-tauri/Cargo.toml)
36+
CLI_VER=$(extract_toml_version crates/ralph-cli/Cargo.toml)
37+
CONF_VER=$(python3 -c 'import json; print(json.load(open("src-tauri/tauri.conf.json"))["version"])')
38+
PKG_VER=$(python3 -c 'import json; print(json.load(open("package.json"))["version"])')
39+
40+
# src-tauri/Cargo.toml is canonical. Every other version string must
41+
# agree, otherwise the built artifacts would misrepresent themselves
42+
# and the updater's latest.json would disagree with the installed
43+
# binary's reported version.
44+
fail=0
45+
for pair in "src-tauri/Cargo.toml=$DESKTOP_VER" \
46+
"crates/ralph-cli/Cargo.toml=$CLI_VER" \
47+
"src-tauri/tauri.conf.json=$CONF_VER" \
48+
"package.json=$PKG_VER"; do
49+
file="${pair%%=*}"
50+
ver="${pair#*=}"
51+
if [[ "$ver" != "$DESKTOP_VER" ]]; then
52+
echo "::error file=$file::version mismatch: $file is '$ver', expected '$DESKTOP_VER' (from src-tauri/Cargo.toml)"
53+
fail=1
54+
fi
55+
done
56+
if [[ "$fail" -eq 1 ]]; then
57+
exit 1
58+
fi
59+
60+
VERSION="$DESKTOP_VER"
61+
TAG="release-${VERSION}"
62+
63+
if [[ "$GITHUB_REF" == refs/tags/release-* ]]; then
64+
# Direct tag push — build + release unconditionally.
65+
SHOULD_RELEASE=true
66+
else
67+
# Push to main — only release if this version hasn't been tagged.
68+
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
69+
echo "Tag $TAG already exists — version unchanged, skipping release"
70+
SHOULD_RELEASE=false
71+
else
72+
git config user.name "github-actions[bot]"
73+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
74+
git tag -a "$TAG" -m "Release $VERSION"
75+
git push origin "$TAG"
76+
SHOULD_RELEASE=true
77+
fi
78+
fi
79+
80+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
81+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
82+
echo "should_release=$SHOULD_RELEASE" >> "$GITHUB_OUTPUT"
83+
1284
build-cli:
85+
needs: prepare
86+
if: needs.prepare.outputs.should_release == 'true'
1387
strategy:
1488
matrix:
1589
include:
@@ -113,6 +187,8 @@ jobs:
113187
path: ${{ matrix.artifact }}${{ runner.os == 'Windows' && '.exe' || '' }}
114188

115189
build-desktop:
190+
needs: prepare
191+
if: needs.prepare.outputs.should_release == 'true'
116192
strategy:
117193
matrix:
118194
include:
@@ -193,12 +269,12 @@ jobs:
193269
- name: Collect updater metadata
194270
shell: bash
195271
env:
196-
TAG_NAME: ${{ github.ref_name }}
272+
TAG_NAME: ${{ needs.prepare.outputs.tag }}
273+
VERSION: ${{ needs.prepare.outputs.version }}
197274
BUNDLE_DIR: target/${{ matrix.target }}/release/bundle
198275
UPDATER_PLATFORM: ${{ matrix.updater_platform }}
199276
run: |
200277
set -euo pipefail
201-
VERSION="${TAG_NAME#release-}"
202278
# Find the updater bundle + its .sig produced by Tauri. Each OS
203279
# has exactly one updater target: .app.tar.gz (mac), .nsis .exe
204280
# (windows), .AppImage (linux).
@@ -251,38 +327,35 @@ jobs:
251327
path: updater-meta/*.json
252328

253329
release:
254-
needs: [build-cli, build-desktop]
330+
needs: [prepare, build-cli, build-desktop]
331+
if: needs.prepare.outputs.should_release == 'true'
255332
runs-on: ubuntu-latest
256333

257334
steps:
258-
- name: Extract version from tag
259-
id: version
260-
run: echo "version=${GITHUB_REF_NAME#release-}" >> $GITHUB_OUTPUT
261-
262335
- name: Download all artifacts
263336
uses: actions/download-artifact@v4
264337
with:
265338
path: artifacts
266339

267340
- name: Assemble updater latest.json
341+
env:
342+
VERSION: ${{ needs.prepare.outputs.version }}
268343
run: |
269344
set -euo pipefail
270-
VERSION="${{ steps.version.outputs.version }}"
271345
PUB_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
272346
python3 - <<PY
273347
import json, glob, os
274348
platforms = {}
275-
version = None
349+
version = os.environ["VERSION"]
276350
for path in glob.glob("artifacts/updater-meta-*/*.json"):
277351
with open(path) as f:
278352
meta = json.load(f)
279-
version = meta["version"]
280353
platforms[meta["platform"]] = {
281354
"signature": meta["signature"],
282355
"url": meta["url"],
283356
}
284357
out = {
285-
"version": version or "$VERSION",
358+
"version": version,
286359
"notes": "See release notes on GitHub.",
287360
"pub_date": "$PUB_DATE",
288361
"platforms": platforms,
@@ -296,7 +369,8 @@ jobs:
296369
- name: Create GitHub Release
297370
uses: softprops/action-gh-release@v2
298371
with:
299-
name: v${{ steps.version.outputs.version }}
372+
tag_name: ${{ needs.prepare.outputs.tag }}
373+
name: v${{ needs.prepare.outputs.version }}
300374
generate_release_notes: true
301375
body: |
302376
## macOS notes

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@ npx tauri signer generate -w ~/.tauri/ralph-updater.key
127127
Losing the private key is unrecoverable — existing installs can no longer be
128128
upgraded through the in-app updater and would need a fresh install.
129129

130+
### Maintainer: cutting a release
131+
132+
1. Bump the version in **all four** files (they must match):
133+
- `src-tauri/Cargo.toml` (canonical source)
134+
- `crates/ralph-cli/Cargo.toml`
135+
- `src-tauri/tauri.conf.json`
136+
- `package.json`
137+
2. Commit and push to `main`. The release workflow will:
138+
- verify the four version strings agree (fail CI otherwise),
139+
- tag the commit as `release-X.Y.Z` if that tag doesn't already exist,
140+
- build and sign the CLI + desktop bundles,
141+
- publish them + `latest.json` to a GitHub Release.
142+
143+
Pushing a `release-X.Y.Z` tag by hand also works and skips the version-change
144+
detection — useful for re-running a release.
145+
130146
## Building from source
131147

132148
```bash

crates/ralph-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ralph-cli"
3-
version = "0.2.2"
3+
version = "0.2.3"
44
edition = "2021"
55

66
[[bin]]

0 commit comments

Comments
 (0)