Skip to content

Commit 7b8df6d

Browse files
committed
Add release workflow with multi-platform builds and Homebrew tap automation
1 parent cd914d6 commit 7b8df6d

3 files changed

Lines changed: 3151 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v[0-9]+.[0-9]+.[0-9]+'
7+
8+
env:
9+
CARGO_TERM_COLOR: always
10+
BINARY_NAME: tellers
11+
12+
jobs:
13+
# ── 1. Generate the API client on Linux (the Docker action only works there) ──
14+
generate-api-client:
15+
name: Generate API Client
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Generate API client
21+
uses: openapi-generators/openapitools-generator-action@v1
22+
with:
23+
generator: rust
24+
openapi-file: src/tellers_api/openapi.tellers_public_api.yaml
25+
generator-tag: v7.17.0
26+
command-args: >
27+
-o generated/tellers_api_client
28+
--additional-properties=packageName=tellers_api_client,packageVersion=0.1.0,library=reqwest,supportAsync=true,reqwestClient=true
29+
--skip-validate-spec --strict-spec=false
30+
31+
- name: Upload generated API client
32+
uses: actions/upload-artifact@v4
33+
with:
34+
name: generated-api-client
35+
path: generated/tellers_api_client
36+
37+
# ── 2. Build release binaries for every target ──────────────────────────────
38+
build:
39+
name: Build — ${{ matrix.target }}
40+
needs: generate-api-client
41+
runs-on: ${{ matrix.os }}
42+
strategy:
43+
fail-fast: false
44+
matrix:
45+
include:
46+
- target: aarch64-apple-darwin
47+
os: macos-latest
48+
ext: tar.gz
49+
- target: x86_64-apple-darwin
50+
os: macos-latest
51+
ext: tar.gz
52+
- target: x86_64-unknown-linux-musl
53+
os: ubuntu-latest
54+
ext: tar.gz
55+
- target: aarch64-unknown-linux-gnu
56+
os: ubuntu-24.04-arm # native ARM64 runner — no cross-compilation needed
57+
ext: tar.gz
58+
- target: x86_64-pc-windows-msvc
59+
os: windows-latest
60+
ext: zip
61+
62+
steps:
63+
- uses: actions/checkout@v4
64+
65+
- name: Download generated API client
66+
uses: actions/download-artifact@v4
67+
with:
68+
name: generated-api-client
69+
path: generated/tellers_api_client
70+
71+
- name: Install Rust toolchain
72+
uses: dtolnay/rust-toolchain@stable
73+
with:
74+
targets: ${{ matrix.target }}
75+
76+
- name: Install musl tools
77+
if: matrix.target == 'x86_64-unknown-linux-musl'
78+
run: sudo apt-get install -y musl-tools
79+
80+
- name: Build release binary
81+
run: cargo build --release --locked --target ${{ matrix.target }}
82+
83+
- name: Get version from tag
84+
id: version
85+
shell: bash
86+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
87+
88+
- name: Package binary (Unix)
89+
if: matrix.ext == 'tar.gz'
90+
shell: bash
91+
run: |
92+
BIN="target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}"
93+
ARCHIVE="${{ env.BINARY_NAME }}-${{ steps.version.outputs.VERSION }}-${{ matrix.target }}.tar.gz"
94+
tar -czf "$ARCHIVE" -C "$(dirname "$BIN")" "$(basename "$BIN")"
95+
sha256sum "$ARCHIVE" | cut -d' ' -f1 > "$ARCHIVE.sha256"
96+
echo "ARCHIVE=$ARCHIVE" >> "$GITHUB_ENV"
97+
98+
- name: Package binary (Windows)
99+
if: matrix.ext == 'zip'
100+
shell: pwsh
101+
run: |
102+
$bin = "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}.exe"
103+
$archive = "${{ env.BINARY_NAME }}-${{ steps.version.outputs.VERSION }}-${{ matrix.target }}.zip"
104+
Compress-Archive -Path $bin -DestinationPath $archive
105+
$hash = (Get-FileHash $archive -Algorithm SHA256).Hash.ToLower()
106+
$hash | Out-File -FilePath "$archive.sha256" -NoNewline -Encoding ascii
107+
echo "ARCHIVE=$archive" >> $env:GITHUB_ENV
108+
109+
- name: Upload artifact
110+
uses: actions/upload-artifact@v4
111+
with:
112+
name: release-${{ matrix.target }}
113+
path: ${{ env.BINARY_NAME }}-* # archives + .sha256 files
114+
115+
# ── 3. Create the GitHub Release ────────────────────────────────────────────
116+
release:
117+
name: Create GitHub Release
118+
needs: build
119+
runs-on: ubuntu-latest
120+
permissions:
121+
contents: write
122+
outputs:
123+
version: ${{ steps.version.outputs.VERSION }}
124+
steps:
125+
- name: Get version from tag
126+
id: version
127+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
128+
129+
- name: Download all build artifacts
130+
uses: actions/download-artifact@v4
131+
with:
132+
pattern: release-*
133+
merge-multiple: true
134+
path: artifacts
135+
136+
- name: Create GitHub Release
137+
uses: softprops/action-gh-release@v2
138+
with:
139+
files: artifacts/**
140+
generate_release_notes: true
141+
142+
# ── 4. Push updated formula to the Homebrew tap ─────────────────────────────
143+
#
144+
# Prerequisites (one-time setup):
145+
# 1. Create repo tellers-ai/homebrew (or rename to homebrew-tellers for `brew tap tellers-ai/tellers`)
146+
# 2. Add Formula/tellers.rb (use .github/homebrew/tellers.rb as the seed)
147+
# 3. Create a GitHub PAT (classic) with the "repo" scope
148+
# 4. Add it as secret HOMEBREW_TAP_TOKEN in this repo's settings
149+
#
150+
update-homebrew-tap:
151+
name: Update Homebrew Tap
152+
needs: release
153+
runs-on: ubuntu-latest
154+
env:
155+
VERSION: ${{ needs.release.outputs.version }}
156+
steps:
157+
- name: Download build artifacts
158+
uses: actions/download-artifact@v4
159+
with:
160+
pattern: release-*
161+
merge-multiple: true
162+
path: artifacts
163+
164+
- name: Generate formula
165+
run: |
166+
MACOS_ARM_SHA=$(cat "artifacts/tellers-${VERSION}-aarch64-apple-darwin.tar.gz.sha256")
167+
MACOS_X86_SHA=$(cat "artifacts/tellers-${VERSION}-x86_64-apple-darwin.tar.gz.sha256")
168+
LINUX_X86_SHA=$(cat "artifacts/tellers-${VERSION}-x86_64-unknown-linux-musl.tar.gz.sha256")
169+
LINUX_ARM_SHA=$(cat "artifacts/tellers-${VERSION}-aarch64-unknown-linux-gnu.tar.gz.sha256")
170+
VER="${VERSION#v}"
171+
BASE="https://github.com/tellers-ai/tellers-cli/releases/download/${VERSION}"
172+
173+
cat > tellers.rb << EOF
174+
class Tellers < Formula
175+
desc "Tellers CLI - interact with tellers.ai from the terminal"
176+
homepage "https://tellers.ai"
177+
version "${VER}"
178+
license "MIT"
179+
180+
on_macos do
181+
if Hardware::CPU.arm?
182+
url "${BASE}/tellers-${VERSION}-aarch64-apple-darwin.tar.gz"
183+
sha256 "${MACOS_ARM_SHA}"
184+
else
185+
url "${BASE}/tellers-${VERSION}-x86_64-apple-darwin.tar.gz"
186+
sha256 "${MACOS_X86_SHA}"
187+
end
188+
end
189+
190+
on_linux do
191+
if Hardware::CPU.arm?
192+
url "${BASE}/tellers-${VERSION}-aarch64-unknown-linux-gnu.tar.gz"
193+
sha256 "${LINUX_ARM_SHA}"
194+
else
195+
url "${BASE}/tellers-${VERSION}-x86_64-unknown-linux-musl.tar.gz"
196+
sha256 "${LINUX_X86_SHA}"
197+
end
198+
end
199+
200+
def install
201+
bin.install "tellers"
202+
end
203+
204+
test do
205+
system "#{bin}/tellers", "--help"
206+
end
207+
end
208+
EOF
209+
210+
- name: Push formula to tap repo
211+
env:
212+
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
213+
run: |
214+
git clone "https://x-access-token:${TAP_TOKEN}@github.com/tellers-ai/homebrew.git" tap
215+
mkdir -p tap/Formula
216+
cp tellers.rb tap/Formula/tellers.rb
217+
cd tap
218+
git config user.name "GitHub Actions"
219+
git config user.email "actions@github.com"
220+
git add Formula/tellers.rb
221+
git diff --cached --quiet || git commit -m "tellers ${VERSION}"
222+
git push

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
target/
22
**/*.rs.bk
3-
Cargo.lock
3+
# Cargo.lock is intentionally tracked for binaries (ensures reproducible release builds)
44
.DS_Store
55
.idea/
66
.vscode/

0 commit comments

Comments
 (0)