Skip to content

Commit 648fba4

Browse files
nanderssclaude
andcommitted
Add deploy script that packages into a signed .app bundle
TCC will not honour an Accessibility grant for the bare Mach-O binary the README installs, so key: actions silently do nothing no matter how many times the permission is granted. Two compounding causes: 1. swift build leaves the binary linker-signed (flags 0x20002), which TCC treats differently from a real ad-hoc signature. 2. Even after codesign --force --sign -, the grant is still ignored. codesign reports the install directory as Format=bundle because of the adjacent Info.plist, but there is no bundle structure behind it and the plist is "not bound" into the signature, so there is no stable identity for TCC to pin a grant to. Running from a real .app bundle fixes it: Format=app bundle, Info.plist sealed into the signature, real CFBundleIdentifier, LaunchAgent pointed at Contents/MacOS/StreamDeckController. Sign with a certificate rather than ad-hoc. An ad-hoc designated requirement is a bare cdhash, so every rebuild is a new identity and voids the grant; a certificate makes the requirement identifier + certificate leaf, independent of the binary's contents. Verified by rebuilding: cdhash changed from 30e7b6c9 to 5f4281b6 with the grant still in effect. Fail if the identity is missing rather than falling back to ad-hoc, since the fallback appears to work and breaks key: actions later with no error in the log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent e5ddf0f commit 648fba4

1 file changed

Lines changed: 47 additions & 0 deletions

File tree

deploy.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
# Build, package into the .app bundle, sign, and restart the daemon.
3+
#
4+
# Why the bundle and the signing identity both matter:
5+
#
6+
# 1. The daemon must run from inside a signed .app bundle. TCC will not honour
7+
# an Accessibility grant for a bare Mach-O binary, and without Accessibility
8+
# CGEvent.post() no-ops, so every key: action dies with nothing logged.
9+
#
10+
# 2. It must be signed with a real identity, not ad-hoc. An ad-hoc signature's
11+
# designated requirement is a bare cdhash, so every rebuild is a new identity
12+
# and silently voids the Accessibility grant. Signing with a certificate
13+
# makes the requirement:
14+
# identifier "com.streamdeck.controller" and certificate leaf = H"..."
15+
# which is independent of the binary's contents and survives rebuilds.
16+
#
17+
# Set up the signing identity once with a self-signed code-signing certificate
18+
# (see SIGNING_IDENTITY below). If it is missing this script stops rather than
19+
# falling back to ad-hoc, because the fallback would appear to work and then
20+
# break key: actions until the grant was re-added by hand.
21+
set -euo pipefail
22+
23+
cd "$(dirname "$0")"
24+
APP="$HOME/Applications/StreamDeckController.app"
25+
AGENT="$HOME/Library/LaunchAgents/com.streamdeck.controller.plist"
26+
SIGNING_IDENTITY="StreamDeckController Local Signing"
27+
28+
if ! security find-identity -v -p codesigning | grep -q "$SIGNING_IDENTITY"; then
29+
echo "error: signing identity '$SIGNING_IDENTITY' not found or not trusted." >&2
30+
echo "Check with: security find-identity -v -p codesigning" >&2
31+
exit 1
32+
fi
33+
34+
swift build -c release
35+
36+
launchctl bootout "gui/$(id -u)" "$AGENT" 2>/dev/null || true
37+
38+
mkdir -p "$APP/Contents/MacOS"
39+
cp .build/release/StreamDeckController "$APP/Contents/MacOS/StreamDeckController"
40+
codesign --force --sign "$SIGNING_IDENTITY" "$APP"
41+
42+
launchctl bootstrap "gui/$(id -u)" "$AGENT"
43+
44+
echo
45+
echo "Deployed to $APP"
46+
codesign -d -r- "$APP" 2>&1 | grep designated
47+
echo "Log: ~/Library/Logs/StreamDeckController.log"

0 commit comments

Comments
 (0)