fix(deploy): smoke-test tt before cutover, harden launchd swap against races, warn on TCC re-grant
8a235ef5e902844947f2e8b92fbdf04314fd4d3e
Kevin Kortum <kevinkortum@me.com> · 2026-07-08 13:16
parent 56f17709
1 files changed
deploy/install-autotrack.sh +37 −0
@@ -14,11 +14,48 @@
[ -x "$TT" ] || { echo "missing $TT — run: make build helpers" >&2; exit 1; }
mkdir -p "${HOME}/.local/share/ticktock" "${HOME}/Library/LaunchAgents"
+# Prove the new binary actually runs before we touch the live agent. If this
+# fails we bail out here, with the Python daemon still installed and running.
+echo "smoke-testing ${TT} autotrack --once ..."
+if ! "$TT" autotrack --once >/dev/null 2>&1; then
+ echo "✗ ${TT} autotrack --once failed — aborting, Python daemon left running" >&2
+ exit 1
+fi
+
sed -e "s#@TT@#${TT}#g" -e "s#@HOME@#${HOME}#g" \
"$REPO/deploy/autotrack.plist.template" > "$PLIST"
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$PLIST"
+
+# bootout/bootstrap can race: bootout can return before the old job is fully
+# torn down, making bootstrap report success while the new job never reaches
+# "running". Give it a moment, then confirm and force a (re)start if needed
+# rather than silently leaving no daemon in place.
+sleep 1
+state="$(launchctl print "gui/$(id -u)/${LABEL}" 2>/dev/null | awk '/state = /{print $3; exit}')"
+if [ "$state" != "running" ]; then
+ echo " ${LABEL} not running yet (state: ${state:-unknown}) — kickstarting..." >&2
+ launchctl kickstart -k "gui/$(id -u)/${LABEL}" 2>/dev/null || true
+ sleep 1
+ state="$(launchctl print "gui/$(id -u)/${LABEL}" 2>/dev/null | awk '/state = /{print $3; exit}')"
+fi
+if [ "$state" != "running" ]; then
+ echo "✗ ${LABEL} did not reach 'running' state (state: ${state:-unknown})." >&2
+ echo " Check: launchctl print gui/$(id -u)/${LABEL}" >&2
+ echo " Rollback: see the header of this script." >&2
+ exit 1
+fi
+
echo "✓ ${LABEL} → ${TT} autotrack (${PLIST})"
echo " verify: launchctl print gui/$(id -u)/${LABEL} | grep -E 'state|program'"
echo " logs: tail -f ~/.local/share/ticktock/autotrack.err"
+echo
+echo "IMPORTANT — macOS permissions do NOT carry over from the old daemon:"
+echo " ${TT} is a new binary, so Accessibility, Automation (System Events /"
+echo " browser control), and Calendar access must be re-granted in System"
+echo " Settings → Privacy & Security for ${TT} (and its winctx helper)."
+echo " Until granted, autotrack degrades silently instead of erroring."
+echo " Trigger the permission prompts now from Terminal:"
+echo " \"$TT\" autotrack --once"
+echo " \"$TT\" autotrack --list-calendars"