From 6f2112a7d6ac3b231ab4835953b9f4199011f6f2 Mon Sep 17 00:00:00 2001 From: Nathan Nutter Date: Wed, 23 Sep 2026 14:32:35 -0500 Subject: [PATCH 1/3] Sign the app bundle ad-hoc after build An unsigned bundle downloaded from a release fails Gatekeeper with a misleading is damaged error once quarantine is applied. Ad-hoc signing marks the bundle intact; first launch still needs right-click Open since the signature is not notarized. --- mise.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mise.toml b/mise.toml index a78ab76..a7cc574 100644 --- a/mise.toml +++ b/mise.toml @@ -29,6 +29,11 @@ mkdir -p dist/Timber.app/Contents/MacOS dist/Timber.app/Contents/Resources cp "$BIN_DIR/Timber" dist/Timber.app/Contents/MacOS/Timber cp packaging/Info.plist dist/Timber.app/Contents/Info.plist cp -R "$BIN_DIR/Timber_Timber.bundle" dist/Timber.app/Contents/Resources/ +# Ad-hoc sign so Gatekeeper treats a downloaded copy as an intact app +# bundle instead of reporting it as damaged. Still unsigned for +# Gatekeeper's notarization check, so first launch needs right-click Open. +codesign --force --deep --sign - dist/Timber.app +codesign --verify --verbose dist/Timber.app """ [tasks.test] From dfad1e949210e2afeb16b4d0da6529439fd31253 Mon Sep 17 00:00:00 2001 From: Nathan Nutter Date: Wed, 23 Sep 2026 14:33:57 -0500 Subject: [PATCH 2/3] Build a universal binary for both Apple Silicon and Intel A plain swift build emits a native-arch-only binary, so the release zip built on arm64 runners cannot launch on Intel Macs. Build both triples in isolated scratch dirs and combine them with lipo. --- mise.toml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mise.toml b/mise.toml index a7cc574..da566b1 100644 --- a/mise.toml +++ b/mise.toml @@ -22,13 +22,20 @@ fi # sandbox so it runs without an approval prompt; scratch output already # lives in .build. SWIFT_PM_FLAGS="--disable-sandbox --cache-path /tmp/swiftpm-cache --config-path /tmp/swiftpm-config --security-path /tmp/swiftpm-security" -swift build $SWIFT_PM_FLAGS -BIN_DIR="$(swift build $SWIFT_PM_FLAGS --show-bin-path)" +# Plain `swift build` emits a native-arch-only binary (arm64 on current +# runners), which cannot launch on Intel Macs. Build both slices and +# combine them so one release zip runs everywhere. Each triple gets +# its own scratch dir since they otherwise share one output path and +# the second build overwrites the first. +swift build $SWIFT_PM_FLAGS --scratch-path .build/arm64 --triple arm64-apple-macosx +swift build $SWIFT_PM_FLAGS --scratch-path .build/x86_64 --triple x86_64-apple-macosx +ARM_BIN="$(swift build $SWIFT_PM_FLAGS --scratch-path .build/arm64 --triple arm64-apple-macosx --show-bin-path)" +X86_BIN="$(swift build $SWIFT_PM_FLAGS --scratch-path .build/x86_64 --triple x86_64-apple-macosx --show-bin-path)" rm -rf dist/TimberMenu.app dist/Timber.app mkdir -p dist/Timber.app/Contents/MacOS dist/Timber.app/Contents/Resources -cp "$BIN_DIR/Timber" dist/Timber.app/Contents/MacOS/Timber +lipo -create "$ARM_BIN/Timber" "$X86_BIN/Timber" -output dist/Timber.app/Contents/MacOS/Timber cp packaging/Info.plist dist/Timber.app/Contents/Info.plist -cp -R "$BIN_DIR/Timber_Timber.bundle" dist/Timber.app/Contents/Resources/ +cp -R "$ARM_BIN/Timber_Timber.bundle" dist/Timber.app/Contents/Resources/ # Ad-hoc sign so Gatekeeper treats a downloaded copy as an intact app # bundle instead of reporting it as damaged. Still unsigned for # Gatekeeper's notarization check, so first launch needs right-click Open. From 98056e8e3ed1b7904286ad50265a405aca3e99a9 Mon Sep 17 00:00:00 2001 From: Nathan Nutter Date: Wed, 23 Sep 2026 14:34:21 -0500 Subject: [PATCH 3/3] Document first-launch Gatekeeper step for release zips The bundle is ad-hoc signed, not notarized, so document the right-click Open and xattr quarantine-clearing workarounds. --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 8755c48..ee4f15e 100644 --- a/README.md +++ b/README.md @@ -62,3 +62,14 @@ icon). That also matters for notifications: `UNUserNotificationCenter` traps in a raw binary, so the app posts native notifications only from the bundle and falls back to `osascript display notification` otherwise (e.g. `swift run Timber`). + +## Install a release download + +Release zips carry a universal (`arm64` + `x86_64`) build with an +ad-hoc signature (no notarization). On first launch Gatekeeper will +refuse a plain double-click; instead right-click (or Ctrl-click) +`Timber.app` → Open → Open, or clear quarantine once: + +```sh +xattr -d com.apple.quarantine Timber.app # path to your copy +```