▍ humdrum codex / ticktock v0.0.2
license AGPL-3.0

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"