121 lines
6.7 KiB
Bash
Executable File
121 lines
6.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# An installed ElyMark updates from a served release, asking first.
|
|
set -euo pipefail
|
|
|
|
root=$(cd "$(dirname "$0")/../.." && pwd)
|
|
[ "$(uname)" = Darwin ] || { echo "SKIP: macOS only"; exit 0; }
|
|
work=$(mktemp -d)
|
|
cleanup() {
|
|
pkill -f "$work/Applications/ElyMark.app" 2>/dev/null || true
|
|
if [ -n "${server:-}" ]; then
|
|
kill "$server" 2>/dev/null || true
|
|
wait "$server" 2>/dev/null || true
|
|
fi
|
|
rm -rf "$work"
|
|
}
|
|
trap cleanup EXIT
|
|
fail() { echo "FAIL: $*" >&2; exit 1; }
|
|
|
|
ELY_DIST="$work/dist" "$root/packaging/macos/bundle.sh" ely-mark debug >"$work/bundle" 2>&1 || fail "bundle: $(tail -5 "$work/bundle")"
|
|
swiftc -O -o "$work/ax-press" "$root/tests/scripts/ax-press.swift"
|
|
mkdir -p "$work/Applications" "$work/new" "$work/serve"
|
|
ditto "$work/dist/ElyMark.app" "$work/Applications/ElyMark.app"
|
|
# The new release: the same app, marked.
|
|
ditto "$work/dist/ElyMark.app" "$work/new/ElyMark.app"
|
|
touch "$work/new/ElyMark.app/Contents/Resources/ely-update-marker"
|
|
codesign --force --deep --sign - "$work/new/ElyMark.app" 2>/dev/null
|
|
ln -s /Applications "$work/new/Applications"
|
|
hdiutil create -quiet -volname ElyMark -srcfolder "$work/new" -format UDZO "$work/serve/ElyMark.dmg"
|
|
port=$(python3 -c 'import socket; s = socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1])')
|
|
sha=$(shasum -a 256 "$work/serve/ElyMark.dmg" | cut -d' ' -f1)
|
|
key="macos-$(uname -m | sed s/arm64/aarch64/)"
|
|
printf '{"version": "9.9.9", "assets": {"%s": {"url": "http://127.0.0.1:%s/ElyMark.dmg", "sha256": "%s"}}}\n' "$key" "$port" "$sha" >"$work/serve/ely-mark.json"
|
|
python3 -m http.server "$port" --bind 127.0.0.1 --directory "$work/serve" >"$work/served" 2>&1 &
|
|
server=$!
|
|
mkdir -p "$work/home"
|
|
# The installer's open: says whether the failure note came first.
|
|
mkdir -p "$work/bin"
|
|
printf '#!/bin/sh\n[ -e "%s" ] && echo before >>"%s" || echo after >>"%s"\nexec /usr/bin/open "$@"\n' \
|
|
"$work/home/Library/Application Support/ElyOffice/ely-mark/updates/failed.txt" "$work/opened" "$work/opened" >"$work/bin/open"
|
|
chmod +x "$work/bin/open"
|
|
start() {
|
|
PATH="$work/bin:$PATH" HOME="$work/home" ELY_LOG=info ELY_LOCALE=en ELY_NO_DIALOG=1 ELY_UPDATE_MANIFEST="http://127.0.0.1:$port/ely-mark.json" \
|
|
"$work/Applications/ElyMark.app/Contents/MacOS/ely-mark" >"$work/out" 2>&1 &
|
|
pid=$!
|
|
}
|
|
start
|
|
[ "$("$work/ax-press" "$pid" none)" != untrusted ] || fail "needs Accessibility permission"
|
|
# Off by default: nothing is asked at launch.
|
|
for _ in $(seq 60); do grep -q "no check at launch" "$work/out" && break; sleep 0.5; done
|
|
grep -q "no check at launch" "$work/out" || fail "the launch check ran: $(grep update "$work/out" | tail -3)"
|
|
for _ in $(seq 60); do grep -q 'mark: shown as' "$work/out" && break; sleep 0.5; done
|
|
# A new untitled page: it takes keys, autosave skips it.
|
|
osascript -e "tell application \"System Events\" to set frontmost of (first process whose unix id is $pid) to true" -e 'delay 1' \
|
|
-e 'tell application "System Events" to keystroke "n" using command down' -e 'delay 2' \
|
|
-e 'tell application "System Events" to keystroke "unsaved"' >/dev/null
|
|
sleep 3
|
|
! grep -q GET "$work/served" || fail "asked without being told: $(cat "$work/served")"
|
|
press() {
|
|
for _ in $(seq 60); do
|
|
[ "$("$work/ax-press" "$pid" "$1")" = pressed ] && return 0
|
|
sleep 0.5
|
|
done
|
|
fail "could not press $1: $(grep update "$work/out" | tail -3)"
|
|
}
|
|
press "Check for Updates…"
|
|
for _ in $(seq 60); do grep -q "offers 9.9.9" "$work/out" && break; sleep 0.5; done
|
|
grep -q "offers 9.9.9" "$work/out" || fail "no offer: $(grep update "$work/out" | tail -3)"
|
|
press Download
|
|
press "Restart to update"
|
|
# Unsaved text: the question comes first; cancel installs nothing.
|
|
press Cancel
|
|
sleep 3
|
|
kill -0 "$pid" 2>/dev/null || fail "a cancelled quit still quit"
|
|
[ ! -e "$work/Applications/ElyMark.app/Contents/Resources/ely-update-marker" ] || fail "installed before the question"
|
|
# A plain quit after the cancel installs nothing either.
|
|
osascript -e 'tell application "System Events" to keystroke "q" using command down' >/dev/null
|
|
press "Don't Save"
|
|
for _ in $(seq 60); do kill -0 "$pid" 2>/dev/null || break; sleep 0.5; done
|
|
! kill -0 "$pid" 2>/dev/null || fail "the app did not quit"
|
|
sleep 8
|
|
[ ! -e "$work/Applications/ElyMark.app/Contents/Resources/ely-update-marker" ] || fail "a plain quit installed the update"
|
|
! pgrep -f "$(basename "$work")/Applications/ElyMark.app/Contents/MacOS/ely-mark" >/dev/null || fail "a plain quit reopened the app"
|
|
# Relaunched: check, download again, and Restart installs.
|
|
start
|
|
for _ in $(seq 60); do grep -q 'mark: shown as' "$work/out" && break; sleep 0.5; done
|
|
press "Check for Updates…"
|
|
press Download
|
|
press "Restart to update"
|
|
for _ in $(seq 60); do kill -0 "$pid" 2>/dev/null || break; sleep 0.5; done
|
|
! kill -0 "$pid" 2>/dev/null || fail "the old app did not quit"
|
|
# The installer copies once the old app has gone.
|
|
for _ in $(seq 120); do [ -e "$work/Applications/ElyMark.app/Contents/Resources/ely-update-marker" ] && break; sleep 0.5; done
|
|
[ -e "$work/Applications/ElyMark.app/Contents/Resources/ely-update-marker" ] || fail "the new app is not in place"
|
|
# The new app itself, by the path the system resolved.
|
|
binary="$(basename "$work")/Applications/ElyMark.app/Contents/MacOS/ely-mark"
|
|
for _ in $(seq 40); do pgrep -f "$binary" >/dev/null && break; sleep 0.5; done
|
|
fresh=$(pgrep -f "$binary" | head -n 1 || true)
|
|
[ -n "$fresh" ] && [ "$fresh" != "$pid" ] || fail "the new app did not open"
|
|
[ "$(ls -A "$work/Applications")" = ElyMark.app ] || fail "copies left: $(ls -A "$work/Applications")"
|
|
# A broken release: its note is written before the reopen.
|
|
pkill -f "$binary" || true
|
|
for _ in $(seq 20); do pgrep -f "$binary" >/dev/null || break; sleep 0.5; done
|
|
mkdir -p "$work/broken/Other.app"
|
|
hdiutil create -quiet -volname Broken -srcfolder "$work/broken" -format UDZO "$work/serve/Broken.dmg"
|
|
sha=$(shasum -a 256 "$work/serve/Broken.dmg" | cut -d' ' -f1)
|
|
rm -f "$work/opened"
|
|
printf '{"version": "9.9.10", "assets": {"%s": {"url": "http://127.0.0.1:%s/Broken.dmg", "sha256": "%s"}}}\n' "$key" "$port" "$sha" >"$work/serve/ely-mark.json"
|
|
start
|
|
for _ in $(seq 60); do grep -q 'mark: shown as' "$work/out" && break; sleep 0.5; done
|
|
press "Check for Updates…"
|
|
press Download
|
|
press "Restart to update"
|
|
note="$work/home/Library/Application Support/ElyOffice/ely-mark/updates/failed.txt"
|
|
# Not the old process, which this script started and reaps.
|
|
reopened() { pgrep -f "$binary" | grep -vx "$pid" | head -n 1 || true; }
|
|
for _ in $(seq 60); do [ -n "$(reopened)" ] && [ -e "$note" ] && break; sleep 0.5; done
|
|
grep -q "holds no ElyMark.app" "$note" || fail "no failure note: $(cat "$note" 2>/dev/null)"
|
|
[ -n "$(reopened)" ] || fail "the old app did not reopen"
|
|
[ "$(head -n 1 "$work/opened")" = before ] || fail "the app reopened before the note was written"
|
|
echo "ok: ElyMark downloads, installs and reopens its update"
|