Skip to content
C.W.K.
Stream
Lesson 04 of 06 · published

Build Once, Ship Verified Copies to Every Mac

~17 min · proof-and-fleet, deployment, fleet, rsync, codesign

Level 0Bundle Opener
0 XP0/81 lessons0/17 achievements
0/100 XP to next level100 XP to go0% complete
"Pushing yesterday's binary to eight Macs is the worst outcome this tool can produce."

Why a Copy Is Enough

The family's Macs do not each build their own apps. One Mac builds, signs and installs; the others receive copies of that signed bundle. This works because of the bundle-signing track. Every app is signed with the one self-signed identity, so its designated requirement names the certificate rather than a hash, and a grant approved on a peer keeps matching every later copy. Peers need no Swift toolchain and no keychain import: codesign --verify checks the signature against the certificate embedded in the bundle, and the private key is only needed to sign. The deploy script therefore never builds anything. If it finds source newer than the installed bundle on the building Mac, it refuses, because the only thing worse than no deploy is a confident deploy of the wrong binary everywhere.

Preflight Before the First Peer

Everything that can disqualify a deploy is checked before any peer is touched: the installed bundle is newer than its sources, its signature verifies strictly, its requirement is certificate-pinned, and the LaunchAgent plist it declares exists. A bad build must fail on the building Mac, not after it has reached Mac number one while Mac number eight is still unverified.

On Each Peer: Stage, Verify, Stop, Swap, Verify

The bundle is copied to a hidden, space-free staging name beside /Applications. The space matters: the peers' openrsync split a remote destination with a space into two arguments and died. So the fleet deploy pins Homebrew's rsync, while the TestFlight export pins Apple's, for the opposite measured reason. Leftover staging from an interrupted run is cleared with find, not a shell glob, because in the peers' zsh an unmatched glob is a fatal error for the whole command, which silently skipped the cleanup in the normal case.

On the peer the staged bundle is verified, then the running app is stopped according to its quit policy. An ambient utility can be signalled. A document app must not be: SIGTERM never reaches applicationShouldTerminate, so the prose editor's refusal to quit during Korean input composition, a peer sync or an export, and its save-or-keep-draft decision, would all be skipped. It gets a polite quit through a direct Apple Event, which works from ssh, with a deadline, and a refusal becomes a skip. The terminal app is skipped while it is open, because a quit can end running jobs. Then the old bundle is moved aside, the new one moved in, verified at its final path, and the old one restored if anything failed.

Report What Is True Per App, Per Mac

Each app on each peer ends in one line. A LaunchAgent is loaded when someone is logged in at that Mac and staged when no one is. Permission state is read per service from the database that holds it: never asked, asked and not approved (which will not prompt again), granted, or unreadable. Manual steps are listed only for apps that actually need a grant, or the report sends the owner to six Macs to approve a prompt that will never appear, and "relaunch needed" is listed only when the deploy quit a running app that nothing else will bring back.

Code

On the building Mac: preflight, then one verified copy per peer·bash
#!/bin/bash
# Office builds once; peers receive copies. This script never builds.
set -euo pipefail
app=Spark quit_policy=signal
installed="/Applications/$app.app"
peers=(${PEERS:-air mini})

# Preflight, before any peer is touched: a stale build must never reach Mac number one.
stale=$(find Sources Resources Package.swift -newer "$installed" -print -quit 2>/dev/null || true)
[[ -z "$stale" ]] || { echo "HARD_FAIL $stale is newer than $installed: build and install here first"; exit 1; }
codesign --verify --deep --strict "$installed"
codesign -d -r- "$installed" 2>&1 | grep -q 'certificate leaf' \
  || { echo "HARD_FAIL $installed is not signed with the shared identity; its grants would not survive a copy"; exit 1; }

for peer in "${peers[@]}"; do
  staged="/Applications/.spark-deploy.$$"                     # no spaces: peer rsync splits on them
  if ! rsync -aH --delete -e "ssh -o BatchMode=yes" "$installed/" "$peer:$staged/"; then
    echo "$app@$peer FAILED (rsync)"; continue
  fi
  result=$(ssh -o BatchMode=yes "$peer" "bash -s -- '$app' '$staged' '$quit_policy'" < peer-install.sh) \
    || true
  echo "$app@$peer $result"
done
peer-install.sh: verify, stop by policy, swap, verify again, roll back·bash
#!/bin/bash
# Runs ON the peer:  ssh peer 'bash -s -- Spark /Applications/.spark-deploy.123 polite' < peer-install.sh
# Verify what arrived, stop the running copy the right way, swap, verify again, roll back on failure.
set -euo pipefail
app=$1 staged=$2 quit_policy=$3            # quit_policy: polite (document app) | signal (utility)
root=${APPLICATIONS:-/Applications}
dest="$root/$app.app" previous="$root/.$app.previous.$$"
info="$staged/Contents/Info.plist"
exe=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$info")
bundle_id=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$info")

xattr -cr "$staged"                                          # copied metadata breaks signatures
codesign --verify --deep --strict "$staged" || { echo "FAILED app=$app reason=staged-signature"; exit 1; }

if pgrep -x "$exe" >/dev/null; then
  if [[ $quit_policy == polite ]]; then
    # A signal never reaches applicationShouldTerminate, so unsaved work would be skipped.
    # A direct Apple Event works from ssh; it can block on a dialog, so it gets a deadline.
    osascript -e "tell application id \"$bundle_id\" to quit" >/dev/null 2>&1 &
    for _ in $(seq 1 100); do pgrep -x "$exe" >/dev/null || break; sleep 0.1; done
    if pgrep -x "$exe" >/dev/null; then echo "SKIP app=$app reason=quit-refused (unsaved work?)"; exit 0; fi
  else
    pkill -x "$exe" || true
  fi
fi

[[ -d "$dest" ]] && mv "$dest" "$previous"
if mv "$staged" "$dest" && codesign --verify --deep --strict "$dest" 2>/dev/null; then
  rm -rf "$previous"
  echo "OK app=$app build=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$dest/Contents/Info.plist" 2>/dev/null || echo unknown)"
else
  rm -rf "$dest"
  [[ -d "$previous" ]] && mv "$previous" "$dest"
  echo "ROLLED_BACK app=$app reason=installed-signature"
  exit 1
fi

External links

Exercise

Run peer-install.sh locally with APPLICATIONS pointed at a scratch directory: install build 1, stage build 2 and install it, then stage a copy whose executable you append a byte to and confirm it is refused before anything is swapped. Next, add a check to ship.sh that fails when the building Mac's installed bundle is older than its sources, and prove it by touching a source file. Finally, write the one-line result format your deploy prints per app and peer, including agent and permission state.
Hint
Appending to the executable breaks the signature's code hash, so codesign --verify --deep --strict refuses the staged copy with a strict validation error. find <paths> -newer <bundle> -print -quit prints the first newer path and stops.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.