Skip to content

Commit c573b35

Browse files
committed
ci: ship prebuilt macOS binaries via GitHub Actions, drop on-user compile
The Homebrew tap formula was compiling looper from source on every install, pulling in rust + cargo deps and burning user CPU for minutes per upgrade. This commit moves compilation to CI: - .github/workflows/release.yml: tag-triggered matrix builds aarch64 and x86_64 macOS tarballs and attaches them to the GitHub release - scripts/render-formula.sh: emits a dual-arch formula with on_arm/on_intel download URLs and SHA256s, declares ffmpeg + yt-dlp as runtime deps, preserves --HEAD source-build path for power users - Makefile: release no longer creates the GH release (CI does); bump-formula now waits for CI assets, downloads them, computes both SHA256s, and regenerates the formula via the renderer script; new smoke-test target verifies the published formula installs the prebuilt binary cleanly
1 parent c0bb30f commit c573b35

3 files changed

Lines changed: 179 additions & 20 deletions

File tree

.github/workflows/release.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
create-release:
13+
name: Create GitHub Release
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: softprops/action-gh-release@v2
17+
with:
18+
generate_release_notes: true
19+
20+
build:
21+
name: Build ${{ matrix.target }}
22+
needs: create-release
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
include:
27+
- target: aarch64-apple-darwin
28+
os: macos-14
29+
- target: x86_64-apple-darwin
30+
os: macos-13
31+
runs-on: ${{ matrix.os }}
32+
steps:
33+
- uses: actions/checkout@v4
34+
35+
- uses: dtolnay/rust-toolchain@stable
36+
with:
37+
targets: ${{ matrix.target }}
38+
39+
- uses: Swatinem/rust-cache@v2
40+
with:
41+
key: ${{ matrix.target }}
42+
43+
- name: Build release binary
44+
run: cargo build --release --target ${{ matrix.target }}
45+
46+
- name: Package binary
47+
run: |
48+
mkdir -p dist
49+
tar -czf "dist/looper-${{ matrix.target }}.tar.gz" \
50+
-C "target/${{ matrix.target }}/release" looper
51+
52+
- name: Upload artifact to release
53+
uses: softprops/action-gh-release@v2
54+
with:
55+
files: dist/looper-${{ matrix.target }}.tar.gz

Makefile

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.PHONY: help build build-release build-macos run test test-all install clean \
2-
release release-patch release-minor bump-formula \
2+
release release-patch release-minor bump-formula wait-for-release-assets smoke-test \
33
bench-all bench-startup bench-playback bench-pause bench-memory bench-cpu \
44
bench-profile bench-watch bench-results bench-analyze bench-clean
55

@@ -143,30 +143,80 @@ release-minor: ## Bump minor version and release
143143
cargo set-version --bump minor
144144
$(MAKE) release
145145

146-
release: ## Tag, push, create GitHub release, and update Homebrew formula
146+
release: ## Tag, push, wait for CI binaries, then update Homebrew tap
147147
@echo "Releasing v$(VERSION)..."
148148
git add Cargo.toml Cargo.lock
149149
git diff --cached --quiet || git commit -m "Bump version to v$(VERSION)"
150150
git tag v$(VERSION)
151151
git push origin main
152152
git push origin v$(VERSION)
153-
gh release create v$(VERSION) \
154-
--repo program247365/looper \
155-
--title "v$(VERSION)" \
156-
--generate-notes
153+
@echo "Tag pushed. CI will build binaries and create the GitHub release."
157154
$(MAKE) bump-formula
158155

