91 lines
4.3 KiB
Bash
91 lines
4.3 KiB
Bash
#!/bin/bash
|
|||
|
|
#
|
||
|
|
# install-latest.sh — quit, replace, relaunch. One step, repeatable.
|
||
|
|
#
|
||
|
|
# Doing this by hand meant three builds in a row were staged and never
|
||
|
|
# installed, so the app under test was two versions behind what the log said
|
||
|
|
# was fixed. That is worse than no build at all.
|
||
|
|
#
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||
|
|
REL="$ROOT/Releases"
|
||
|
|
DIST="$ROOT/app/dist"
|
||
|
|
APPS="/Applications"
|
||
|
|
NAME="Church Translate.app"
|
||
|
|
|
||
|
|
# Both places, newest first. `npm run dist` writes to app/dist and only a
|
||
|
|
# release copy lands in Releases/ — looking in one of them installed a build
|
||
|
|
# that was two versions behind the one just compiled, and said "3.8.0 -> 3.8.0"
|
||
|
|
# while doing it. That is the exact failure this script was written to stop.
|
||
|
|
DMG="$(ls -t "$REL"/*.dmg "$DIST"/*.dmg 2>/dev/null | head -1)"
|
||
|
|
[ -n "$DMG" ] || { echo "No .dmg in $REL or $DIST"; exit 1; }
|
||
|
|
echo "installing: $(basename "$DMG")"
|
||
|
|
|
||
|
|
# Stale volumes from earlier installs keep the old version's name around and
|
||
|
|
# make it genuinely hard to tell what is mounted.
|
||
|
|
for v in /Volumes/*Church\ Translate*; do
|
||
|
|
[ -d "$v" ] && { echo " ejecting stale $v"; hdiutil detach "$v" -quiet -force || true; }
|
||
|
|
done
|
||
|
|
|
||
|
|
# NEVER install over a live service. main.js deliberately blocks its own window
|
||
|
|
# close with a confirmation dialog while translation is live, so that nobody
|
||
|
|
# takes the captions down mid-sermon — and this script's force-terminate would
|
||
|
|
# walk straight through that protection. Ask the server instead.
|
||
|
|
if pgrep -f "$NAME/Contents/MacOS" >/dev/null; then
|
||
|
|
CFG="$HOME/Library/Application Support/Church Translate/data/config.json"
|
||
|
|
KEY="$(/usr/bin/python3 -c "import json,sys;print(json.load(open(sys.argv[1])).get('controlToken',''))" "$CFG" 2>/dev/null || true)"
|
||
|
|
if [ -n "$KEY" ]; then
|
||
|
|
ST="$(curl -s --max-time 4 "http://127.0.0.1:3100/api/status.txt?key=$KEY" 2>/dev/null || true)"
|
||
|
|
if [ "$ST" = "live" ]; then
|
||
|
|
echo "REFUSING TO INSTALL: translation is LIVE right now."
|
||
|
|
echo "Stop the service first — installing would cut the captions for everyone listening."
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
[ -n "$ST" ] && echo " service is '$ST' (not live) — safe to replace"
|
||
|
|
fi
|
||
|
|
echo " quitting the running app"
|
||
|
|
osascript -e 'tell application "Church Translate" to quit' 2>/dev/null || true
|
||
|
|
for _ in $(seq 1 50); do # 25s: it shuts three model children down first
|
||
|
|
pgrep -f "$NAME/Contents/MacOS" >/dev/null || break
|
||
|
|
sleep 0.5
|
||
|
|
done
|
||
|
|
# The forked children (server, mt, tts) exit with the parent; if the parent
|
||
|
|
# is wedged, take it down rather than installing under a live process.
|
||
|
|
pgrep -f "$NAME/Contents/MacOS" >/dev/null && {
|
||
|
|
echo " did not quit in 25s — terminating"
|
||
|
|
pkill -f "$NAME/Contents/MacOS" || true
|
||
|
|
sleep 2
|
||
|
|
}
|
||
|
|
fi
|
||
|
|
|
||
|
|
MNT="$(mktemp -d /tmp/ct-mnt.XXXXXX)"
|
||
|
|
hdiutil attach "$DMG" -nobrowse -quiet -mountpoint "$MNT"
|
||
|
|
trap 'hdiutil detach "$MNT" -quiet -force >/dev/null 2>&1 || true; rmdir "$MNT" 2>/dev/null || true' EXIT
|
||
|
|
|
||
|
|
# Prove the replacement exists BEFORE removing what works.
|
||
|
|
[ -d "$MNT/$NAME" ] || { echo "no $NAME inside the dmg"; exit 1; }
|
||
|
|
NEW="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$MNT/$NAME/Contents/Info.plist")"
|
||
|
|
NEWB="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$MNT/$NAME/Contents/Info.plist" 2>/dev/null || echo '?')"
|
||
|
|
OLD="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$APPS/$NAME/Contents/Info.plist" 2>/dev/null || echo 'none')"
|
||
|
|
OLDB="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$APPS/$NAME/Contents/Info.plist" 2>/dev/null || echo '0')"
|
||
|
|
echo " $OLD (build $OLDB) -> $NEW (build $NEWB)"
|
||
|
|
|
||
|
|
# Installing the same build over itself looks identical to a successful
|
||
|
|
# install and is how a fix gets reported as shipped when it is not on disk.
|
||
|
|
if [ "$OLD" = "$NEW" ] && [ "$OLDB" = "$NEWB" ]; then
|
||
|
|
echo " NOTE: that is the build already installed — nothing changed."
|
||
|
|
echo " Did you forget to bump version/buildNumber, or to run npm run dist?"
|
||
|
|
fi
|
||
|
|
|
||
|
|
rm -rf "$APPS/$NAME"
|
||
|
|
ditto "$MNT/$NAME" "$APPS/$NAME"
|
||
|
|
|
||
|
|
# The build is deliberately unsigned, so a fresh copy off a disk image carries
|
||
|
|
# a quarantine flag and Gatekeeper refuses to open it without a right-click.
|
||
|
|
# This is the user's own locally built app being installed at their request.
|
||
|
|
xattr -dr com.apple.quarantine "$APPS/$NAME" 2>/dev/null || true
|
||
|
|
|
||
|
|
open -a "$APPS/$NAME"
|
||
|
|
echo " launched"
|