Skip to content

Commit e095df9

Browse files
jguiceclaude
andcommitted
feat(macos): build universal binary (Intel + Apple Silicon)
Cross-compile both aarch64-apple-darwin and x86_64-apple-darwin on the existing macos-14 (arm64) runner, then `lipo` them into a single fat binary before bundling. One signed/notarized DMG covers both Mac CPU families: utter-VERSION-macos-universal.dmg. Notarization is the slow/flaky step in this job and only runs once either way, so the cost is the second `cargo build` (cached) plus ~doubled binary size — small relative to the model. The README's macOS quickstart drops the "(Apple Silicon)" qualifier and points at the new universal DMG name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c0da115 commit e095df9

3 files changed

Lines changed: 21 additions & 14 deletions

File tree

.github/workflows/release.yml

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,14 @@ jobs:
155155
if-no-files-found: error
156156

157157
macos-build:
158-
name: Build macOS arm64 (signed + notarized DMG)
158+
name: Build macOS universal (signed + notarized DMG)
159159
needs: test
160160
runs-on: macos-14
161161
env:
162162
APPLE_ID: ${{ secrets.APPLE_ID }}
163163
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
164164
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
165165
UTTER_SIGN_IDENTITY: "Developer ID Application: Josh Guice (YKQ46WD7SL)"
166-
RUST_TARGET: aarch64-apple-darwin
167166

168167
steps:
169168
- name: Resolve checkout ref
@@ -186,12 +185,12 @@ jobs:
186185
- name: Install Rust toolchain
187186
uses: dtolnay/rust-toolchain@stable
188187
with:
189-
targets: ${{ env.RUST_TARGET }}
188+
targets: aarch64-apple-darwin,x86_64-apple-darwin
190189

191190
- name: Cache cargo
192191
uses: Swatinem/rust-cache@v2
193192
with:
194-
shared-key: release-macos-arm64
193+
shared-key: release-macos-universal
195194

196195
- name: Import Developer ID cert into temp keychain
197196
env:
@@ -220,12 +219,20 @@ jobs:
220219
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
221220
222221
- name: Build release binary (arm64)
223-
run: cargo build --release --locked --target "$RUST_TARGET"
222+
run: cargo build --release --locked --target aarch64-apple-darwin
223+
224+
- name: Build release binary (x86_64)
225+
run: cargo build --release --locked --target x86_64-apple-darwin
224226

225-
- name: Stage binary for bundle
227+
- name: Combine into universal binary
226228
run: |
229+
set -euo pipefail
227230
mkdir -p target/release
228-
cp "target/${RUST_TARGET}/release/utter" target/release/utter
231+
lipo -create -output target/release/utter \
232+
target/aarch64-apple-darwin/release/utter \
233+
target/x86_64-apple-darwin/release/utter
234+
lipo -info target/release/utter
235+
file target/release/utter
229236
230237
- name: Build + sign .app bundle
231238
run: ./scripts/make-bundle.sh
@@ -253,7 +260,7 @@ jobs:
253260
set -euo pipefail
254261
TAG="${DISPATCH_TAG:-$RAW_VERSION}"
255262
VERSION="${TAG#v}"
256-
DMG_NAME="utter-${VERSION}-macos-arm64.dmg"
263+
DMG_NAME="utter-${VERSION}-macos-universal.dmg"
257264
mkdir -p dist
258265
STAGE="$RUNNER_TEMP/dmg-stage"
259266
rm -rf "$STAGE"
@@ -274,7 +281,7 @@ jobs:
274281
- name: Sign + notarize + staple DMG
275282
run: |
276283
set -euo pipefail
277-
DMG=$(ls dist/utter-*-macos-arm64.dmg)
284+
DMG=$(ls dist/utter-*-macos-universal.dmg)
278285
codesign --force --sign "$UTTER_SIGN_IDENTITY" --timestamp "$DMG"
279286
xcrun notarytool submit "$DMG" \
280287
--apple-id "$APPLE_ID" \
@@ -287,8 +294,8 @@ jobs:
287294
- name: Upload DMG artifact
288295
uses: actions/upload-artifact@v4
289296
with:
290-
name: dist-macos-arm64
291-
path: dist/utter-*-macos-arm64.dmg
297+
name: dist-macos-universal
298+
path: dist/utter-*-macos-universal.dmg
292299
if-no-files-found: error
293300

294301
release:

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ Local, no-cloud push-to-talk dictation for **macOS and Linux**. Hold a key, spea
77

88
## Quickstart
99

10-
### macOS (Apple Silicon)
10+
### macOS (Apple Silicon or Intel)
1111

12-
1. Download `utter-VERSION-macos-arm64.dmg` from the [latest release](https://github.com/jguice/utter/releases/latest). Open it, drag `utter.app` to `/Applications`.
12+
1. Download `utter-VERSION-macos-universal.dmg` from the [latest release](https://github.com/jguice/utter/releases/latest). Open it, drag `utter.app` to `/Applications`.
1313
2. Fetch the speech model (~650 MB, one-time):
1414
```bash
1515
curl -fsSL https://raw.githubusercontent.com/jguice/utter/main/scripts/download-model.sh | bash

docs/TROUBLESHOOTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Troubleshooting (Linux)
22

3-
Everything below is Linux-specific. On macOS, check Console.app for messages tagged `utter` and open an issue if you hit something not covered by the [README quickstart's onboarding flow](../README.md#macos-apple-silicon).
3+
Everything below is Linux-specific. On macOS, check Console.app for messages tagged `utter` and open an issue if you hit something not covered by the [README quickstart's onboarding flow](../README.md#macos-apple-silicon-or-intel).
44

55
**Nothing pastes after I release the key.**
66
```bash

0 commit comments

Comments
 (0)