-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathMakefile
More file actions
141 lines (129 loc) · 7.47 KB
/
Copy pathMakefile
File metadata and controls
141 lines (129 loc) · 7.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
BINARY_NAME := CheICalMCP
# Swift 6 Concurrency Guard:
# Try native build first; fall back to Swift 5 language mode if upstream
# dependencies have strict concurrency errors (swift-sdk#214).
# Remove FALLBACK_FLAGS once the upstream issue is fixed.
FALLBACK_FLAGS := $(shell swift build 2>&1 | grep -q "SendingRisksDataRace" && echo "-Xswiftc -swift-version -Xswiftc 5")
.PHONY: build release release-signed verify-release-ready verify-developer-id install install-signed clean test
# Detect drift between AppVersion.current and the latest release tag.
# Soft pre-flight: warns on drift, never aborts on the drift case (a maintainer
# doing genuine pre-release work needs AppVersion ahead of the latest tag).
# Hard-fails ONLY when Version.swift can't be parsed at all — no other behavior
# can succeed in that case anyway, since build-mcpb.sh Step 0.5 also requires
# AppVersion to be parseable. Surfaces the case where Info.plist /
# mcpb/manifest.json got bumped on main without ever cutting a tag (cf. #48).
#
# Three drift cases are reported separately so the warning is actionable:
# ahead → expected pre-release; tag v$VERSION when ready
# behind → downgrade or stale branch; do NOT tag — investigate first
# diverged (e.g. v1.7.1 vs 2.0.0-rc.1) → unstructured drift; manual review
verify-release-ready:
@SOURCE_VERSION=$$(grep -E 'static let current = "' Sources/CheICalMCP/Version.swift | sed -E 's/.*"([^"]+)".*/\1/'); \
LATEST_TAG=$$(git tag --sort=-creatordate | head -1); \
if [ -z "$$SOURCE_VERSION" ]; then \
echo "✗ Could not parse AppVersion.current from Version.swift" >&2; \
echo " This target must be run from the repo root." >&2; \
exit 1; \
fi; \
if [ -z "$$LATEST_TAG" ]; then \
echo "ℹ No git tags yet — version drift check skipped (first release?)"; \
elif [ "v$${SOURCE_VERSION}" = "$$LATEST_TAG" ]; then \
echo "ℹ AppVersion.current ($$SOURCE_VERSION) matches latest tag ($$LATEST_TAG) — no version bump needed for next release"; \
elif [ "$$(printf '%s\n%s\n' "v$${SOURCE_VERSION}" "$$LATEST_TAG" | sort -V | tail -1)" = "v$${SOURCE_VERSION}" ]; then \
echo "⚠ Pre-release drift: AppVersion.current=$$SOURCE_VERSION is AHEAD of latest tag=$$LATEST_TAG"; \
echo " Expected if you're cutting v$${SOURCE_VERSION}. Tag v$${SOURCE_VERSION} when this build ships."; \
else \
echo "⚠ DOWNGRADE drift: AppVersion.current=$$SOURCE_VERSION is BEHIND latest tag=$$LATEST_TAG"; \
echo " DO NOT tag v$${SOURCE_VERSION} — that would publish older code as the latest release."; \
echo " Likely cause: stale branch, bad merge, or accidental Version.swift revert. Investigate before continuing."; \
fi
build:
swift build $(FALLBACK_FLAGS)
# Local release build (ad-hoc signed). Use for dev iteration, NOT for distribution.
release:
swift build -c release $(FALLBACK_FLAGS)
# Distribution release: builds universal binary, signs with Developer ID,
# notarizes via xcrun notarytool, and packages into .mcpb.
# Requires Developer ID Application cert in keychain + notarytool keychain
# profile (see README "Signing & Notarization" for one-time setup).
#
# REQUIRE_CODESIGN=1 makes signing mandatory — missing DEVELOPER_ID, missing
# cert, or missing notarytool profile will fail-fast instead of silently
# producing an unsigned .mcpb. This is the canonical release-cut command;
# use plain `./scripts/build-mcpb.sh` (without the flag) for fork-friendly
# unsigned dev builds.
# Must be run from the repo root: this target invokes ./scripts/build-mcpb.sh
# via a relative path. `make -f /abs/path/Makefile release-signed` from a
# different cwd would fail to find the script.
release-signed: verify-release-ready
@echo ""
@echo "⚠ macOS 26 TCC behavior on the resulting binary remains unverified."
@echo " Manual test required before tagging v1.7.1 — see #54."
@echo ""
@: $${DEVELOPER_ID:?DEVELOPER_ID not set. See README 'Signing & Notarization' for setup.}
@: $${NOTARY_PROFILE:?NOTARY_PROFILE not set. See README 'Signing & Notarization' for setup.}
REQUIRE_CODESIGN=1 ./scripts/build-mcpb.sh
# Local dev install with ad-hoc signing. Fast iteration.
# Note: TCC dialogs do not appear on macOS 26 with ad-hoc signing —
# use `make release-signed` for testing TCC flows on macOS 26.
#
# rm -f forces a fresh inode: if any old CheICalMCP processes are still running
# (e.g. held by Claude Code MCP integrations or launchd), `cp` over the existing
# file would reuse the same inode, and the macOS kernel caches code-signature
# hashes per-inode — leading to "load code signature error 2" SIGKILL on the
# new binary. See #62 for the upgrade-trap discovery during macOS 26 testing.
install: release
rm -f ~/bin/$(BINARY_NAME)
cp .build/release/$(BINARY_NAME) ~/bin/$(BINARY_NAME)
chmod +x ~/bin/$(BINARY_NAME)
codesign --force --sign - ~/bin/$(BINARY_NAME)
@echo "Installed: ~/bin/$(BINARY_NAME) (ad-hoc signed — dev only)"
# Dev install with Developer ID signature (no notarization). Slower than
# `make install` (Developer ID round-trip vs ad-hoc), but **fast enough for
# dev iteration**: skips the notarytool submit step (1-15 min waits) that
# `make release-signed` adds for distribution.
#
# Why this target exists (#50): on macOS 26, ad-hoc signed binaries cannot
# trigger TCC permission dialogs (Calendar / Reminders). Maintainers
# dogfooding `--setup` flows on their own machine need a Developer ID
# signature to test the macOS 26 TCC interaction. `make install` (ad-hoc)
# is too weak; `make release-signed` (full notarization, distribution-ready)
# is overkill — this target is the middle ground.
#
# Trade-offs vs release-signed:
# - SIGNED with Developer ID + hardened runtime → TCC dialogs work
# - NOT notarized → Gatekeeper online-checks on first launch may stutter
# (one-time network hit), but the binary IS launchable on the dev machine
# - Suitable for: maintainer dogfood, TCC flow verification on macOS 26
# - NOT suitable for: distribution to other users (notarization required)
#
# Pre-condition: same as `release-signed` — `DEVELOPER_ID` exported in env.
# `NOTARY_PROFILE` is NOT required (no notarization step).
#
# `verify-developer-id` runs FIRST (before `release`) so a missing
# DEVELOPER_ID env var aborts BEFORE the ~30-second `swift build -c release`
# (#50 verify Codex caught: original placement let the DEVELOPER_ID check
# fire only after build, wasting the user's iteration time on a guaranteed
# failure).
# Internal helper target: hard-fail early if DEVELOPER_ID env var is missing.
# Listed in .PHONY since it produces no file. Used as a left-most dep on
# `install-signed` so make resolves it before the `release` build dep.
verify-developer-id:
@: $${DEVELOPER_ID:?DEVELOPER_ID not set. See README 'Signing & Notarization' for setup. \
For dev install on macOS 26 you only need DEVELOPER_ID — NOTARY_PROFILE is unused here.}
install-signed: verify-developer-id release
rm -f ~/bin/$(BINARY_NAME)
cp .build/release/$(BINARY_NAME) ~/bin/$(BINARY_NAME)
chmod +x ~/bin/$(BINARY_NAME)
codesign --force \
--sign "$$DEVELOPER_ID" \
--options runtime \
--entitlements Sources/CheICalMCP/Entitlements.plist \
~/bin/$(BINARY_NAME)
@echo "Installed: ~/bin/$(BINARY_NAME) (Developer ID signed, NOT notarized — dev only)"
@echo "ℹ macOS 26 TCC dialogs WILL trigger because the binary has Developer ID + hardened runtime."
@echo "ℹ For distribution to other users, use 'make release-signed' instead (adds notarization)."
test:
swift test $(FALLBACK_FLAGS)
clean:
swift package clean