diff --git a/scripts/e2e/lib/upgrade-survivor/run.sh b/scripts/e2e/lib/upgrade-survivor/run.sh index c83f0f535b6d..d8ee0d3abbfe 100644 --- a/scripts/e2e/lib/upgrade-survivor/run.sh +++ b/scripts/e2e/lib/upgrade-survivor/run.sh @@ -1475,8 +1475,8 @@ phase seed-source-only-plugin-shadow seed_source_only_plugin_shadow phase assert-baseline assert_baseline_state phase seed-legacy-runtime-deps-symlink seed_legacy_runtime_deps_symlink phase resolve-candidate resolve_candidate_version -phase prepare-update-restart-probe prepare_update_restart_probe phase configure-clawhub-fixture configure_clawhub_fixture +phase prepare-update-restart-probe prepare_update_restart_probe phase configure-plugin-registry configure_plugin_registry phase update-candidate update_candidate if [ -n "${OPENCLAW_CLAWHUB_URL:-}" ]; then diff --git a/scripts/e2e/upgrade-survivor-docker.sh b/scripts/e2e/upgrade-survivor-docker.sh index 1a1efc2e6701..50fb182aa200 100755 --- a/scripts/e2e/upgrade-survivor-docker.sh +++ b/scripts/e2e/upgrade-survivor-docker.sh @@ -438,13 +438,13 @@ export OPENCLAW_PACKAGE_ACCEPTANCE_LEGACY_COMPAT echo "Checking dirty-state config before update..." OPENCLAW_UPGRADE_SURVIVOR_ASSERT_STAGE=baseline node scripts/e2e/lib/upgrade-survivor/assertions.mjs assert-config OPENCLAW_UPGRADE_SURVIVOR_ASSERT_STAGE=baseline node scripts/e2e/lib/upgrade-survivor/assertions.mjs assert-state +configure_clawhub_fixture if [ "$UPDATE_RESTART_MODE" = "auto-auth" ]; then # shellcheck disable=SC1091 source scripts/e2e/lib/upgrade-survivor/update-restart-auth.sh prepare_update_restart_probe_current_install "$PORT" "$GATEWAY_LOG" fi -configure_clawhub_fixture configure_plugin_registry echo "Running package update against the mounted tarball..." update_args=(update --tag "${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}" --yes --json) diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index 763d6e805243..fcb70963aeb8 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -2308,12 +2308,32 @@ docker_e2e_docker_run_cmd run demo expect( publishedRunner.indexOf("phase configure-plugin-registry configure_plugin_registry"), ).toBeLessThan(publishedRunner.indexOf("phase update-candidate update_candidate")); - expect(runner.indexOf("\nconfigure_clawhub_fixture\n")).toBeLessThan( + const runnerClawHubIndex = runner.indexOf("\nconfigure_clawhub_fixture\n"); + const runnerPrepareIndex = runner.indexOf( + 'prepare_update_restart_probe_current_install "$PORT" "$GATEWAY_LOG"', + ); + const runnerPluginRegistryIndex = runner.indexOf("\nconfigure_plugin_registry\n"); + expect(runnerClawHubIndex).toBeGreaterThan(-1); + expect(runnerClawHubIndex).toBeLessThan(runnerPrepareIndex); + expect(runnerPrepareIndex).toBeLessThan(runnerPluginRegistryIndex); + expect(runnerPluginRegistryIndex).toBeLessThan( runner.indexOf('\necho "Running package update against the mounted tarball..."\n'), ); - expect( - publishedRunner.indexOf("phase configure-clawhub-fixture configure_clawhub_fixture"), - ).toBeLessThan(publishedRunner.indexOf("phase update-candidate update_candidate")); + const publishedClawHubIndex = publishedRunner.indexOf( + "phase configure-clawhub-fixture configure_clawhub_fixture", + ); + const publishedPrepareIndex = publishedRunner.indexOf( + "phase prepare-update-restart-probe prepare_update_restart_probe", + ); + const publishedPluginRegistryIndex = publishedRunner.indexOf( + "phase configure-plugin-registry configure_plugin_registry", + ); + expect(publishedClawHubIndex).toBeGreaterThan(-1); + expect(publishedClawHubIndex).toBeLessThan(publishedPrepareIndex); + expect(publishedPrepareIndex).toBeLessThan(publishedPluginRegistryIndex); + expect(publishedPluginRegistryIndex).toBeLessThan( + publishedRunner.indexOf("phase update-candidate update_candidate"), + ); expect(publishedRunner.indexOf("phase update-candidate update_candidate")).toBeLessThan( publishedRunner.indexOf("phase assert-prepublish-requests node"), ); @@ -2864,6 +2884,57 @@ fi } }); + it("preserves the ClawHub fixture URL across a supervised gateway restart", async () => { + const workDir = tempDirs.make("openclaw-update-restart-clawhub-env-"); + const gatewayPath = join(workDir, "gateway.mjs"); + writeFileSync( + gatewayPath, + `import fs from "node:fs"; +fs.appendFileSync(process.env.URLS_FILE, process.env.OPENCLAW_CLAWHUB_URL + "\\n"); +const starts = fs.readFileSync(process.env.URLS_FILE, "utf8").trim().split("\\n").length; +process.exit(starts === 1 ? 1 : 78); +`, + ); + const scripts = [ + readFileSync(UPGRADE_SURVIVOR_RUN_SCRIPT, "utf8"), + readFileSync(UPGRADE_SURVIVOR_UPDATE_RESTART_AUTH_PATH, "utf8"), + ]; + + for (const [index, script] of scripts.entries()) { + const supervisorPath = join(workDir, `clawhub-env-supervisor-${index}.mjs`); + const urlsPath = join(workDir, `clawhub-env-urls-${index}`); + const logPath = join(workDir, `clawhub-env-daemon-${index}.log`); + const source = extractUpgradeSurvivorSupervisor(script).replace( + "const restartDelayMs = 5_000;", + "const restartDelayMs = 5;", + ); + writeFileSync(supervisorPath, source); + + const supervisor = spawn(process.execPath, [supervisorPath], { + env: { + ...process.env, + OPENCLAW_CLAWHUB_URL: "http://127.0.0.1:43123", + OPENCLAW_SYSTEMCTL_SHIM_DAEMON_LOG: logPath, + OPENCLAW_SYSTEMCTL_SHIM_EXEC_START: `${shellQuote(process.execPath)} ${shellQuote(gatewayPath)}`, + URLS_FILE: urlsPath, + }, + stdio: "ignore", + }); + try { + expect(await waitForProcessExit(supervisor)).toBe(0); + expect(readFileSync(urlsPath, "utf8").trim().split("\n")).toEqual([ + "http://127.0.0.1:43123", + "http://127.0.0.1:43123", + ]); + } finally { + if (supervisor.exitCode === null && supervisor.signalCode === null) { + supervisor.kill("SIGTERM"); + await waitForProcessExit(supervisor).catch(() => undefined); + } + } + } + }); + it.skipIf(process.platform === "win32")( "terminates supervised gateway descendants at the systemd stop timeout", async () => {