Files
ElyOffice/tests/scripts/update.sh
T

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"