From d925b0113fe8e8b26b121e470f3678bf4b103e26 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Mar 2026 01:56:15 +0000 Subject: [PATCH] test: add parallels linux smoke harness --- AGENTS.md | 13 + package.json | 1 + scripts/e2e/parallels-linux-smoke.sh | 587 +++++++++++++++++++++++++ scripts/e2e/parallels-macos-smoke.sh | 4 +- scripts/e2e/parallels-windows-smoke.sh | 2 +- 5 files changed, 604 insertions(+), 3 deletions(-) create mode 100644 scripts/e2e/parallels-linux-smoke.sh diff --git a/AGENTS.md b/AGENTS.md index 394fa98ef7c..5f715abc1b0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -207,6 +207,7 @@ - `prlctl exec` is fine for deterministic repo commands, but it can misrepresent interactive shell behavior (`PATH`, `HOME`, `curl | bash`, shebang resolution). For installer parity or shell-sensitive repros, prefer the guest Terminal or `prlctl enter`. - Fresh Tahoe snapshot current reality: `brew` exists, `node` may not be on `PATH` in noninteractive guest exec. Use absolute `/opt/homebrew/bin/node` for repo/CLI runs when needed. - Preferred automation entrypoint: `pnpm test:parallels:macos`. It restores the snapshot most closely matching `macOS 26.3.1 fresh`, serves the current `main` tarball from the host, then runs fresh-install and latest-release-to-main smoke lanes. + - Gateway verification in smoke runs should use `openclaw gateway status --deep --require-rpc`, not plain `--deep`, so probe failures go non-zero. - Harness output: pass `--json` for machine-readable summary; per-phase logs land under `/tmp/openclaw-parallels-smoke.*`. - Fresh host-served tgz install: restore fresh snapshot, install tgz as guest root with `HOME=/var/root`, then run onboarding as the desktop user via `prlctl exec --current-user`. - For `openclaw onboard --non-interactive --secret-input-mode ref --install-daemon`, expect env-backed auth-profile refs (for example `OPENAI_API_KEY`) to be copied into the service env at install time; this path was fixed and should stay green. @@ -214,10 +215,22 @@ - Root-installed tarball smoke on Tahoe can still log plugin blocks for world-writable `extensions/*` under `/opt/homebrew/lib/node_modules/openclaw`; treat that as separate from onboarding/gateway health unless the task is plugin loading. - Parallels Windows smoke playbook: - Preferred automation entrypoint: `pnpm test:parallels:windows`. It restores the snapshot most closely matching `pre-openclaw-native-e2e-2026-03-12`, serves the current `main` tarball from the host, then runs fresh-install and latest-release-to-main smoke lanes. + - Gateway verification in smoke runs should use `openclaw gateway status --deep --require-rpc`, not plain `--deep`, so probe failures go non-zero. - Always use `prlctl exec --current-user` for Windows guest runs; plain `prlctl exec` lands in `NT AUTHORITY\SYSTEM` and does not match the real desktop-user install path. - Prefer explicit `npm.cmd` / `openclaw.cmd`. Bare `npm` / `openclaw` in PowerShell can hit the `.ps1` shim and fail under restrictive execution policy. - Use PowerShell only as the transport (`powershell.exe -NoProfile -ExecutionPolicy Bypass`) and call the `.cmd` shims explicitly from inside it. - Harness output: pass `--json` for machine-readable summary; per-phase logs land under `/tmp/openclaw-parallels-windows.*`. +- Parallels Linux smoke playbook: + - Preferred automation entrypoint: `pnpm test:parallels:linux`. It restores the snapshot most closely matching `fresh` on `Ubuntu 24.04.3 ARM64`, serves the current `main` tarball from the host, then runs fresh-install and latest-release-to-main smoke lanes. + - Use plain `prlctl exec` on this snapshot. `--current-user` is not the right transport there. + - Fresh snapshot reality: `curl` is missing and `apt-get update` can fail on clock skew. Bootstrap with `apt-get -o Acquire::Check-Date=false update` and install `curl ca-certificates` before testing installer paths. + - Fresh `main` tgz smoke on Linux still needs the latest-release installer first, because this snapshot has no Node/npm before bootstrap. The harness does stable bootstrap first, then overlays current `main`. + - This snapshot does not have a usable `systemd --user` session. Treat managed daemon install as unsupported here; use `--skip-health`, then verify with direct `openclaw gateway run --bind loopback --port 18789 --force`. + - Env-backed auth refs are still fine, but any direct shell launch (`openclaw gateway run`, `openclaw agent --local`, Linux `gateway status --deep` against that direct run) must inherit the referenced env vars in the same shell. + - `prlctl exec` reaps detached Linux child processes on this snapshot, so a background `openclaw gateway run` launched from automation is not a trustworthy smoke path. The harness verifies installer + `agent --local`; do direct gateway checks only from an interactive guest shell when needed. + - When you do run Linux gateway checks manually from an interactive guest shell, use `openclaw gateway status --deep --require-rpc` so an RPC miss is a hard failure. + - Prefer direct argv guest commands for fetch/install steps (`curl`, `npm install -g`, `openclaw ...`) over nested `bash -lc` quoting; Linux guest quoting through Parallels was the flaky part. + - Harness output: pass `--json` for machine-readable summary; per-phase logs land under `/tmp/openclaw-parallels-linux.*`. - Never edit `node_modules` (global/Homebrew/npm/git installs too). Updates overwrite. Skill notes go in `tools.md` or `AGENTS.md`. - When adding a new `AGENTS.md` anywhere in the repo, also add a `CLAUDE.md` symlink pointing to it (example: `ln -s AGENTS.md CLAUDE.md`). - Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/openclaw && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`. diff --git a/package.json b/package.json index 70499b1f357..68e6b35c02c 100644 --- a/package.json +++ b/package.json @@ -325,6 +325,7 @@ "test:install:smoke": "bash scripts/test-install-sh-docker.sh", "test:live": "OPENCLAW_LIVE_TEST=1 CLAWDBOT_LIVE_TEST=1 vitest run --config vitest.live.config.ts", "test:macmini": "OPENCLAW_TEST_VM_FORKS=0 OPENCLAW_TEST_PROFILE=serial node scripts/test-parallel.mjs", + "test:parallels:linux": "bash scripts/e2e/parallels-linux-smoke.sh", "test:parallels:macos": "bash scripts/e2e/parallels-macos-smoke.sh", "test:parallels:windows": "bash scripts/e2e/parallels-windows-smoke.sh", "test:perf:budget": "node scripts/test-perf-budget.mjs", diff --git a/scripts/e2e/parallels-linux-smoke.sh b/scripts/e2e/parallels-linux-smoke.sh new file mode 100644 index 00000000000..120a8290bc2 --- /dev/null +++ b/scripts/e2e/parallels-linux-smoke.sh @@ -0,0 +1,587 @@ +#!/usr/bin/env bash +set -euo pipefail + +VM_NAME="Ubuntu 24.04.3 ARM64" +SNAPSHOT_HINT="fresh" +MODE="both" +OPENAI_API_KEY_ENV="OPENAI_API_KEY" +INSTALL_URL="https://openclaw.ai/install.sh" +HOST_PORT="18427" +HOST_PORT_EXPLICIT=0 +HOST_IP="" +LATEST_VERSION="" +JSON_OUTPUT=0 +KEEP_SERVER=0 + +MAIN_TGZ_DIR="$(mktemp -d)" +MAIN_TGZ_PATH="" +SERVER_PID="" +RUN_DIR="$(mktemp -d /tmp/openclaw-parallels-linux.XXXXXX)" + +TIMEOUT_SNAPSHOT_S=180 +TIMEOUT_BOOTSTRAP_S=600 +TIMEOUT_INSTALL_S=1200 +TIMEOUT_VERIFY_S=90 +TIMEOUT_ONBOARD_S=180 +TIMEOUT_AGENT_S=180 + +FRESH_MAIN_STATUS="skip" +FRESH_MAIN_VERSION="skip" +FRESH_GATEWAY_STATUS="skip" +FRESH_AGENT_STATUS="skip" +UPGRADE_STATUS="skip" +LATEST_INSTALLED_VERSION="skip" +UPGRADE_MAIN_VERSION="skip" +UPGRADE_GATEWAY_STATUS="skip" +UPGRADE_AGENT_STATUS="skip" +DAEMON_STATUS="systemd-user-unavailable" + +say() { + printf '==> %s\n' "$*" +} + +warn() { + printf 'warn: %s\n' "$*" >&2 +} + +die() { + printf 'error: %s\n' "$*" >&2 + exit 1 +} + +cleanup() { + if [[ -n "${SERVER_PID:-}" ]]; then + kill "$SERVER_PID" >/dev/null 2>&1 || true + fi + rm -rf "$MAIN_TGZ_DIR" +} + +trap cleanup EXIT + +usage() { + cat <<'EOF' +Usage: bash scripts/e2e/parallels-linux-smoke.sh [options] + +Options: + --vm Parallels VM name. Default: "Ubuntu 24.04.3 ARM64" + --snapshot-hint Snapshot name substring/fuzzy match. Default: "fresh" + --mode + --openai-api-key-env Host env var name for OpenAI API key. Default: OPENAI_API_KEY + --install-url Installer URL for latest release. Default: https://openclaw.ai/install.sh + --host-port Host HTTP port for current-main tgz. Default: 18427 + --host-ip Override Parallels host IP. + --latest-version Override npm latest version lookup. + --keep-server Leave temp host HTTP server running. + --json Print machine-readable JSON summary. + -h, --help Show help. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --vm) + VM_NAME="$2" + shift 2 + ;; + --snapshot-hint) + SNAPSHOT_HINT="$2" + shift 2 + ;; + --mode) + MODE="$2" + shift 2 + ;; + --openai-api-key-env) + OPENAI_API_KEY_ENV="$2" + shift 2 + ;; + --install-url) + INSTALL_URL="$2" + shift 2 + ;; + --host-port) + HOST_PORT="$2" + HOST_PORT_EXPLICIT=1 + shift 2 + ;; + --host-ip) + HOST_IP="$2" + shift 2 + ;; + --latest-version) + LATEST_VERSION="$2" + shift 2 + ;; + --keep-server) + KEEP_SERVER=1 + shift + ;; + --json) + JSON_OUTPUT=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + die "unknown arg: $1" + ;; + esac +done + +case "$MODE" in + fresh|upgrade|both) ;; + *) + die "invalid --mode: $MODE" + ;; +esac + +OPENAI_API_KEY_VALUE="${!OPENAI_API_KEY_ENV:-}" +[[ -n "$OPENAI_API_KEY_VALUE" ]] || die "$OPENAI_API_KEY_ENV is required" + +resolve_snapshot_id() { + local json hint + json="$(prlctl snapshot-list "$VM_NAME" --json)" + hint="$SNAPSHOT_HINT" + SNAPSHOT_JSON="$json" SNAPSHOT_HINT="$hint" python3 - <<'PY' +import difflib +import json +import os +import sys + +payload = json.loads(os.environ["SNAPSHOT_JSON"]) +hint = os.environ["SNAPSHOT_HINT"].strip().lower() +best_id = None +best_score = -1.0 +for snapshot_id, meta in payload.items(): + name = str(meta.get("name", "")).strip() + lowered = name.lower() + score = 0.0 + if lowered == hint: + score = 10.0 + elif hint and hint in lowered: + score = 5.0 + len(hint) / max(len(lowered), 1) + else: + score = difflib.SequenceMatcher(None, hint, lowered).ratio() + if score > best_score: + best_score = score + best_id = snapshot_id +if not best_id: + sys.exit("no snapshot matched") +print(best_id) +PY +} + +resolve_host_ip() { + if [[ -n "$HOST_IP" ]]; then + printf '%s\n' "$HOST_IP" + return + fi + local detected + detected="$(ifconfig | awk '/inet 10\.211\./ { print $2; exit }')" + [[ -n "$detected" ]] || die "failed to detect Parallels host IP; pass --host-ip" + printf '%s\n' "$detected" +} + +is_host_port_free() { + local port="$1" + python3 - "$port" <<'PY' +import socket +import sys + +sock = socket.socket() +try: + sock.bind(("0.0.0.0", int(sys.argv[1]))) +except OSError: + raise SystemExit(1) +finally: + sock.close() +PY +} + +allocate_host_port() { + python3 - <<'PY' +import socket + +sock = socket.socket() +sock.bind(("0.0.0.0", 0)) +print(sock.getsockname()[1]) +sock.close() +PY +} + +resolve_host_port() { + if is_host_port_free "$HOST_PORT"; then + printf '%s\n' "$HOST_PORT" + return + fi + if [[ "$HOST_PORT_EXPLICIT" -eq 1 ]]; then + die "host port $HOST_PORT already in use" + fi + HOST_PORT="$(allocate_host_port)" + warn "host port 18427 busy; using $HOST_PORT" + printf '%s\n' "$HOST_PORT" +} + +guest_exec() { + prlctl exec "$VM_NAME" "$@" +} + +restore_snapshot() { + local snapshot_id="$1" + say "Restore snapshot $SNAPSHOT_HINT ($snapshot_id)" + prlctl snapshot-switch "$VM_NAME" --id "$snapshot_id" >/dev/null +} + +bootstrap_guest() { + guest_exec apt-get -o Acquire::Check-Date=false update + guest_exec apt-get install -y curl ca-certificates +} + +resolve_latest_version() { + if [[ -n "$LATEST_VERSION" ]]; then + printf '%s\n' "$LATEST_VERSION" + return + fi + npm view openclaw version --userconfig "$(mktemp)" +} + +current_build_commit() { + python3 - <<'PY' +import json +import pathlib + +path = pathlib.Path("dist/build-info.json") +if not path.exists(): + print("") +else: + print(json.loads(path.read_text()).get("commit", "")) +PY +} + +ensure_current_build() { + local head build_commit + head="$(git rev-parse HEAD)" + build_commit="$(current_build_commit)" + if [[ "$build_commit" == "$head" ]]; then + return + fi + say "Build dist for current head" + pnpm build + build_commit="$(current_build_commit)" + [[ "$build_commit" == "$head" ]] || die "dist/build-info.json still does not match HEAD after build" +} + +pack_main_tgz() { + say "Pack current main tgz" + ensure_current_build + local short_head pkg + short_head="$(git rev-parse --short HEAD)" + pkg="$( + npm pack --ignore-scripts --json --pack-destination "$MAIN_TGZ_DIR" \ + | python3 -c 'import json, sys; data = json.load(sys.stdin); print(data[-1]["filename"])' + )" + MAIN_TGZ_PATH="$MAIN_TGZ_DIR/openclaw-main-$short_head.tgz" + cp "$MAIN_TGZ_DIR/$pkg" "$MAIN_TGZ_PATH" + say "Packed $MAIN_TGZ_PATH" + tar -xOf "$MAIN_TGZ_PATH" package/dist/build-info.json +} + +start_server() { + local host_ip="$1" + local artifact probe_url attempt + artifact="$(basename "$MAIN_TGZ_PATH")" + attempt=0 + while :; do + attempt=$((attempt + 1)) + say "Serve current main tgz on $host_ip:$HOST_PORT" + ( + cd "$MAIN_TGZ_DIR" + exec python3 -m http.server "$HOST_PORT" --bind 0.0.0.0 + ) >/tmp/openclaw-parallels-linux-http.log 2>&1 & + SERVER_PID=$! + sleep 1 + probe_url="http://127.0.0.1:$HOST_PORT/$artifact" + if kill -0 "$SERVER_PID" >/dev/null 2>&1 && curl -fsSI "$probe_url" >/dev/null 2>&1; then + return 0 + fi + kill "$SERVER_PID" >/dev/null 2>&1 || true + wait "$SERVER_PID" >/dev/null 2>&1 || true + SERVER_PID="" + if [[ "$HOST_PORT_EXPLICIT" -eq 1 || $attempt -ge 3 ]]; then + die "failed to start reachable host HTTP server on port $HOST_PORT" + fi + HOST_PORT="$(allocate_host_port)" + warn "retrying host HTTP server on port $HOST_PORT" + done +} + +install_latest_release() { + guest_exec curl -fsSL "$INSTALL_URL" -o /tmp/openclaw-install.sh + guest_exec /usr/bin/env OPENCLAW_NO_ONBOARD=1 bash /tmp/openclaw-install.sh --no-onboard + guest_exec openclaw --version +} + +install_main_tgz() { + local host_ip="$1" + local temp_name="$2" + local tgz_url="http://$host_ip:$HOST_PORT/$(basename "$MAIN_TGZ_PATH")" + guest_exec curl -fsSL "$tgz_url" -o "/tmp/$temp_name" + guest_exec npm install -g "/tmp/$temp_name" --no-fund --no-audit + guest_exec openclaw --version +} + +verify_version_contains() { + local needle="$1" + local version + version="$(guest_exec openclaw --version)" + printf '%s\n' "$version" + case "$version" in + *"$needle"*) ;; + *) + echo "version mismatch: expected substring $needle" >&2 + return 1 + ;; + esac +} + +run_ref_onboard() { + guest_exec /usr/bin/env "OPENAI_API_KEY=$OPENAI_API_KEY_VALUE" openclaw onboard \ + --non-interactive \ + --mode local \ + --auth-choice openai-api-key \ + --secret-input-mode ref \ + --gateway-port 18789 \ + --gateway-bind loopback \ + --skip-skills \ + --skip-health \ + --accept-risk \ + --json +} + +verify_local_turn() { + guest_exec /usr/bin/env "OPENAI_API_KEY=$OPENAI_API_KEY_VALUE" openclaw agent \ + --local \ + --agent main \ + --message ping \ + --json +} + +phase_log_path() { + printf '%s/%s.log\n' "$RUN_DIR" "$1" +} + +extract_last_version() { + local log_path="$1" + python3 - "$log_path" <<'PY' +import pathlib +import re +import sys + +text = pathlib.Path(sys.argv[1]).read_text(errors="replace") +matches = re.findall(r"OpenClaw [^\r\n]+ \([0-9a-f]{7,}\)", text) +print(matches[-1] if matches else "") +PY +} + +show_log_excerpt() { + local log_path="$1" + warn "log tail: $log_path" + tail -n 80 "$log_path" >&2 || true +} + +phase_run() { + local phase_id="$1" + local timeout_s="$2" + shift 2 + + local log_path pid start rc timed_out + log_path="$(phase_log_path "$phase_id")" + say "$phase_id" + start=$SECONDS + timed_out=0 + + ( + "$@" + ) >"$log_path" 2>&1 & + pid=$! + + while kill -0 "$pid" >/dev/null 2>&1; do + if (( SECONDS - start >= timeout_s )); then + timed_out=1 + kill "$pid" >/dev/null 2>&1 || true + sleep 2 + kill -9 "$pid" >/dev/null 2>&1 || true + break + fi + sleep 1 + done + + set +e + wait "$pid" + rc=$? + set -e + + if (( timed_out )); then + warn "$phase_id timed out after ${timeout_s}s" + printf 'timeout after %ss\n' "$timeout_s" >>"$log_path" + show_log_excerpt "$log_path" + return 124 + fi + + if [[ $rc -ne 0 ]]; then + warn "$phase_id failed (rc=$rc)" + show_log_excerpt "$log_path" + return "$rc" + fi + + return 0 +} + +write_summary_json() { + local summary_path="$RUN_DIR/summary.json" + python3 - "$summary_path" <<'PY' +import json +import os +import sys + +summary = { + "vm": os.environ["SUMMARY_VM"], + "snapshotHint": os.environ["SUMMARY_SNAPSHOT_HINT"], + "snapshotId": os.environ["SUMMARY_SNAPSHOT_ID"], + "mode": os.environ["SUMMARY_MODE"], + "latestVersion": os.environ["SUMMARY_LATEST_VERSION"], + "currentHead": os.environ["SUMMARY_CURRENT_HEAD"], + "runDir": os.environ["SUMMARY_RUN_DIR"], + "daemon": os.environ["SUMMARY_DAEMON_STATUS"], + "freshMain": { + "status": os.environ["SUMMARY_FRESH_MAIN_STATUS"], + "version": os.environ["SUMMARY_FRESH_MAIN_VERSION"], + "gateway": os.environ["SUMMARY_FRESH_GATEWAY_STATUS"], + "agent": os.environ["SUMMARY_FRESH_AGENT_STATUS"], + }, + "upgrade": { + "status": os.environ["SUMMARY_UPGRADE_STATUS"], + "latestVersionInstalled": os.environ["SUMMARY_LATEST_INSTALLED_VERSION"], + "mainVersion": os.environ["SUMMARY_UPGRADE_MAIN_VERSION"], + "gateway": os.environ["SUMMARY_UPGRADE_GATEWAY_STATUS"], + "agent": os.environ["SUMMARY_UPGRADE_AGENT_STATUS"], + }, +} +with open(sys.argv[1], "w", encoding="utf-8") as handle: + json.dump(summary, handle, indent=2, sort_keys=True) +print(sys.argv[1]) +PY +} + +run_fresh_main_lane() { + local snapshot_id="$1" + local host_ip="$2" + phase_run "fresh.restore-snapshot" "$TIMEOUT_SNAPSHOT_S" restore_snapshot "$snapshot_id" + phase_run "fresh.bootstrap-guest" "$TIMEOUT_BOOTSTRAP_S" bootstrap_guest + phase_run "fresh.install-latest-bootstrap" "$TIMEOUT_INSTALL_S" install_latest_release + phase_run "fresh.install-main" "$TIMEOUT_INSTALL_S" install_main_tgz "$host_ip" "openclaw-main-fresh.tgz" + FRESH_MAIN_VERSION="$(extract_last_version "$(phase_log_path fresh.install-main)")" + phase_run "fresh.verify-main-version" "$TIMEOUT_VERIFY_S" verify_version_contains "$(git rev-parse --short=7 HEAD)" + phase_run "fresh.onboard-ref" "$TIMEOUT_ONBOARD_S" run_ref_onboard + FRESH_GATEWAY_STATUS="skipped-no-detached-linux-gateway" + phase_run "fresh.first-local-agent-turn" "$TIMEOUT_AGENT_S" verify_local_turn + FRESH_AGENT_STATUS="pass" +} + +run_upgrade_lane() { + local snapshot_id="$1" + local host_ip="$2" + phase_run "upgrade.restore-snapshot" "$TIMEOUT_SNAPSHOT_S" restore_snapshot "$snapshot_id" + phase_run "upgrade.bootstrap-guest" "$TIMEOUT_BOOTSTRAP_S" bootstrap_guest + phase_run "upgrade.install-latest" "$TIMEOUT_INSTALL_S" install_latest_release + LATEST_INSTALLED_VERSION="$(extract_last_version "$(phase_log_path upgrade.install-latest)")" + phase_run "upgrade.verify-latest-version" "$TIMEOUT_VERIFY_S" verify_version_contains "$LATEST_VERSION" + phase_run "upgrade.install-main" "$TIMEOUT_INSTALL_S" install_main_tgz "$host_ip" "openclaw-main-upgrade.tgz" + UPGRADE_MAIN_VERSION="$(extract_last_version "$(phase_log_path upgrade.install-main)")" + phase_run "upgrade.verify-main-version" "$TIMEOUT_VERIFY_S" verify_version_contains "$(git rev-parse --short=7 HEAD)" + phase_run "upgrade.onboard-ref" "$TIMEOUT_ONBOARD_S" run_ref_onboard + UPGRADE_GATEWAY_STATUS="skipped-no-detached-linux-gateway" + phase_run "upgrade.first-local-agent-turn" "$TIMEOUT_AGENT_S" verify_local_turn + UPGRADE_AGENT_STATUS="pass" +} + +SNAPSHOT_ID="$(resolve_snapshot_id)" +LATEST_VERSION="$(resolve_latest_version)" +HOST_IP="$(resolve_host_ip)" +HOST_PORT="$(resolve_host_port)" + +say "VM: $VM_NAME" +say "Snapshot hint: $SNAPSHOT_HINT" +say "Latest npm version: $LATEST_VERSION" +say "Current head: $(git rev-parse --short HEAD)" +say "Run logs: $RUN_DIR" + +pack_main_tgz +start_server "$HOST_IP" + +if [[ "$MODE" == "fresh" || "$MODE" == "both" ]]; then + set +e + run_fresh_main_lane "$SNAPSHOT_ID" "$HOST_IP" + fresh_rc=$? + set -e + if [[ $fresh_rc -eq 0 ]]; then + FRESH_MAIN_STATUS="pass" + else + FRESH_MAIN_STATUS="fail" + fi +fi + +if [[ "$MODE" == "upgrade" || "$MODE" == "both" ]]; then + set +e + run_upgrade_lane "$SNAPSHOT_ID" "$HOST_IP" + upgrade_rc=$? + set -e + if [[ $upgrade_rc -eq 0 ]]; then + UPGRADE_STATUS="pass" + else + UPGRADE_STATUS="fail" + fi +fi + +if [[ "$KEEP_SERVER" -eq 0 && -n "${SERVER_PID:-}" ]]; then + kill "$SERVER_PID" >/dev/null 2>&1 || true + SERVER_PID="" +fi + +SUMMARY_JSON_PATH="$( + SUMMARY_VM="$VM_NAME" \ + SUMMARY_SNAPSHOT_HINT="$SNAPSHOT_HINT" \ + SUMMARY_SNAPSHOT_ID="$SNAPSHOT_ID" \ + SUMMARY_MODE="$MODE" \ + SUMMARY_LATEST_VERSION="$LATEST_VERSION" \ + SUMMARY_CURRENT_HEAD="$(git rev-parse --short HEAD)" \ + SUMMARY_RUN_DIR="$RUN_DIR" \ + SUMMARY_DAEMON_STATUS="$DAEMON_STATUS" \ + SUMMARY_FRESH_MAIN_STATUS="$FRESH_MAIN_STATUS" \ + SUMMARY_FRESH_MAIN_VERSION="$FRESH_MAIN_VERSION" \ + SUMMARY_FRESH_GATEWAY_STATUS="$FRESH_GATEWAY_STATUS" \ + SUMMARY_FRESH_AGENT_STATUS="$FRESH_AGENT_STATUS" \ + SUMMARY_UPGRADE_STATUS="$UPGRADE_STATUS" \ + SUMMARY_LATEST_INSTALLED_VERSION="$LATEST_INSTALLED_VERSION" \ + SUMMARY_UPGRADE_MAIN_VERSION="$UPGRADE_MAIN_VERSION" \ + SUMMARY_UPGRADE_GATEWAY_STATUS="$UPGRADE_GATEWAY_STATUS" \ + SUMMARY_UPGRADE_AGENT_STATUS="$UPGRADE_AGENT_STATUS" \ + write_summary_json +)" + +if [[ "$JSON_OUTPUT" -eq 1 ]]; then + cat "$SUMMARY_JSON_PATH" +else + printf '\nSummary:\n' + printf ' daemon: %s\n' "$DAEMON_STATUS" + printf ' fresh-main: %s (%s)\n' "$FRESH_MAIN_STATUS" "$FRESH_MAIN_VERSION" + printf ' latest->main: %s (%s)\n' "$UPGRADE_STATUS" "$UPGRADE_MAIN_VERSION" + printf ' logs: %s\n' "$RUN_DIR" + printf ' summary: %s\n' "$SUMMARY_JSON_PATH" +fi + +if [[ "$FRESH_MAIN_STATUS" == "fail" || "$UPGRADE_STATUS" == "fail" ]]; then + exit 1 +fi diff --git a/scripts/e2e/parallels-macos-smoke.sh b/scripts/e2e/parallels-macos-smoke.sh index f86b09719f9..c85f3d237ec 100644 --- a/scripts/e2e/parallels-macos-smoke.sh +++ b/scripts/e2e/parallels-macos-smoke.sh @@ -462,7 +462,7 @@ run_ref_onboard() { } verify_gateway() { - guest_current_user_exec "$GUEST_NODE_BIN" "$GUEST_OPENCLAW_ENTRY" gateway status --deep + guest_current_user_exec "$GUEST_NODE_BIN" "$GUEST_OPENCLAW_ENTRY" gateway status --deep --require-rpc } verify_turn() { @@ -587,7 +587,7 @@ capture_latest_ref_failure() { fi warn "Latest release ref-mode onboard failed pre-upgrade" set +e - guest_current_user_exec "$GUEST_NODE_BIN" "$GUEST_OPENCLAW_ENTRY" gateway status --deep || true + guest_current_user_exec "$GUEST_NODE_BIN" "$GUEST_OPENCLAW_ENTRY" gateway status --deep --require-rpc || true set -e return 1 } diff --git a/scripts/e2e/parallels-windows-smoke.sh b/scripts/e2e/parallels-windows-smoke.sh index cf122d3ad6b..548d3d033aa 100644 --- a/scripts/e2e/parallels-windows-smoke.sh +++ b/scripts/e2e/parallels-windows-smoke.sh @@ -675,7 +675,7 @@ EOF } verify_gateway() { - guest_run_openclaw "" "" gateway status --deep + guest_run_openclaw "" "" gateway status --deep --require-rpc } verify_turn() {