Skip to content

Commit d501123

Browse files
cyberpapiiiclaude
andcommitted
feat: add Makefile for build, sign, and install workflow
Single command `make install` builds release, signs with persistent identity, restarts launchd service, and verifies health. `make setup-signing` creates a self-signed code signing certificate so Full Disk Access persists across rebuilds (macOS TCC matches on certificate identity rather than binary hash). Other targets: status, logs, clean, help. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fb41a22 commit d501123

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

swift/Makefile

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# iMessage Max — Build, Sign & Install
2+
#
3+
# Usage:
4+
# make install Build, sign, restart service, verify health
5+
# make build Build release binary only
6+
# make restart Restart the launchd service
7+
# make status Show service health and version
8+
# make logs Tail stderr log
9+
# make clean Remove debug build artifacts
10+
# make setup-signing Create persistent code signing identity (one-time)
11+
#
12+
# The signing identity lets Full Disk Access persist across rebuilds.
13+
# Without it, you'd need to re-grant FDA in System Settings every build.
14+
# Run `make setup-signing` once, then `make install` for every update.
15+
16+
BINARY := .build/release/imessage-max
17+
IDENTITY := iMessage Max Dev
18+
LAUNCHD_LABEL := local.imessage-max
19+
PORT := 8080
20+
CERT_CN := iMessage Max Dev
21+
22+
.PHONY: build sign install restart status logs clean setup-signing verify help
23+
24+
help: ## Show this help
25+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}'
26+
27+
build: ## Build release binary
28+
@echo "Building release..."
29+
@swift build -c release 2>&1 | grep -E "Build complete|error:|warning:.*imessage" || true
30+
@echo "✓ Built $(BINARY)"
31+
32+
sign: build ## Build and sign with persistent identity
33+
@if codesign --force --sign "$(IDENTITY)" $(BINARY) 2>/dev/null; then \
34+
echo "✓ Signed with '$(IDENTITY)' — FDA persists across rebuilds"; \
35+
elif security find-certificate -c "$(CERT_CN)" ~/Library/Keychains/login.keychain-db >/dev/null 2>&1; then \
36+
codesign --force --sign "$(IDENTITY)" $(BINARY) 2>&1; \
37+
echo "✓ Signed with '$(IDENTITY)'"; \
38+
else \
39+
echo "⚠ No signing identity '$(IDENTITY)' — using ad-hoc (FDA needs re-granting each build)"; \
40+
echo " Run 'make setup-signing' once to fix this permanently"; \
41+
fi
42+
43+
restart: ## Restart the launchd service
44+
@echo "Restarting service..."
45+
@launchctl kickstart -k gui/$$(id -u)/$(LAUNCHD_LABEL) 2>/dev/null || \
46+
(launchctl bootstrap gui/$$(id -u) ~/Library/LaunchAgents/$(LAUNCHD_LABEL).plist 2>/dev/null && echo "Bootstrapped") || true
47+
@echo "✓ Service restarted"
48+
49+
verify: ## Verify server is healthy
50+
@echo "Waiting for server..."
51+
@for i in 1 2 3 4 5 6 7 8; do \
52+
sleep 1; \
53+
RESULT=$$(curl -sf http://127.0.0.1:$(PORT) -X POST \
54+
-H "Content-Type: application/json" \
55+
-H "Accept: application/json" \
56+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"verify","version":"1.0"}}}' 2>/dev/null); \
57+
if [ -n "$$RESULT" ]; then \
58+
echo "$$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(f'✓ {r[\"serverInfo\"][\"name\"]} v{r[\"serverInfo\"][\"version\"]} healthy on port $(PORT)')"; \
59+
exit 0; \
60+
fi; \
61+
done; \
62+
echo "✗ Server not responding on port $(PORT)"; exit 1
63+
64+
install: sign restart verify ## Build, sign, restart, verify (the full workflow)
65+
@echo ""
66+
@echo "Done! If MCP Router lost connection, reconnect with /mcp"
67+
68+
status: ## Show service status and version
69+
@echo "=== Process ==="
70+
@pgrep -fl "imessage-max.*$(PORT)" || echo "Not running"
71+
@echo ""
72+
@echo "=== Version ==="
73+
@$(BINARY) --version 2>/dev/null || echo "Binary not found"
74+
@echo ""
75+
@echo "=== Signature ==="
76+
@codesign -dvv $(BINARY) 2>&1 | grep -E "Signature|Authority|CDHash" || echo "Not signed"
77+
@echo ""
78+
@echo "=== Health ==="
79+
@curl -sf http://127.0.0.1:$(PORT) -X POST \
80+
-H "Content-Type: application/json" \
81+
-H "Accept: application/json" \
82+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"status","version":"1.0"}}}' 2>/dev/null \
83+
| python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(f'✓ Responding — v{r[\"serverInfo\"][\"version\"]}')" 2>/dev/null \
84+
|| echo "✗ Not responding"
85+
86+
logs: ## Tail the stderr log
87+
@tail -f ~/Library/Logs/imessage-max.stderr.log
88+
89+
clean: ## Remove debug artifacts and old logs
90+
@echo "Cleaning..."
91+
@rm -rf .build/debug .build/arm64-apple-macosx/debug .build/debug.yaml
92+
@echo " Removed debug build artifacts"
93+
@cat /dev/null > ~/Library/Logs/imessage-max.stderr.log 2>/dev/null || true
94+
@cat /dev/null > ~/Library/Logs/imessage-max.stdout.log 2>/dev/null || true
95+
@echo " Cleared log files"
96+
@echo "✓ Clean"
97+
98+
setup-signing: ## Create persistent code signing identity (one-time)
99+
@if codesign --force --sign "$(IDENTITY)" --generate-entitlement-der /dev/null 2>/dev/null; then \
100+
echo "✓ Signing identity '$(IDENTITY)' already works"; \
101+
exit 0; \
102+
fi
103+
@echo "Creating self-signed code signing certificate '$(CERT_CN)'..."
104+
@echo "This lets Full Disk Access persist across rebuilds."
105+
@echo ""
106+
@# Generate cert with code signing EKU
107+
openssl req -x509 -newkey rsa:2048 \
108+
-keyout /tmp/_im_key.pem -out /tmp/_im_cert.pem \
109+
-days 3650 -nodes \
110+
-subj "/CN=$(CERT_CN)" \
111+
-addext "extendedKeyUsage=codeSigning" \
112+
-addext "keyUsage=digitalSignature" \
113+
2>/dev/null
114+
@# Package as PKCS12 (legacy format for macOS Keychain compatibility)
115+
openssl pkcs12 -export \
116+
-out /tmp/_im.p12 \
117+
-inkey /tmp/_im_key.pem \
118+
-in /tmp/_im_cert.pem \
119+
-passout pass:_imtmp \
120+
-certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1 \
121+
2>/dev/null
122+
@# Import to login keychain
123+
security import /tmp/_im.p12 \
124+
-k ~/Library/Keychains/login.keychain-db \
125+
-P "_imtmp" \
126+
-T /usr/bin/codesign
127+
@# Allow codesign to access the key without prompting
128+
security set-key-partition-list -S apple-tool:,apple:,codesign: \
129+
-s -k "" ~/Library/Keychains/login.keychain-db >/dev/null 2>&1 || true
130+
@# Clean up temp files
131+
@rm -f /tmp/_im_key.pem /tmp/_im_cert.pem /tmp/_im.p12
132+
@echo ""
133+
@# Verify it works
134+
@if codesign --force --sign "$(IDENTITY)" $(BINARY) 2>/dev/null; then \
135+
echo "✓ Signing identity '$(IDENTITY)' created and working"; \
136+
echo " Grant FDA one more time in System Settings — it will persist across rebuilds."; \
137+
else \
138+
echo "⚠ Certificate imported but needs manual trust:"; \
139+
echo " 1. Open Keychain Access"; \
140+
echo " 2. Find '$(CERT_CN)' in login keychain"; \
141+
echo " 3. Double-click → Trust → Code Signing → Always Trust"; \
142+
fi

0 commit comments

Comments
 (0)