From fe088a03951d204b8f7e47e850e6ce0cef8b7ce2 Mon Sep 17 00:00:00 2001 From: Aaron Erickson Date: Wed, 20 May 2026 15:13:17 -0700 Subject: [PATCH] fix(e2e): unblock current nightly regressions --- scripts/install.sh | 58 +++++++++++++++++- .../lib/sandbox_lifecycle.sh | 4 +- test/install-openshell-upgrade-prompt.test.ts | 60 +++++++++++++++++-- 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index 9189967c35..89693d15c8 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1426,8 +1426,12 @@ install_nemoclaw() { # floor. The source-checkout branch intentionally skips this — a developer # running ./scripts/install.sh manages their own openshell. The script is # idempotent on the happy path. See #2272. - spin "Installing OpenShell CLI" bash "${NEMOCLAW_SOURCE_ROOT}/scripts/install-openshell.sh" - prefer_user_local_openshell + if truthy_env "${NEMOCLAW_DEFER_OPENSHELL_INSTALL:-}"; then + info "Deferring OpenShell CLI installation until after pre-upgrade backup." + else + spin "Installing OpenShell CLI" bash "${NEMOCLAW_SOURCE_ROOT}/scripts/install-openshell.sh" + prefer_user_local_openshell + fi fi refresh_path @@ -1576,6 +1580,54 @@ resolve_existing_cli_runner() { return 1 } +prepare_current_cli_for_preupgrade_backup() { + local old_defer="${NEMOCLAW_DEFER_OPENSHELL_INSTALL:-__unset__}" + info "Preparing current ${_CLI_DISPLAY} CLI for legacy OpenShell backup retry…" + export NEMOCLAW_DEFER_OPENSHELL_INSTALL=1 + install_nemoclaw + if [[ "$old_defer" == "__unset__" ]]; then + unset NEMOCLAW_DEFER_OPENSHELL_INSTALL + else + export NEMOCLAW_DEFER_OPENSHELL_INSTALL="$old_defer" + fi + verify_nemoclaw +} + +resolve_prepared_cli_runner() { + if [[ -n "${_CLI_PATH:-}" && -x "$_CLI_PATH" ]] && is_real_nemoclaw_cli "$_CLI_PATH" "$_CLI_BIN"; then + printf "%s" "$_CLI_PATH" + return 0 + fi + resolve_existing_cli_runner +} + +run_preupgrade_backup() { + local old_cli_runner="$1" old_openshell_version="$2" + + if "$old_cli_runner" backup-all 2>&1; then + return 0 + fi + + if ! legacy_openshell_gateway_upgrade_needed "$old_openshell_version"; then + return 1 + fi + + warn "Pre-upgrade backup with the existing ${_CLI_BIN} CLI failed." + warn "Retrying with the current ${_CLI_DISPLAY} CLI before retiring the legacy OpenShell gateway." + if ! prepare_current_cli_for_preupgrade_backup; then + warn "Could not prepare the current ${_CLI_DISPLAY} CLI for backup retry." + return 1 + fi + + local retry_cli_runner="" + if ! retry_cli_runner="$(resolve_prepared_cli_runner)"; then + warn "Could not locate the current ${_CLI_BIN} CLI for backup retry." + return 1 + fi + + "$retry_cli_runner" backup-all 2>&1 +} + installed_openshell_version() { command_exists openshell || return 1 openshell --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 @@ -1734,7 +1786,7 @@ preinstall_backup_and_retire_legacy_gateway() { fi info "Backing up ${sandbox_count} sandbox(es) before upgrading OpenShell…" - if ! "$old_cli_runner" backup-all 2>&1; then + if ! run_preupgrade_backup "$old_cli_runner" "$old_openshell_version"; then if legacy_openshell_gateway_upgrade_needed "$old_openshell_version"; then error "Pre-upgrade backup failed. Aborting before retiring the legacy OpenShell gateway." fi diff --git a/test/e2e/validation_suites/lib/sandbox_lifecycle.sh b/test/e2e/validation_suites/lib/sandbox_lifecycle.sh index 367397feb9..36cbd457ea 100755 --- a/test/e2e/validation_suites/lib/sandbox_lifecycle.sh +++ b/test/e2e/validation_suites/lib/sandbox_lifecycle.sh @@ -78,8 +78,10 @@ sandbox_lifecycle_assert_status_fields_present() { return 1 } if [[ "${E2E_DRY_RUN:-0}" != "1" ]]; then + local status_output_lower + status_output_lower="$(printf '%s' "${SANDBOX_LIFECYCLE_LAST_OUTPUT}" | tr '[:upper:]' '[:lower:]')" for field in status gateway sandbox; do - [[ "${SANDBOX_LIFECYCLE_LAST_OUTPUT,,}" == *"${field}"* ]] || { + [[ "${status_output_lower}" == *"${field}"* ]] || { sandbox_lifecycle_fail "${id}" "missing status field: ${field}" return 1 } diff --git a/test/install-openshell-upgrade-prompt.test.ts b/test/install-openshell-upgrade-prompt.test.ts index 8bfee62a8e..038463dd88 100644 --- a/test/install-openshell-upgrade-prompt.test.ts +++ b/test/install-openshell-upgrade-prompt.test.ts @@ -18,6 +18,8 @@ function runPreinstallUpgradeGuard( env: Record = {}, options: { backupSucceeds?: boolean; + fallbackBackupSucceeds?: boolean; + fallbackAvailable?: boolean; hasCli?: boolean; openshellVersion?: string; supportsBackupAll?: boolean; @@ -29,17 +31,21 @@ function runPreinstallUpgradeGuard( const cliLog = path.join(tmp, "cli.log"); const openshellLog = path.join(tmp, "openshell.log"); const fakeCli = path.join(bin, "nemoclaw"); + const currentCli = path.join(bin, "nemoclaw-current"); + const preparedFlag = path.join(tmp, "prepared-current-cli"); fs.mkdirSync(path.join(home, ".nemoclaw"), { recursive: true }); fs.mkdirSync(bin, { recursive: true }); fs.writeFileSync(path.join(home, ".nemoclaw", "sandboxes.json"), '{"sandboxes":{"alpha":{}}}'); const supportsBackupAll = options.supportsBackupAll === false ? "0" : "1"; const backupSucceeds = options.backupSucceeds === false ? "0" : "1"; + const fallbackAvailable = options.fallbackAvailable === true ? "1" : "0"; + const fallbackBackupSucceeds = options.fallbackBackupSucceeds === false ? "0" : "1"; const openshellVersion = options.openshellVersion ?? "0.0.36"; writeExecutable( fakeCli, `#!/usr/bin/env bash -printf '%s\\n' "$*" >> "${cliLog}" +printf 'old:%s\\n' "$*" >> "${cliLog}" if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then if [ "${supportsBackupAll}" = "1" ]; then printf 'nemoclaw backup-all\\n' @@ -52,10 +58,27 @@ if [ "$1" = "backup-all" ] && [ "\${2:-}" != "--help" ] && [ "${backupSucceeds}" exit 3 fi exit 0 +`, + ); + writeExecutable( + currentCli, + `#!/usr/bin/env bash +printf 'current:%s\\n' "$*" >> "${cliLog}" +if [ "$1" = "--version" ]; then + printf 'nemoclaw v0.1.0\\n' + exit 0 +fi +if [ "$1" = "backup-all" ] && [ "${fallbackBackupSucceeds}" != "1" ]; then + exit 4 +fi +exit 0 `, ); - const resolveCli = options.hasCli === false ? "return 1" : `printf '%s' "${fakeCli}"`; + const resolveCli = + options.hasCli === false + ? "return 1" + : `[ -f "${preparedFlag}" ] && printf '%s' "${currentCli}" || printf '%s' "${fakeCli}"`; const snippet = ` source "${INSTALLER_PAYLOAD}" >/dev/null 2>&1 info() { printf '[INFO] %s\\n' "$*"; } @@ -66,6 +89,13 @@ exit 0 command_exists() { [ "$1" = "openshell" ]; } installed_openshell_version() { printf '${openshellVersion}'; } resolve_existing_cli_runner() { ${resolveCli}; } + prepare_current_cli_for_preupgrade_backup() { + printf 'prepare-current\\n' >> "${cliLog}" + [ "${fallbackAvailable}" = "1" ] || return 1 + touch "${preparedFlag}" + _CLI_PATH="${currentCli}" + return 0 + } openshell() { printf '%s\\n' "$*" >> "${openshellLog}"; return 0; } preinstall_backup_and_retire_legacy_gateway printf 'RESTORE=%s\\n' "\${NEMOCLAW_RESTORE_LATEST_BACKUP_ON_RECREATE:-}" @@ -127,7 +157,25 @@ describe("install.sh OpenShell 0.0.37 gateway upgrade prompt", () => { expect(result.stdout).toContain("Accepted experimental OpenShell gateway upgrade"); expect(result.stdout).toContain("RESTORE=1"); expect(cliLog).toContain("--help"); - expect(cliLog).toContain("backup-all"); + expect(cliLog).toContain("old:backup-all"); + expect(openshellLog).toContain("gateway destroy -g nemoclaw"); + }); + + it("retries legacy backup with the current CLI before retiring the gateway", () => { + const { result, cliLog, openshellLog } = runPreinstallUpgradeGuard( + { + NON_INTERACTIVE: "1", + NEMOCLAW_ACCEPT_EXPERIMENTAL_OPENSHELL_UPGRADE: "1", + }, + { backupSucceeds: false, fallbackAvailable: true }, + ); + + expect(result.status).toBe(0); + expect(result.stdout + result.stderr).toContain("Retrying with the current NemoClaw CLI"); + expect(result.stdout).toContain("RESTORE=1"); + expect(cliLog.split(/\r?\n/)).toContain("old:backup-all"); + expect(cliLog.split(/\r?\n/)).toContain("prepare-current"); + expect(cliLog.split(/\r?\n/)).toContain("current:backup-all"); expect(openshellLog).toContain("gateway destroy -g nemoclaw"); }); @@ -143,7 +191,8 @@ describe("install.sh OpenShell 0.0.37 gateway upgrade prompt", () => { expect(result.status).not.toBe(0); expect(result.stdout + result.stderr).toContain("Pre-upgrade backup failed"); expect(cliLog).toContain("--help"); - expect(cliLog.split(/\r?\n/)).toContain("backup-all"); + expect(cliLog.split(/\r?\n/)).toContain("old:backup-all"); + expect(cliLog.split(/\r?\n/)).toContain("prepare-current"); expect(openshellLog).toBe(""); }); @@ -159,8 +208,9 @@ describe("install.sh OpenShell 0.0.37 gateway upgrade prompt", () => { expect(result.stdout + result.stderr).toContain( "Fix the OpenShell gateway state, rerun 'nemoclaw backup-all', then rerun the installer.", ); - expect(cliLog.split(/\r?\n/)).toContain("backup-all"); + expect(cliLog.split(/\r?\n/)).toContain("old:backup-all"); expect(cliLog).not.toContain("--help"); + expect(cliLog).not.toContain("prepare-current"); expect(openshellLog).toBe(""); });