Share zoom preparation across both launchers and stage the helper with each driver. Use the Appearance preference bridge and verify page zoom rather than display DPR. Real Electron regressions fail with transient zoom on focus/navigation and pass with persistence. Repeated click-throughs, onboarding unit tests, E2E typecheck and lint passed. The historical onboarding timeout and full install/update matrix remain unverified.
439 lines
18 KiB
Bash
Executable File
439 lines
18 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Prove a macOS user who installed OLD via the published desktop installer
|
|
# (Hermes-Setup.dmg from the website) can reach HEAD.
|
|
#
|
|
# The macOS sibling of tests/install/windows-e2e.ps1's desktop-installer
|
|
# arm, sharing the staging trick: every git process is pointed at a local
|
|
# bare clone via url.<file://serve.git>.insteadOf in a driver-owned
|
|
# GIT_CONFIG_GLOBAL. The published dmg carries no commit pin - it installs
|
|
# whatever `main` serves - so parking serve.git's main at OLD stages the
|
|
# "user on the current release" start, and advancing it to HEAD makes an
|
|
# update available exactly the way it does for a real user.
|
|
#
|
|
# Phases (state shared via the workroot, mirroring the windows driver):
|
|
# stage bare-clone this checkout to serve.git, park main at OLD
|
|
# install download the dmg, hdiutil attach, run the installer app's
|
|
# binary DIRECTLY (env inheritance: an `open`-launched app sees
|
|
# none of our redirect env), wait for the install to land
|
|
# update advance served main to HEAD, apply ONE update method:
|
|
# open-app-update launch the installed app binary
|
|
# under Playwright, click Update now
|
|
# hermes-desktop-app-update capture `hermes desktop`'s spawn,
|
|
# launch the spec under Playwright,
|
|
# click Update now
|
|
# hermes-update CLI update from the installed venv
|
|
# installer-script[+desktop] re-run the current install one-liner
|
|
#
|
|
# Usage:
|
|
# tests/install/macos-desktop-e2e.sh --phase stage|install|update|all
|
|
# --update-method open-app-update|hermes-desktop-app-update
|
|
# [--install-ref REF] [--dmg-url URL]
|
|
#
|
|
# Requires a clean full-history checkout with release tags fetched, on a
|
|
# macOS host with a window server (the GitHub macos runners qualify).
|
|
|
|
set -euo pipefail
|
|
|
|
# One time base for every transcript in this leg: ts_prefix stamps lines
|
|
# relative to TS_BASE, so all logs share the driver's clock and a single
|
|
# playback.html offset slider aligns every file with the recording.
|
|
export TS_BASE=$SECONDS
|
|
|
|
PHASE="all"
|
|
UPDATE_METHOD=""
|
|
INSTALL_REF=""
|
|
DMG_URL="https://hermes-assets.nousresearch.com/Hermes-Setup.dmg"
|
|
PLAYWRIGHT_VERSION="1.58.2"
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--phase)
|
|
[ "$#" -ge 2 ] || { echo 'error: --phase needs a value' >&2; exit 1; }
|
|
PHASE="$2"; shift 2 ;;
|
|
--update-method)
|
|
[ "$#" -ge 2 ] || { echo 'error: --update-method needs a value' >&2; exit 1; }
|
|
UPDATE_METHOD="$2"; shift 2 ;;
|
|
--install-ref)
|
|
[ "$#" -ge 2 ] || { echo 'error: --install-ref needs a value' >&2; exit 1; }
|
|
INSTALL_REF="$2"; shift 2 ;;
|
|
--dmg-url)
|
|
[ "$#" -ge 2 ] || { echo 'error: --dmg-url needs a value' >&2; exit 1; }
|
|
DMG_URL="$2"; shift 2 ;;
|
|
-h|--help) sed -n '2,32p' "$0"; exit 0 ;;
|
|
*) echo "error: unknown argument: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
case "$UPDATE_METHOD" in
|
|
open-app-update|hermes-desktop-app-update|hermes-update|installer-script|installer-script+desktop) ;;
|
|
*) echo "error: unsupported --update-method '$UPDATE_METHOD'" >&2; exit 1 ;;
|
|
esac
|
|
[ "$(uname -s)" = "Darwin" ] || { echo "error: this driver runs on macOS only" >&2; exit 1; }
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
REPO_URL_SSH="git@github.com:NousResearch/hermes-agent.git"
|
|
REPO_URL_HTTPS="https://github.com/NousResearch/hermes-agent.git"
|
|
ASSETS="$REPO_ROOT/tests/install/e2e-assets"
|
|
|
|
WORK_ROOT="${HERMES_E2E_WORKROOT:-${RUNNER_TEMP:-${TMPDIR:-/tmp}}/hermes-macos-desktop-e2e}"
|
|
LOG_DIR="${HERMES_E2E_LOG_DIR:-$WORK_ROOT/logs}"
|
|
SERVE_REPO="$WORK_ROOT/serve.git"
|
|
STATE="$WORK_ROOT/shas.env"
|
|
export HOME_SANDBOX="$WORK_ROOT/home"
|
|
|
|
step() { printf '\n=== %s ===\n' "$*"; }
|
|
ok() { printf ' OK %s\n' "$*"; }
|
|
fail() { printf 'E2E ASSERTION FAILED: %s\n' "$*" >&2; exit 1; }
|
|
# shellcheck source=../e2e-assets/ts-prefix.sh
|
|
source "$(dirname "$0")/e2e-assets/ts-prefix.sh" 2>/dev/null || ts_prefix() { cat; }
|
|
log_group() {
|
|
printf '::group::%s\n' "$1"
|
|
cat "$2"
|
|
printf '::endgroup::\n'
|
|
}
|
|
|
|
# Every phase runs in its own process (separate CI steps), so the redirect
|
|
# env is re-established here, not inherited.
|
|
arm_redirect() {
|
|
# --- the git URL redirect -----------------------------------------------------
|
|
|
|
# we redirect to our own repo so we can play around with what commit hermes thinks we're on.
|
|
# A driver-owned global gitconfig, NOT GIT_CONFIG_COUNT/KEY_n/VALUE_n env
|
|
# config: install.sh sets those itself and would clobber ours.
|
|
actual_git_url="$(git -C "$REPO_ROOT" remote get-url origin)"
|
|
GIT_CFG="$WORK_ROOT/gitconfig"
|
|
cat > "$GIT_CFG" <<EOF
|
|
[url "file://$SERVE_REPO"]
|
|
insteadOf = $actual_git_url
|
|
insteadOf = $REPO_URL_HTTPS
|
|
insteadOf = $REPO_URL_SSH
|
|
EOF
|
|
export GIT_CONFIG_GLOBAL="$GIT_CFG"
|
|
|
|
# check it worked
|
|
expected_git_url="file://$SERVE_REPO"
|
|
actual_git_url="$(git -C "$REPO_ROOT" remote get-url origin)"
|
|
if [[ "$actual_git_url" != "$expected_git_url" ]]; then
|
|
fail "failed git remote get-url shim: origin resolves to '$actual_git_url', expected '$expected_git_url'"
|
|
fi
|
|
ok "git URL redirect via GIT_CONFIG_GLOBAL=$GIT_CFG"
|
|
|
|
|
|
# shim git and make 'git remote get-url origin' report the actual HA upstream
|
|
|
|
# insteadOf is transparent for transport but `git remote get-url origin` gives you the
|
|
# replacement, so _get_origin_url() sees file://$SERVE_REPO and _is_fork() would return true.
|
|
# we check for the arguments "remote get-url origin" in order in any position
|
|
# to allow for e.g. -c with some config being passed.
|
|
# if we didn't do this, we'd need the .skip_upstream_prompt file to prevent a hang in headless,"add the
|
|
# official repo as upstream?" prompt would hang a headless run. But we don't anymore :D
|
|
REAL_GIT="$(command -v git)"
|
|
REAL_GIT_QUOTED="$(printf '%q' "$REAL_GIT")"
|
|
SHIM_DIR="$WORK_ROOT/shim"
|
|
mkdir -p "$SHIM_DIR"
|
|
cat > "$SHIM_DIR/git" <<EOF
|
|
#!/usr/bin/env bash
|
|
prev2=""
|
|
prev1=""
|
|
for arg in "\$@"; do
|
|
if [ "\$prev2" = "remote" ] && [ "\$prev1" = "get-url" ] && [ "\$arg" = "origin" ]; then
|
|
echo "$REPO_URL_HTTPS"
|
|
exit 0
|
|
fi
|
|
prev2="\$prev1"
|
|
prev1="\$arg"
|
|
done
|
|
exec "$REAL_GIT_QUOTED" "\$@"
|
|
EOF
|
|
chmod +x "$SHIM_DIR/git"
|
|
export PATH="$SHIM_DIR:$PATH"
|
|
|
|
# check it worked
|
|
observed_git_url="$(git -C "$REPO_ROOT" remote get-url origin)"
|
|
if [[ "$observed_git_url" != "$REPO_URL_HTTPS" ]]; then
|
|
fail "failed git remote get-url shim: origin resolves to '$observed_git_url', expected '$REPO_URL_HTTPS'"
|
|
fi
|
|
ok "git remote get-url shim: $SHIM_DIR/git -> $REAL_GIT (origin reports $REPO_URL_HTTPS)"
|
|
|
|
# -------
|
|
export HOME="$HOME_SANDBOX"
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
export HERMES_HOME="$HOME/.hermes"
|
|
export INSTALL_DIR="$HERMES_HOME/hermes-agent"
|
|
}
|
|
|
|
phase_stage() {
|
|
step "staging serve.git (main -> OLD)"
|
|
[ -z "$(git -C "$REPO_ROOT" status --porcelain -uno)" ] \
|
|
|| fail "checkout has uncommitted tracked changes; the staged clone must be a reviewable commit"
|
|
|
|
rm -rf "$WORK_ROOT"
|
|
mkdir -p "$WORK_ROOT" "$LOG_DIR" "$HOME_SANDBOX/.local/bin"
|
|
|
|
local old_ref="$INSTALL_REF"
|
|
if [ -z "$old_ref" ] || [ "$old_ref" = "auto" ]; then
|
|
old_ref="$(git -C "$REPO_ROOT" tag --list 'v[0-9]*' --sort=-creatordate | head -1)"
|
|
[ -n "$old_ref" ] || fail "no release tags in the checkout to use as OLD"
|
|
fi
|
|
local old_sha head_sha
|
|
old_sha="$(git -C "$REPO_ROOT" rev-parse "${old_ref}^{commit}")"
|
|
head_sha="$(git -C "$REPO_ROOT" rev-parse HEAD)"
|
|
[ "$old_sha" != "$head_sha" ] || fail "OLD ($old_ref) IS HEAD; no update would be available"
|
|
|
|
git clone --bare --quiet "$REPO_ROOT" "$SERVE_REPO"
|
|
git -C "$SERVE_REPO" update-ref refs/heads/main "$old_sha"
|
|
git -C "$SERVE_REPO" symbolic-ref HEAD refs/heads/main
|
|
git -C "$SERVE_REPO" config uploadpack.allowAnySHA1InWant true
|
|
|
|
arm_redirect
|
|
mkdir -p "$HERMES_HOME"
|
|
touch "$HERMES_HOME/.skip_upstream_prompt"
|
|
|
|
printf 'OLD_SHA=%s\nOLD_REF=%s\nHEAD_SHA=%s\n' "$old_sha" "$old_ref" "$head_sha" > "$STATE"
|
|
ok "serve.git main = $old_sha ($old_ref), update target $head_sha"
|
|
}
|
|
|
|
find_installed_app() {
|
|
# The bootstrap installs the packaged app; look where the product puts it
|
|
# (the checkout's release dir), plus /Applications for a copied bundle.
|
|
local cand
|
|
for cand in \
|
|
"$INSTALL_DIR/apps/desktop/release/mac-arm64/Hermes.app" \
|
|
"$INSTALL_DIR/apps/desktop/release/mac/Hermes.app" \
|
|
"/Applications/Hermes.app"; do
|
|
[ -d "$cand" ] && { printf '%s' "$cand"; return 0; }
|
|
done
|
|
return 1
|
|
}
|
|
|
|
phase_install() {
|
|
# shellcheck disable=SC1090
|
|
. "$STATE"
|
|
arm_redirect
|
|
step "installing OLD ($OLD_REF) via the published Hermes-Setup.dmg"
|
|
|
|
local dmg="$WORK_ROOT/Hermes-Setup.dmg"
|
|
[ -f "$dmg" ] || curl -fsSL -o "$dmg" "$DMG_URL"
|
|
[ "$(stat -f%z "$dmg")" -gt 1000000 ] || fail "dmg download too small: $(stat -f%z "$dmg") bytes"
|
|
# curl'd files carry no quarantine attr, but belt and braces on a runner.
|
|
xattr -dr com.apple.quarantine "$dmg" 2>/dev/null || true
|
|
|
|
local mount
|
|
mount="$(hdiutil attach -nobrowse -readonly "$dmg" | awk -F'\t' '/\/Volumes\//{print $NF; exit}')"
|
|
[ -n "$mount" ] || fail "hdiutil attach produced no mount point"
|
|
ok "dmg mounted at $mount"
|
|
|
|
local app_bin=""
|
|
local app
|
|
app="$(find "$mount" -maxdepth 1 -name '*.app' | head -1)"
|
|
[ -n "$app" ] || { hdiutil detach "$mount" >/dev/null 2>&1 || true; fail "no .app inside the dmg"; }
|
|
app_bin="$(find "$app/Contents/MacOS" -type f -perm +111 | head -1)"
|
|
[ -n "$app_bin" ] || fail "no executable inside $app/Contents/MacOS"
|
|
|
|
# The Setup app is Tauri (Rust + system webview): Playwright/Electron
|
|
# attach never works, and run bare it waits forever on its setup-choice
|
|
# screen. Launch it in the background with our env (direct exec, not
|
|
# `open`: launchd inherits NONE of the redirect env) and drive the
|
|
# "Install Hermes" button with native input.
|
|
local rc=0
|
|
bash "$ASSETS/drive-dmg-install.sh" \
|
|
--app-bin "$app_bin" \
|
|
--install-dir "$INSTALL_DIR" \
|
|
--proof-dir "$LOG_DIR" 2>&1 \
|
|
| ts_prefix > "$LOG_DIR/bootstrap-install.log" || rc=$?
|
|
log_group "Hermes-Setup (dmg bootstrap) transcript" "$LOG_DIR/bootstrap-install.log"
|
|
hdiutil detach "$mount" >/dev/null 2>&1 || true
|
|
[ "$rc" -eq 0 ] || fail "dmg bootstrap exited $rc; transcript above"
|
|
|
|
[ -d "$INSTALL_DIR/.git" ] || fail "no checkout landed at $INSTALL_DIR"
|
|
local got
|
|
got="$(git -C "$INSTALL_DIR" rev-parse HEAD)"
|
|
[ "$got" = "$OLD_SHA" ] || fail "installed checkout is $got, expected OLD ($OLD_SHA)"
|
|
ok "checkout is OLD ($OLD_SHA)"
|
|
local hermes="$INSTALL_DIR/venv/bin/hermes"
|
|
[ -x "$hermes" ] || fail "no hermes console script at $hermes"
|
|
"$hermes" --version 2>&1 | ts_prefix > "$LOG_DIR/version-old.log" || fail "hermes --version failed after install"
|
|
ok "hermes --version works: $(head -c 120 "$LOG_DIR/version-old.log" | tr -d '\n')"
|
|
find_installed_app >/dev/null || fail "no installed Hermes.app after the dmg bootstrap"
|
|
ok "installed app: $(find_installed_app)"
|
|
}
|
|
|
|
ensure_playwright() {
|
|
# Install the driver's OWN pinned @playwright/test into a scratch dir
|
|
# (never the installed tree's copy). Idempotent across phases.
|
|
local pw_dir="$WORK_ROOT/playwright"
|
|
[ -d "$pw_dir/node_modules/@playwright/test" ] && { printf '%s' "$pw_dir"; return 0; }
|
|
mkdir -p "$pw_dir"
|
|
(cd "$pw_dir" && npm install --no-save --no-audit --no-fund \
|
|
"@playwright/test@$PLAYWRIGHT_VERSION" 2>&1 | ts_prefix > "$LOG_DIR/playwright-install.log") \
|
|
|| { log_group "playwright install transcript" "$LOG_DIR/playwright-install.log"; fail "playwright install failed"; }
|
|
printf '%s' "$pw_dir"
|
|
}
|
|
|
|
installer_supports() {
|
|
# $1: ref; $2: flag. Installer flags must match the installer being run,
|
|
# not this checkout's: older releases reject options added later.
|
|
# Capture before grepping: a `git show | grep -q` pipe takes SIGPIPE
|
|
# under pipefail when grep exits at first match, so a supported flag
|
|
# would read as unsupported.
|
|
local text
|
|
text="$(git -C "$REPO_ROOT" show "$1:scripts/install.sh")"
|
|
grep -qF -- "$2" <<< "$text"
|
|
}
|
|
|
|
run_installer() {
|
|
# $1: ref whose scripts/install.sh to run; $2: log name; $3: "desktop" to
|
|
# opt the desktop stage in (--include-desktop). Mirrors the POSIX driver.
|
|
local script="$WORK_ROOT/install-$2.sh"
|
|
git -C "$REPO_ROOT" show "$1:scripts/install.sh" > "$script"
|
|
chmod +x "$script"
|
|
local flags=(--skip-setup)
|
|
if installer_supports "$1" "--skip-browser"; then
|
|
flags+=(--skip-browser)
|
|
fi
|
|
if [ "${3:-}" = "desktop" ]; then
|
|
installer_supports "$1" "--include-desktop" \
|
|
|| fail "ref $1 does not support --include-desktop; this leg cannot mean what it claims"
|
|
flags+=(--include-desktop)
|
|
fi
|
|
# </dev/null: EOF makes every prompt take its default.
|
|
local rc=0
|
|
bash "$script" "${flags[@]}" < /dev/null 2>&1 | ts_prefix > "$LOG_DIR/install-$2.log" || rc=$?
|
|
log_group "installer ($2) transcript" "$LOG_DIR/install-$2.log"
|
|
[ "$rc" -eq 0 ] || fail "installer ($2) exited $rc; transcript above"
|
|
}
|
|
|
|
run_playwright_update() {
|
|
# $1: spec file to launch from.
|
|
local spec="$1"
|
|
local pw_dir
|
|
pw_dir="$(ensure_playwright)"
|
|
cp "$ASSETS/launch-from-spec.mjs" "$ASSETS/window-input.cjs" "$pw_dir/"
|
|
local rc=0
|
|
(cd "$pw_dir" && node launch-from-spec.mjs \
|
|
--spec "$spec" \
|
|
--result "$HERMES_HOME/.hermes-update-result.json" \
|
|
--expect-sha "$HEAD_SHA" \
|
|
--repo-dir "$INSTALL_DIR" 2>&1 \
|
|
| ts_prefix > "$LOG_DIR/app-update.log") || rc=$?
|
|
log_group "app update (Playwright) transcript" "$LOG_DIR/app-update.log"
|
|
[ "$rc" -eq 0 ] || fail "app-driven update exited $rc; transcript above"
|
|
}
|
|
|
|
phase_update() {
|
|
# shellcheck disable=SC1090
|
|
. "$STATE"
|
|
arm_redirect
|
|
step "advancing served main to HEAD"
|
|
git -C "$SERVE_REPO" update-ref refs/heads/main "$HEAD_SHA"
|
|
ok "serve.git main = $HEAD_SHA"
|
|
|
|
step "updating via $UPDATE_METHOD"
|
|
# The app must boot configured or the onboarding overlay (a fullscreen
|
|
# div) eats every click: configure the mock inference server exactly like
|
|
# the dev:mock flow does, so the app is genuinely configured.
|
|
# shellcheck source=../install/e2e-assets/mock-provider.sh
|
|
source "$ASSETS/mock-provider.sh"
|
|
mock_start "$WORK_ROOT"
|
|
trap mock_stop EXIT
|
|
case "$UPDATE_METHOD" in
|
|
hermes-update)
|
|
# The CLI route a dmg user takes from a terminal. `--yes` reaches the
|
|
# update subcommand only in later releases; ask the installed hermes.
|
|
local hermes="$INSTALL_DIR/venv/bin/hermes"
|
|
local update_cmd=("$hermes" update)
|
|
if "$hermes" update --help 2>&1 | grep -qF -- --yes; then
|
|
update_cmd=("$hermes" update --yes)
|
|
fi
|
|
local rc=0
|
|
(cd "$INSTALL_DIR" && "${update_cmd[@]}" < /dev/null 2>&1 | ts_prefix > "$LOG_DIR/update.log") || rc=$?
|
|
log_group "hermes update transcript" "$LOG_DIR/update.log"
|
|
[ "$rc" -eq 0 ] || fail "hermes update exited $rc; transcript above"
|
|
;;
|
|
installer-script)
|
|
# A dmg user re-running today's install one-liner.
|
|
run_installer "$HEAD_SHA" head
|
|
;;
|
|
installer-script+desktop)
|
|
run_installer "$HEAD_SHA" head desktop
|
|
# The desktop stage is this leg's claim: the rebuilt app must exist.
|
|
head_app=""
|
|
for cand in \
|
|
"$INSTALL_DIR/apps/desktop/release/mac-arm64/Hermes.app" \
|
|
"$INSTALL_DIR/apps/desktop/release/mac/Hermes.app"; do
|
|
[ -d "$cand" ] && { head_app="$cand"; break; }
|
|
done
|
|
[ -n "$head_app" ] || fail "no built Hermes.app under the checkout after the +desktop update"
|
|
ok "rebuilt app present: $head_app"
|
|
;;
|
|
open-app-update)
|
|
# The installed app IS the user surface here (double-click the .app);
|
|
# hand-build the spec Playwright launches from. Env: the redirect set,
|
|
# which is exactly what the app's children (git, hermes update) need.
|
|
local app app_bin
|
|
app="$(find_installed_app)" || fail "no installed app to launch"
|
|
app_bin="$(find "$app/Contents/MacOS" -type f -perm +111 | head -1)"
|
|
python3 - "$app_bin" "$WORK_ROOT/launch-spec.json" <<'PYEOF'
|
|
import json, os, sys
|
|
spec = {
|
|
"argv": [sys.argv[1]],
|
|
"cwd": os.path.dirname(sys.argv[1]),
|
|
"env": dict(os.environ),
|
|
"matchedShape": "packaged",
|
|
}
|
|
with open(sys.argv[2], "w") as fh:
|
|
json.dump(spec, fh, indent=2)
|
|
PYEOF
|
|
run_playwright_update "$WORK_ROOT/launch-spec.json"
|
|
;;
|
|
hermes-desktop-app-update)
|
|
# The product's own launch, captured at its spawn site.
|
|
local hermes="$INSTALL_DIR/venv/bin/hermes"
|
|
local spec="$WORK_ROOT/launch-spec.json"
|
|
local rc=0
|
|
(cd "$INSTALL_DIR" && \
|
|
PYTHONPATH="$ASSETS/launch-capture${PYTHONPATH:+:$PYTHONPATH}" \
|
|
HERMES_E2E_CAPTURE_LAUNCH="$spec" \
|
|
"$hermes" desktop < /dev/null 2>&1 | ts_prefix > "$LOG_DIR/desktop-launch-capture.log") || rc=$?
|
|
log_group "hermes desktop (launch capture) transcript" "$LOG_DIR/desktop-launch-capture.log"
|
|
[ "$rc" -eq 0 ] || fail "hermes desktop exited $rc during launch capture"
|
|
[ -f "$spec.captured" ] || fail "hermes desktop exited 0 but no launch was captured"
|
|
ok "captured $(cat "$spec.captured") launch spec"
|
|
run_playwright_update "$spec"
|
|
;;
|
|
esac
|
|
|
|
local got
|
|
got="$(git -C "$INSTALL_DIR" rev-parse HEAD)"
|
|
[ "$got" = "$HEAD_SHA" ] || fail "checkout is $got, expected HEAD ($HEAD_SHA)"
|
|
ok "checkout landed on HEAD ($HEAD_SHA)"
|
|
|
|
# Install-side state BEFORE the post-update smoke: on app-update legs the
|
|
# updater's own transcript is streamed into the app UI and otherwise lost,
|
|
# so snapshot every place it also lands (product logs, update hand-off
|
|
# files, the venv's entry-point dir) while the install is still there to
|
|
# inspect — the smoke assertion below can `fail` out of the driver, and the
|
|
# evidence must already be on disk when it does.
|
|
local ildest="$LOG_DIR/install-logs"
|
|
mkdir -p "$ildest"
|
|
cp -R "$HOME_SANDBOX/.hermes/logs" "$ildest/hermes-logs" 2>/dev/null || true
|
|
local ud="$HOME_SANDBOX/Library/Application Support/Hermes"
|
|
[ -d "$ud" ] && cp -R "$ud" "$ildest/desktop-userdata" 2>/dev/null || true
|
|
cp "$HERMES_HOME/.hermes-update-result.json" "$ildest" 2>/dev/null || true
|
|
ls -la "$HERMES_HOME" > "$ildest/hermes-home-ls.txt" 2>/dev/null || true
|
|
ls -la "$INSTALL_DIR/venv/bin" > "$ildest/venv-bin-ls.txt" 2>/dev/null || true
|
|
ls -la "$INSTALL_DIR/venv" > "$ildest/venv-ls.txt" 2>/dev/null || true
|
|
ok "collected install-side logs to $ildest"
|
|
|
|
"$INSTALL_DIR/venv/bin/hermes" --version 2>&1 | ts_prefix > "$LOG_DIR/version-head.log" \
|
|
|| fail "hermes --version failed after update"
|
|
ok "hermes --version works post-update"
|
|
step "PASS: $OLD_REF -> HEAD via $UPDATE_METHOD"
|
|
}
|
|
|
|
case "$PHASE" in
|
|
stage) phase_stage ;;
|
|
install) phase_install ;;
|
|
update) phase_update ;;
|
|
all) phase_stage; phase_install; phase_update ;;
|
|
*) echo "error: --phase must be stage, install, update or all" >&2; exit 1 ;;
|
|
esac
|