159-
bump-formula: ## Update Homebrew tap formula to current version
160-
$(eval SHA256 := $(shell curl -sL "https://github.com/program247365/looper/archive/refs/tags/v$(VERSION).tar.gz" | shasum -a 256 | awk '{print $$1}'))
161-
@echo "SHA256: $(SHA256)"
162-
rm -rf $(TAP_DIR)
163-
git clone $(TAP_REPO) $(TAP_DIR)
164-
sed -i '' \
165-
-e 's|url ".*"|url "https://github.com/program247365/looper/archive/refs/tags/v$(VERSION).tar.gz"|' \
166-
-e 's|sha256 ".*"|sha256 "$(SHA256)"|' \
167-
$(TAP_DIR)/Formula/looper.rb
168-
cd $(TAP_DIR) && git add Formula/looper.rb && \
169-
git commit -m "Update looper to v$(VERSION)" && \
170-
git push origin main
171-
rm -rf $(TAP_DIR)
172-
@echo "Done. Install with: brew tap program247365/tap && brew install looper"
156+
wait-for-release-assets: ## Wait until both arch tarballs are attached to the GH release
157+
@echo "Waiting for v$(VERSION) release assets (arm64 + x86_64)..."
158+
@for i in $$(seq 1 90); do \
159+
ASSETS=$$(gh release view v$(VERSION) --repo program247365/looper --json assets --jq '.assets[].name' 2>/dev/null || true); \
160+
ARM=$$(echo "$$ASSETS" | grep -c "looper-aarch64-apple-darwin.tar.gz" || true); \
161+
X86=$$(echo "$$ASSETS" | grep -c "looper-x86_64-apple-darwin.tar.gz" || true); \
162+
if [ "$$ARM" = "1" ] && [ "$$X86" = "1" ]; then \
163+
echo "Both assets present."; \
164+
exit 0; \
165+
fi; \
166+
printf " (%02d/90) waiting... arm64=%s x86_64=%s\n" "$$i" "$$ARM" "$$X86"; \
167+
sleep 10; \
168+
done; \
169+
echo "Timed out waiting for release assets. Check: gh run list --repo program247365/looper"; \
170+
exit 1
171+
172+
bump-formula: wait-for-release-assets ## Update tap formula with prebuilt binary URLs + SHA256s
173+
@set -e; \
174+
ARM_URL="https://github.com/program247365/looper/releases/download/v$(VERSION)/looper-aarch64-apple-darwin.tar.gz"; \
175+
X86_URL="https://github.com/program247365/looper/releases/download/v$(VERSION)/looper-x86_64-apple-darwin.tar.gz"; \
176+
echo "Computing SHA256s..."; \
177+
ARM_SHA=$$(curl -fsSL "$$ARM_URL" | shasum -a 256 | awk '{print $$1}'); \
178+
X86_SHA=$$(curl -fsSL "$$X86_URL" | shasum -a 256 | awk '{print $$1}'); \
179+
echo " arm64: $$ARM_SHA"; \
180+
echo " x86_64: $$X86_SHA"; \
181+
rm -rf $(TAP_DIR); \
182+
git clone $(TAP_REPO) $(TAP_DIR); \
183+
bash scripts/render-formula.sh "$(VERSION)" "$$ARM_SHA" "$$X86_SHA" > $(TAP_DIR)/Formula/looper.rb; \
184+
cd $(TAP_DIR) && git add Formula/looper.rb && \
185+
git commit -m "Update looper to v$(VERSION)" && \
186+
git push origin main; \
187+
rm -rf $(TAP_DIR); \
188+
echo "Done. Users now get a prebuilt binary on 'brew upgrade looper'."
189+
190+
smoke-test: ## Verify the published formula installs the prebuilt binary cleanly
191+
@echo "Smoke-testing prebuilt install for v$(VERSION)..."
192+
@brew tap program247365/tap >/dev/null 2>&1 || true
193+
@echo "==> Refreshing tap..."
194+
@brew update --quiet
195+
@echo "==> Asserting formula uses prebuilt-binary install path..."
196+
@brew cat program247365/tap/looper | grep -q 'bin.install "looper"' || \
197+
(echo "FAIL: formula is not on the prebuilt path"; exit 1)
198+
@echo "==> Asserting tap formula version matches Cargo.toml..."
199+
@TAP_VERSION=$$(brew cat program247365/tap/looper | awk -F'"' '/^ version /{print $$2; exit}'); \
200+
if [ "$$TAP_VERSION" != "$(VERSION)" ]; then \
201+
echo "FAIL: tap has v$$TAP_VERSION but Cargo.toml is v$(VERSION) — run 'make bump-formula' first"; \
202+
exit 1; \
203+
fi; \
204+
echo " tap version: $$TAP_VERSION"
205+
@echo "==> Reinstalling..."
206+
@if brew list --versions program247365/tap/looper >/dev/null 2>&1; then \
207+
brew reinstall program247365/tap/looper; \
208+
else \
209+
brew install program247365/tap/looper; \
210+
fi
211+
@echo "==> Verifying binary..."
212+
@INSTALLED=$$(brew list --versions program247365/tap/looper | awk '{print $$2}'); \
213+
if [ "$$INSTALLED" != "$(VERSION)" ]; then \
214+
echo "FAIL: brew installed v$$INSTALLED but expected v$(VERSION)"; \
215+
exit 1; \
216+
fi; \
217+
echo " installed: v$$INSTALLED"
218+
@LOOPER_BIN="$$(brew --prefix)/bin/looper"; \
219+
"$$LOOPER_BIN" --help >/dev/null 2>&1 && echo " --help: OK" || \
220+
(echo "FAIL: $$LOOPER_BIN --help failed"; exit 1)
221+
@echo ""
222+
@echo "Smoke test passed for v$(VERSION)."

scripts/render-formula.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env bash
2+
# Render the Homebrew formula for looper with prebuilt-binary URLs and SHA256s.
3+
# Usage: render-formula.sh <version> <arm64-sha256> <x86_64-sha256>
4+
5+
set -euo pipefail
6+
7+
if [[ $# -ne 3 ]]; then
8+
echo "usage: $0 <version> <arm64-sha256> <x86_64-sha256>" >&2
9+
exit 2
10+
fi
11+
12+
VERSION="$1"
13+
ARM_SHA="$2"
14+
X86_SHA="$3"
15+
16+
cat <<EOF
17+
class Looper < Formula
18+
desc "CLI tool that plays a song on loop with a ratatui TUI and FFT visualizer"
19+
homepage "https://github.com/program247365/looper"
20+
version "${VERSION}"
21+
license "MIT"
22+
23+
on_macos do
24+
on_arm do
25+
url "https://github.com/program247365/looper/releases/download/v${VERSION}/looper-aarch64-apple-darwin.tar.gz"
26+
sha256 "${ARM_SHA}"
27+
end
28+
on_intel do
29+
url "https://github.com/program247365/looper/releases/download/v${VERSION}/looper-x86_64-apple-darwin.tar.gz"
30+
sha256 "${X86_SHA}"
31+
end
32+
end
33+
34+
depends_on "ffmpeg"
35+
depends_on "yt-dlp"
36+
37+
head do
38+
url "https://github.com/program247365/looper.git", branch: "main"
39+
depends_on "rust" => :build
40+
end
41+
42+
def install
43+
if build.head?
44+
system "cargo", "install", *std_cargo_args
45+
else
46+
bin.install "looper"
47+
end
48+
end
49+
50+
test do
51+
assert_match "looper", shell_output("#{bin}/looper --help 2>&1")
52+
end
53+
end
54+
EOF

0 commit comments

Comments
 (0)