mac: add signing helper and document debug bundle

This commit is contained in:
Peter Steinberger
2025-12-07 00:30:40 +01:00
parent 757cedc233
commit b2e3013898
3 changed files with 77 additions and 10 deletions

View File

@@ -4,13 +4,15 @@ This app is usually built from `scripts/package-mac-app.sh`, which now:
- sets a stable debug bundle identifier: `com.steipete.clawdis.debug`
- writes the Info.plist with that bundle id (override via `BUNDLE_ID=...`)
- adhoc signs the main binary, the bundled CLI, and the app bundle so macOS treats each rebuild as the same signed bundle and keeps TCC permissions (notifications, accessibility, screen recording, mic, speech)
- calls `scripts/codesign-mac-app.sh` to sign the main binary, bundled CLI, and app bundle so macOS treats each rebuild as the same signed bundle and keeps TCC permissions (notifications, accessibility, screen recording, mic, speech). Defaults to adhoc; set `SIGN_IDENTITY="Developer ID Application: …"` to use a real cert.
- injects build metadata into Info.plist: `ClawdisBuildTimestamp` (UTC) and `ClawdisGitCommit` (short hash) so the About pane can show build, git, and debug/release channel.
## Usage
```bash
# from repo root
scripts/package-mac-app.sh
scripts/package-mac-app.sh # ad-hoc signing
SIGN_IDENTITY="Developer ID Application: Your Name" scripts/package-mac-app.sh # real cert
```
If you need a different bundle id (e.g. release build):
@@ -19,6 +21,14 @@ If you need a different bundle id (e.g. release build):
BUNDLE_ID=com.steipete.clawdis scripts/package-mac-app.sh
```
## Build metadata for About
`package-mac-app.sh` stamps the bundle with:
- `ClawdisBuildTimestamp`: ISO8601 UTC at package time
- `ClawdisGitCommit`: short git hash (or `unknown` if unavailable)
The About tab reads these keys to show version, build date, git commit, and whether its a debug build (via `#if DEBUG`). Run the packager to refresh these values after code changes.
## Why
TCC permissions are tied to the bundle identifier *and* code signature. Unsigned debug builds with changing UUIDs were causing macOS to forget grants after each rebuild. Adhoc signing the binaries and keeping a fixed bundle id/path (`dist/Clawdis.app`) preserves the grants between builds, matching the VibeTunnel approach.
TCC permissions are tied to the bundle identifier *and* code signature. Unsigned debug builds with changing UUIDs were causing macOS to forget grants after each rebuild. Signing the binaries (adhoc by default) and keeping a fixed bundle id/path (`dist/Clawdis.app`) preserves the grants between builds, matching the VibeTunnel approach.

55
scripts/codesign-mac-app.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail
APP_BUNDLE="${1:-dist/Clawdis.app}"
IDENTITY="${SIGN_IDENTITY:--}"
ENT_TMP=$(mktemp /tmp/clawdis-entitlements.XXXXXX.plist)
if [ ! -d "$APP_BUNDLE" ]; then
echo "App bundle not found: $APP_BUNDLE" >&2
exit 1
fi
echo "Using signing identity: $IDENTITY"
cat > "$ENT_TMP" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.hardened-runtime</key>
<true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
</dict>
</plist>
PLIST
# clear extended attributes to avoid stale signatures
xattr -cr "$APP_BUNDLE" 2>/dev/null || true
sign_item() {
local target="$1"
codesign --force --options runtime --timestamp=none --entitlements "$ENT_TMP" --sign "$IDENTITY" "$target"
}
# Sign main binary and CLI helper if present
if [ -f "$APP_BUNDLE/Contents/MacOS/Clawdis" ]; then
echo "Signing main binary"; sign_item "$APP_BUNDLE/Contents/MacOS/Clawdis"
fi
if [ -f "$APP_BUNDLE/Contents/MacOS/ClawdisCLI" ]; then
echo "Signing CLI helper"; sign_item "$APP_BUNDLE/Contents/MacOS/ClawdisCLI"
fi
# Sign any embedded frameworks/dylibs if they ever appear
if [ -d "$APP_BUNDLE/Contents/Frameworks" ]; then
find "$APP_BUNDLE/Contents/Frameworks" \( -name "*.framework" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do
echo "Signing framework: $f"; sign_item "$f"
done
fi
# Finally sign the bundle
sign_item "$APP_BUNDLE"
rm -f "$ENT_TMP"
echo "Codesign complete for $APP_BUNDLE"

View File

@@ -8,7 +8,9 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
APP_ROOT="$ROOT_DIR/dist/Clawdis.app"
BUILD_PATH="$ROOT_DIR/apps/macos/.build"
PRODUCT="Clawdis"
BUNDLE_ID="com.steipete.clawdis.debug"
BUNDLE_ID="${BUNDLE_ID:-com.steipete.clawdis.debug}"
BUILD_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
GIT_COMMIT=$(cd "$ROOT_DIR" && git rev-parse --short HEAD 2>/dev/null || echo "unknown")
cd "$ROOT_DIR/apps/macos"
@@ -42,6 +44,10 @@ cat > "$APP_ROOT/Contents/Info.plist" <<PLIST
<string>15.0</string>
<key>LSUIElement</key>
<true/>
<key>ClawdisBuildTimestamp</key>
<string>${BUILD_TS}</string>
<key>ClawdisGitCommit</key>
<string>${GIT_COMMIT}</string>
<key>NSUserNotificationUsageDescription</key>
<string>Clawdis needs notification permission to show alerts for agent actions.</string>
<key>NSScreenCaptureDescription</key>
@@ -70,12 +76,8 @@ fi
echo "⏹ Stopping any running Clawdis"
killall -q Clawdis 2>/dev/null || true
echo "🔏 Ad-hoc signing binaries for stable TCC permissions"
codesign --force --options runtime --timestamp=none --sign - "$APP_ROOT/Contents/MacOS/Clawdis"
if [ -f "$APP_ROOT/Contents/MacOS/ClawdisCLI" ]; then
codesign --force --options runtime --timestamp=none --sign - "$APP_ROOT/Contents/MacOS/ClawdisCLI"
fi
codesign --force --options runtime --timestamp=none --sign - "$APP_ROOT"
echo "🔏 Signing bundle (ad-hoc by default; set SIGN_IDENTITY for real cert)"
SIGN_IDENTITY="${SIGN_IDENTITY:--}" "$ROOT_DIR/scripts/codesign-mac-app.sh" "$APP_ROOT"
echo "✅ Bundle ready at $APP_ROOT"