Skip to content

Commit 1478480

Browse files
ci: automate Homebrew formula checksum sync in the release pipeline
- release.yml now builds binaries, computes sha256, updates Formula/slmcode.rb on main via scripts/update-formula.sh and pushes before creating the release - scripts/prepare-release.sh: one-command version bump + changelog + tag (dry-run supported), so releasing is: prepare-release.sh x.y.z && push
1 parent 809b632 commit 1478480

3 files changed

Lines changed: 176 additions & 0 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,29 @@ jobs:
8585
)
8686
ls -lah dist
8787
88+
- name: Sync Homebrew formula checksums
89+
env:
90+
VERSION: ${{ steps.ver.outputs.version }}
91+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
92+
run: |
93+
set -euo pipefail
94+
# Apply the formula update on top of the latest main (not the tag
95+
# checkout) so the repo stays linear.
96+
cp scripts/update-formula.sh /tmp/update-formula.sh
97+
git fetch origin main
98+
git checkout -B main origin/main
99+
bash /tmp/update-formula.sh "$VERSION" "$PWD/dist"
100+
if git diff --quiet -- Formula/slmcode.rb; then
101+
echo "Formula unchanged — nothing to commit"
102+
else
103+
git config user.name "github-actions[bot]"
104+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
105+
git add Formula/slmcode.rb
106+
git commit -m "chore: sync Homebrew formula checksums for v${VERSION} [skip ci]"
107+
git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
108+
git push origin main
109+
fi
110+
88111
- name: Create GitHub Release
89112
uses: softprops/action-gh-release@v2
90113
with:

