From 8942b0cd4c249ff760872cb6e96361e84526fd23 Mon Sep 17 00:00:00 2001 From: Spencer Bull Date: Mon, 28 Sep 2026 00:32:20 -0500 Subject: [PATCH] Keep a moved OpenClaw gateway on the runtime's own Node Since 2026.9.6 upstream's installer keeps the Node a gateway service already ran when it rewrites the unit, so moving a gateway the old package installed left it running the runtime's code on /usr/bin/node, from the nodejs package the old openclaw package pulled in and the seed no longer depends on. A moved service now counts as moved only once its program is under ~/.openclaw, and the reinstall pins the runtime's Node with --runtime-path, which gives the same unit a fresh install gets. --- bin/omarchy-install-openclaw-cli | 19 ++++++++++++++----- test/shell.d/openclaw-cli-test.sh | 25 +++++++++++++++++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/bin/omarchy-install-openclaw-cli b/bin/omarchy-install-openclaw-cli index 10084c63..83948199 100755 --- a/bin/omarchy-install-openclaw-cli +++ b/bin/omarchy-install-openclaw-cli @@ -122,21 +122,30 @@ stop_legacy_services() { done } -# Not moved yet: still on the old copy, or running before and not now. +# Not moved yet: still on the old copy, run by a Node outside the runtime, or +# running before and not now. Upstream's installer keeps the Node a service +# already had, so a moved unit can run the runtime's code on the system Node +# the old package depended on, which this one no longer does. unsettled() { - unit_runs "$(unit_path "$1")" "/usr/lib/node_modules/openclaw/" || + local unit words + unit=$(unit_path "$1") + read -ra words <<<"$(sed -n 's/^ExecStart=//p' "$unit" | head -1)" + unit_runs "$unit" "/usr/lib/node_modules/openclaw/" || [[ ${words[0]:-} != "$prefix/"* ]] || { [[ " ${running[*]} " == *" $1 "* ]] && ! systemctl --user is-active --quiet "openclaw-$1.service"; } } # Upstream's installer rewrites a loaded gateway itself, but only warns when # the restart fails, so whatever is still unsettled is installed again from the -# runtime and then has to have settled. +# runtime, on the runtime's own Node, and then has to have settled. rehome_services() { - local role + local role node pin=() + if node=$(realpath -e -- "$prefix/tools/node/bin/node" 2>/dev/null); then + pin=(--runtime-path "$node") + fi for role in "${moving[@]}"; do if unsettled "$role"; then echo "Moving the OpenClaw $role service to $prefix..." - "$runtime_command" "$role" install --force || true + "$runtime_command" "$role" install --force "${pin[@]}" || true fi if unsettled "$role"; then echo "Could not move the OpenClaw $role service to $prefix. Finish with: openclaw $role install --force" >&2 diff --git a/test/shell.d/openclaw-cli-test.sh b/test/shell.d/openclaw-cli-test.sh index a06c7742..d7e6625d 100755 --- a/test/shell.d/openclaw-cli-test.sh +++ b/test/shell.d/openclaw-cli-test.sh @@ -42,18 +42,29 @@ chmod +x "$mock_bin/"* # it is asked. OMARCHY_TEST_INSTALL_BROKEN leaves a command that cannot run. # Like upstream, ` install --force` rewrites the unit onto the runtime # and starts it, OMARCHY_TEST_START_FAIL making the start fail, and the -# installer does that itself for a gateway it finds loaded. +# installer does that itself for a gateway it finds loaded. As upstream does +# since 2026.9.6, a rewrite keeps the Node the unit already ran unless +# --runtime-path pins one. cat >"$seed/install-cli.sh" <<'SH' printf 'install-cli %s%s\n' "$*" "${OPENCLAW_PROFILE:+ profile=$OPENCLAW_PROFILE}" >>"$OMARCHY_TEST_ROOT/events" prefix=$HOME/.openclaw -mkdir -p "$prefix/bin" "$prefix/tools/node-v24.19.0" +mkdir -p "$prefix/bin" "$prefix/tools/node-v24.19.0/bin" +touch "$prefix/tools/node-v24.19.0/bin/node" +ln -sfn "$prefix/tools/node-v24.19.0" "$prefix/tools/node" cat >"$prefix/bin/openclaw" <>"$OMARCHY_TEST_ROOT/events" if [[ \${2:-} == "install" ]]; then - printf 'ExecStart=$prefix/tools/node-v24.19.0/bin/node $prefix/tools/node-v24.19.0/lib/node_modules/openclaw/dist/index.js %s\n' "\$1" >"\$HOME/.config/systemd/user/openclaw-\$1.service" + unit="\$HOME/.config/systemd/user/openclaw-\$1.service" + node=$prefix/tools/node-v24.19.0/bin/node + if [[ \${4:-} == "--runtime-path" ]]; then + node=\$5 + elif [[ -f \$unit ]]; then + node=\$(sed -n 's/^ExecStart=\([^ ]*\).*/\1/p' "\$unit") + fi + printf 'ExecStart=%s $prefix/tools/node-v24.19.0/lib/node_modules/openclaw/dist/index.js %s\n' "\$node" "\$1" >"\$unit" [[ -z "\${OMARCHY_TEST_START_FAIL:-}" ]] || exit 1 touch "\$HOME/active-openclaw-\$1.service" fi @@ -189,6 +200,10 @@ order=$(grep -n -e '^systemctl --user stop openclaw-gateway.service$' -e '^insta ! grep -q "runtime node install\|openclaw-node" "$events" || fail "--now leaves a service running another OpenClaw alone" "$(cat "$events")" ! grep -q "profile=work" <(grep -v -e '^runtime --version' "$events") || fail "--now seeds and moves the default unit whatever profile the shell selects" "$(cat "$events")" +sed -n 's/^ExecStart=\([^ ]*\).*/\1/p' "$units/openclaw-gateway.service" | grep -qx "$test_home/.openclaw/tools/node-v24.19.0/bin/node" || + fail "--now leaves the moved gateway on the runtime's own Node" "$(cat "$units/openclaw-gateway.service")" +grep -Fxq "runtime gateway install --force --runtime-path $test_home/.openclaw/tools/node-v24.19.0/bin/node" "$events" || + fail "--now pins the runtime's Node when the installer kept the system one" "$(cat "$events")" pass "a service the old package installed moves to the runtime, and only that one" new_home stop-fails @@ -230,7 +245,7 @@ printf 'ExecStart=/usr/bin/node /usr/lib/node_modules/openclaw/dist/index.js gat touch "$test_home/active-openclaw-gateway.service" : >"$events" run omarchy-install-openclaw-cli --now || fail "--now moves a gateway beside a runtime that already runs" "$(cat "$test_tmp/output")" -! grep -q '^install-cli' "$events" && grep -Fxq "runtime gateway install --force" "$events" && [[ -e $test_home/active-openclaw-gateway.service ]] || +! grep -q '^install-cli' "$events" && grep -Fxq "runtime gateway install --force --runtime-path $test_home/.openclaw/tools/node-v24.19.0/bin/node" "$events" && [[ -e $test_home/active-openclaw-gateway.service ]] || fail "--now moves a gateway beside a runtime that already runs" "$(cat "$events")" pass "a gateway beside a runtime that already runs is moved without seeding" @@ -255,6 +270,8 @@ run bash -euo pipefail "$test_tmp/migration.sh" || fail "the migration moves Ope grep -q "^install-cli " "$events" && grep -Fxq "runtime gateway install --force" "$events" || fail "the migration seeds the runtime and moves the gateway to it" "$(cat "$events")" [[ $(readlink -- "$command") == "$runtime" ]] || fail "the migration points the command at the runtime" +sed -n 's/^ExecStart=\([^ ]*\).*/\1/p' "$test_home/.config/systemd/user/openclaw-gateway.service" | grep -qx "$test_home/.openclaw/tools/node-v24.19.0/bin/node" || + fail "the migration leaves the gateway on the runtime's own Node, not the old package's system Node" "$(cat "$test_home/.config/systemd/user/openclaw-gateway.service")" new_home migration-foreign touch "$test_tmp/package-installed"