‎scripts/prepare-release.sh‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env bash
2+
# Prepare + tag a new SLMCode release: bumps the version everywhere, appends a
3+
# changelog entry from the commits since the last tag, runs lint + tests, then
4+
# commits and tags. Push is left to the caller so you can review first.
5+
#
6+
# The GitHub release workflow (.github/workflows/release.yml) then builds the
7+
# binaries, auto-syncs the Homebrew formula checksums and publishes the release.
8+
#
9+
# Usage:
10+
# scripts/prepare-release.sh <x.y.z> [--dry-run]
11+
# scripts/prepare-release.sh 0.14.0 --dry-run # show changes, commit nothing
12+
set -euo pipefail
13+
14+
VERSION="${1:-}"
15+
DRY=0
16+
[[ "${2:-}" == "--dry-run" ]] && DRY=1
17+
18+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
19+
echo "usage: prepare-release.sh <x.y.z> [--dry-run]" >&2
20+
exit 1
21+
fi
22+
if git tag -l "v${VERSION}" | grep -q .; then
23+
echo "error: tag v${VERSION} already exists" >&2
24+
exit 1
25+
fi
26+
for f in cmd/slmcode/version.go Makefile README.md docs/changelog.md; do
27+
if ! git diff --quiet -- "$f"; then
28+
echo "error: $f has uncommitted changes — commit or stash first" >&2
29+
exit 1
30+
fi
31+
done
32+
33+
PREV="$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || true)"
34+
PREV="${PREV:-v0.0.0}"
35+
echo "Releasing v${VERSION} (previous: ${PREV})"
36+
37+
run() {
38+
if [[ "$DRY" == 1 ]]; then
39+
echo " [dry-run] $*"
40+
else
41+
"$@"
42+
fi
43+
}
44+
45+
# 1. cmd/slmcode/version.go
46+
perl -0pi -e 's/Version = "[^"]*"/Version = "'"$VERSION"'"/' cmd/slmcode/version.go
47+
48+
# 2. Makefile VERSION
49+
perl -0pi -e 's/^VERSION \?= .*/VERSION ?= '"$VERSION"'/m' Makefile
50+
51+
# 3. README release badge
52+
perl -0pi -e 's/label=v[0-9]+\.[0-9]+\.[0-9]+/label=v'"$VERSION"'/' README.md
53+
54+
# 4. Changelog entry from conventional commits since the previous tag.
55+
{
56+
echo "## v${VERSION} — $(date -u +%Y-%m-%d)"
57+
echo ""
58+
git --no-pager log --oneline --no-merges "${PREV}..HEAD" | awk '{
59+
$1 = ""
60+
sub(/^ /, "")
61+
sub(/^(feat|fix|chore|docs|refactor|test|ci|build|perf)(\([^)]*\))?: /, "")
62+
print "- " toupper(substr($0, 1, 1)) substr($0, 2)
63+
}'
64+
} > /tmp/slmcode-changelog-entry.txt
65+
66+
perl -0pi -e 'my $entry = do { local $/; open my $fh, "<", "/tmp/slmcode-changelog-entry.txt" or die $!; <$fh> };
67+
s/(# Changelog\n\n)/$1$entry/' docs/changelog.md
68+
69+
changed="$(git diff --name-only -- cmd/slmcode/version.go Makefile README.md docs/changelog.md)"
70+
if [[ -z "$changed" ]]; then
71+
echo "error: no changes produced — nothing to release" >&2
72+
exit 1
73+
fi
74+
75+
echo "Changed files:"
76+
git --no-pager diff --stat -- cmd/slmcode/version.go Makefile README.md docs/changelog.md
77+
78+
run make lint
79+
run make test
80+
81+
run git add cmd/slmcode/version.go Makefile README.md docs/changelog.md
82+
run git commit -m "chore: release v${VERSION}"
83+
run git tag "v${VERSION}"
84+
85+
if [[ "$DRY" == 1 ]]; then
86+
git checkout -- cmd/slmcode/version.go Makefile README.md docs/changelog.md
87+
echo " [dry-run] working tree restored — nothing committed"
88+
fi
89+
90+
echo ""
91+
echo "Next steps:"
92+
echo " git push origin main && git push origin v${VERSION}"
93+
echo "The release workflow builds binaries, syncs the Homebrew formula checksums"
94+
echo "and publishes the GitHub release automatically."

‎scripts/update-formula.sh‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env bash
2+
# Sync Formula/slmcode.rb with the actual release-binary checksums.
3+
#
4+
# Called by .github/workflows/release.yml after building the release binaries,
5+
# so the Homebrew formula on main always matches the published assets.
6+
#
7+
# Usage:
8+
# scripts/update-formula.sh <version> <dist-dir>
9+
# scripts/update-formula.sh 0.13.1 dist
10+
set -euo pipefail
11+
12+
VERSION="${1:-}"
13+
DIST="${2:-dist}"
14+
FORMULA="Formula/slmcode.rb"
15+
16+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
17+
echo "usage: update-formula.sh <x.y.z> <dist-dir>" >&2
18+
exit 1
19+
fi
20+
if [[ ! -f "$FORMULA" ]]; then
21+
echo "error: $FORMULA not found (run from the repo root)" >&2
22+
exit 1
23+
fi
24+
25+
sha() {
26+
local asset="slmcode_${VERSION}_$1"
27+
if [[ ! -f "$DIST/$asset" ]]; then
28+
echo "error: $DIST/$asset not found" >&2
29+
exit 1
30+
fi
31+
shasum -a 256 "$DIST/$asset" | awk '{print $1}'
32+
}
33+
34+
# Compute all checksums up front so a missing asset fails before we touch the formula.
35+
DARWIN_ARM64="$(sha darwin_arm64)"
36+
DARWIN_AMD64="$(sha darwin_amd64)"
37+
LINUX_ARM64="$(sha linux_arm64)"
38+
LINUX_AMD64="$(sha linux_amd64)"
39+
40+
tmp="$(mktemp)"
41+
trap 'rm -f "$tmp"' EXIT
42+
43+
# Patch the version line + the four sha256 lines. Each sha256 line is matched
44+
# via the URL that precedes it, so the right asset always gets its own checksum.
45+
perl -0pe '
46+
s/^ version "[^"]*"/ version "'"$VERSION"'"/m;
47+
s/(slmcode_'"$VERSION"'_darwin_arm64"\n\s*sha256 ")[^"]*"/${1}'"$DARWIN_ARM64"'/;
48+
s/(slmcode_'"$VERSION"'_darwin_amd64"\n\s*sha256 ")[^"]*"/${1}'"$DARWIN_AMD64"'/;
49+
s/(slmcode_'"$VERSION"'_linux_arm64"\n\s*sha256 ")[^"]*"/${1}'"$LINUX_ARM64"'/;
50+
s/(slmcode_'"$VERSION"'_linux_amd64"\n\s*sha256 ")[^"]*"/${1}'"$LINUX_AMD64"'/;
51+
' "$FORMULA" > "$tmp"
52+
53+
mv "$tmp" "$FORMULA"
54+
55+
echo "✔ Formula/slmcode.rb synced for v${VERSION}"
56+
echo " darwin_arm64 $DARWIN_ARM64"
57+
echo " darwin_amd64 $DARWIN_AMD64"
58+
echo " linux_arm64 $LINUX_ARM64"
59+
echo " linux_amd64 $LINUX_AMD64"

0 commit comments

Comments
 (0)