Require a moved OpenClaw service to be running again
Upstream's installer rewrites a loaded gateway onto the new copy but only warns when it will not start, which let the install finish with no gateway running. A service that was running before the move now has to be running after it, or the install fails and says the service is stopped; a failed seed says so too. Co-Authored-By: Codex XHigh <noreply@openai.com>
This commit is contained in:
1 parent
d2305add6a
commit
8d25d6aef3
2 files changed
+94
-22
No files matched your search
@@ -79,22 +79,18 @@ unit_runs() {
|
|||||||
# rewrites a loaded gateway service to the copy it has just installed, so
|
# rewrites a loaded gateway service to the copy it has just installed, so
|
||||||
# seeding beside one would take it over.
|
# seeding beside one would take it over.
|
||||||
foreign_gateway() {
|
foreign_gateway() {
|
||||||
local unit="$HOME/.config/systemd/user/openclaw-gateway.service"
|
local unit
|
||||||
|
unit=$(unit_path gateway)
|
||||||
[[ -f $unit ]] && ! unit_runs "$unit" "$prefix/" && ! unit_runs "$unit" "/usr/lib/node_modules/openclaw/"
|
[[ -f $unit ]] && ! unit_runs "$unit" "$prefix/" && ! unit_runs "$unit" "/usr/lib/node_modules/openclaw/"
|
||||||
}
|
}
|
||||||
|
|
||||||
# A service the openclaw package's own copy installed runs from
|
# A service the openclaw package's own copy installed runs from
|
||||||
# /usr/lib/node_modules/openclaw, which that package no longer ships, and is
|
# /usr/lib/node_modules/openclaw, which that package no longer ships, and is
|
||||||
# moved to the runtime, where `openclaw update` can replace its code. A service
|
# moved to the runtime, where `openclaw update` can replace its code. A service
|
||||||
# running any other OpenClaw is the user's arrangement and stays.
|
# running any other OpenClaw is the user's arrangement and stays. The --now
|
||||||
legacy_units() {
|
# steps below list these, and which were running, before anything changes.
|
||||||
local role unit
|
unit_path() {
|
||||||
for role in gateway node; do
|
echo "$HOME/.config/systemd/user/openclaw-$1.service"
|
||||||
unit="$HOME/.config/systemd/user/openclaw-$role.service"
|
|
||||||
if [[ -f $unit ]] && unit_runs "$unit" "/usr/lib/node_modules/openclaw/"; then
|
|
||||||
echo "$role"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Stopped before anything is installed: still running the old code from deleted
|
# Stopped before anything is installed: still running the old code from deleted
|
||||||
@@ -102,7 +98,7 @@ legacy_units() {
|
|||||||
# migrate, which upstream's installer does the moment it finds one loaded.
|
# migrate, which upstream's installer does the moment it finds one loaded.
|
||||||
stop_legacy_services() {
|
stop_legacy_services() {
|
||||||
local role
|
local role
|
||||||
for role in $(legacy_units); do
|
for role in "${moving[@]}"; do
|
||||||
if ! systemctl --user stop "openclaw-$role.service"; then
|
if ! systemctl --user stop "openclaw-$role.service"; then
|
||||||
echo "Could not stop the OpenClaw $role service, so OpenClaw was not moved to $prefix." >&2
|
echo "Could not stop the OpenClaw $role service, so OpenClaw was not moved to $prefix." >&2
|
||||||
return 1
|
return 1
|
||||||
@@ -110,19 +106,36 @@ stop_legacy_services() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Installed again from the runtime, upstream rewrites the unit and starts it,
|
# Not moved yet: still on the old copy, or running before and not now.
|
||||||
# unless its installer already did.
|
unsettled() {
|
||||||
|
unit_runs "$(unit_path "$1")" "/usr/lib/node_modules/openclaw/" ||
|
||||||
|
{ [[ " ${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.
|
||||||
rehome_services() {
|
rehome_services() {
|
||||||
local role
|
local role
|
||||||
for role in $(legacy_units); do
|
for role in "${moving[@]}"; do
|
||||||
echo "Moving the OpenClaw $role service to $prefix..."
|
if unsettled "$role"; then
|
||||||
if ! "$runtime_command" "$role" install --force; then
|
echo "Moving the OpenClaw $role service to $prefix..."
|
||||||
echo "Could not move the OpenClaw $role service. Finish with: openclaw $role install --force" >&2
|
"$runtime_command" "$role" install --force || true
|
||||||
|
fi
|
||||||
|
if unsettled "$role"; then
|
||||||
|
echo "Could not move the OpenClaw $role service to $prefix. Finish with: openclaw $role install --force" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Said whenever a failure leaves a service stopped by this run.
|
||||||
|
stopped_note() {
|
||||||
|
if (( ${#running[@]} )); then
|
||||||
|
echo "The OpenClaw ${running[*]} service was stopped for this and is not running now." >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
--check)
|
--check)
|
||||||
if installed; then exit 0; else exit 1; fi
|
if installed; then exit 0; else exit 1; fi
|
||||||
@@ -167,15 +180,29 @@ if [[ $seeding == "true" && ( ! -r $seed/install-cli.sh || ! -r $seed/openclaw.t
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# What the old package installed and what of it was running, read before
|
||||||
|
# anything changes.
|
||||||
|
moving=()
|
||||||
|
running=()
|
||||||
|
for role in gateway node; do
|
||||||
|
if [[ -f $(unit_path "$role") ]] && unit_runs "$(unit_path "$role")" "/usr/lib/node_modules/openclaw/"; then
|
||||||
|
moving+=("$role")
|
||||||
|
if systemctl --user is-active --quiet "openclaw-$role.service"; then
|
||||||
|
running+=("$role")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
stop_legacy_services
|
stop_legacy_services
|
||||||
|
|
||||||
if [[ $seeding == "true" ]]; then
|
if [[ $seeding == "true" ]]; then
|
||||||
# Every choice the installer reads from the environment is named, so an
|
# Every choice the installer reads from the environment is named, so an
|
||||||
# OPENCLAW_INSTALL_METHOD or OPENCLAW_PREFIX left in a shell cannot move it.
|
# OPENCLAW_INSTALL_METHOD or OPENCLAW_PREFIX left in a shell cannot move it.
|
||||||
echo "Setting up OpenClaw in $prefix..."
|
echo "Setting up OpenClaw in $prefix..."
|
||||||
bash "$seed/install-cli.sh" --install-method npm --prefix "$prefix" --version "$seed/openclaw.tgz" --no-onboard
|
bash "$seed/install-cli.sh" --install-method npm --prefix "$prefix" --version "$seed/openclaw.tgz" --no-onboard || true
|
||||||
if ! runtime_runs; then
|
if ! runtime_runs; then
|
||||||
echo "OpenClaw setup did not complete. Re-run this command after resolving the installer error." >&2
|
echo "OpenClaw setup did not complete. Re-run this command after resolving the installer error." >&2
|
||||||
|
stopped_note
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -183,7 +210,10 @@ fi
|
|||||||
mkdir -p "${command_path%/*}"
|
mkdir -p "${command_path%/*}"
|
||||||
ln -sfn "$runtime_command" "$command_path"
|
ln -sfn "$runtime_command" "$command_path"
|
||||||
|
|
||||||
rehome_services
|
if ! rehome_services; then
|
||||||
|
stopped_note
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if ! on_path; then
|
if ! on_path; then
|
||||||
echo "$runtime_command is ready, but 'openclaw' on PATH is $(type -P openclaw || echo missing), which is what Omarchy runs." >&2
|
echo "$runtime_command is ready, but 'openclaw' on PATH is $(type -P openclaw || echo missing), which is what Omarchy runs." >&2
|
||||||
|
|||||||
@@ -21,16 +21,27 @@ cat >"$mock_bin/omarchy-pkg-add" <<'SH'
|
|||||||
printf 'pkg-add %s\n' "$*" >>"$OMARCHY_TEST_ROOT/events"
|
printf 'pkg-add %s\n' "$*" >>"$OMARCHY_TEST_ROOT/events"
|
||||||
touch "$OMARCHY_TEST_ROOT/package-installed"
|
touch "$OMARCHY_TEST_ROOT/package-installed"
|
||||||
SH
|
SH
|
||||||
|
# The user manager, as far as these tests need one: a service is active while
|
||||||
|
# a marker says so. Stopping clears it; OMARCHY_TEST_STOP_FAIL refuses.
|
||||||
cat >"$mock_bin/systemctl" <<'SH'
|
cat >"$mock_bin/systemctl" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
printf 'systemctl %s\n' "$*" >>"$OMARCHY_TEST_ROOT/events"
|
case "$2" in
|
||||||
[[ -z ${OMARCHY_TEST_STOP_FAIL:-} ]]
|
stop)
|
||||||
|
printf 'systemctl %s\n' "$*" >>"$OMARCHY_TEST_ROOT/events"
|
||||||
|
[[ -z ${OMARCHY_TEST_STOP_FAIL:-} ]] || exit 1
|
||||||
|
rm -f "$HOME/active-$3"
|
||||||
|
;;
|
||||||
|
is-active) [[ -e $HOME/active-$4 ]] ;;
|
||||||
|
esac
|
||||||
SH
|
SH
|
||||||
chmod +x "$mock_bin/"*
|
chmod +x "$mock_bin/"*
|
||||||
|
|
||||||
# Stands in for upstream's install-cli.sh: it writes the command the way the
|
# Stands in for upstream's install-cli.sh: it writes the command the way the
|
||||||
# real one does, execing into the prefix's tools, and that command logs what
|
# real one does, execing into the prefix's tools, and that command logs what
|
||||||
# it is asked. OMARCHY_TEST_INSTALL_BROKEN leaves a command that cannot run.
|
# it is asked. OMARCHY_TEST_INSTALL_BROKEN leaves a command that cannot run.
|
||||||
|
# Like upstream, `<role> 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.
|
||||||
cat >"$seed/install-cli.sh" <<'SH'
|
cat >"$seed/install-cli.sh" <<'SH'
|
||||||
printf 'install-cli %s%s\n' "$*" "${OPENCLAW_PROFILE:+ profile=$OPENCLAW_PROFILE}" >>"$OMARCHY_TEST_ROOT/events"
|
printf 'install-cli %s%s\n' "$*" "${OPENCLAW_PROFILE:+ profile=$OPENCLAW_PROFILE}" >>"$OMARCHY_TEST_ROOT/events"
|
||||||
prefix=$HOME/.openclaw
|
prefix=$HOME/.openclaw
|
||||||
@@ -38,11 +49,19 @@ mkdir -p "$prefix/bin" "$prefix/tools/node-v24.19.0"
|
|||||||
cat >"$prefix/bin/openclaw" <<EOF
|
cat >"$prefix/bin/openclaw" <<EOF
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
[[ -z "${OMARCHY_TEST_INSTALL_BROKEN:-}" ]] || exit 1
|
[[ -z "\${OMARCHY_TEST_INSTALL_BROKEN:-}" ]] || exit 1
|
||||||
printf 'runtime %s%s\n' "\$*" "\${OPENCLAW_PROFILE:+ profile=\$OPENCLAW_PROFILE}" >>"$OMARCHY_TEST_ROOT/events"
|
printf 'runtime %s%s\n' "\$*" "\${OPENCLAW_PROFILE:+ profile=\$OPENCLAW_PROFILE}" >>"$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"
|
||||||
|
[[ -z "\${OMARCHY_TEST_START_FAIL:-}" ]] || exit 1
|
||||||
|
touch "\$HOME/active-openclaw-\$1.service"
|
||||||
|
fi
|
||||||
exec true "$prefix/tools/node-v24.19.0/lib/node_modules/openclaw/dist/entry.js" "\$@"
|
exec true "$prefix/tools/node-v24.19.0/lib/node_modules/openclaw/dist/entry.js" "\$@"
|
||||||
EOF
|
EOF
|
||||||
chmod 755 "$prefix/bin/openclaw"
|
chmod 755 "$prefix/bin/openclaw"
|
||||||
|
if [[ -f $HOME/.config/systemd/user/openclaw-gateway.service ]]; then
|
||||||
|
"$prefix/bin/openclaw" gateway install --force || true
|
||||||
|
fi
|
||||||
SH
|
SH
|
||||||
touch "$seed/openclaw.tgz"
|
touch "$seed/openclaw.tgz"
|
||||||
|
|
||||||
@@ -180,6 +199,29 @@ grep -q "Could not stop the OpenClaw gateway service" "$test_tmp/output" || fail
|
|||||||
fail "a gateway that will not stop leaves the runtime unseeded" "$(cat "$events")"
|
fail "a gateway that will not stop leaves the runtime unseeded" "$(cat "$events")"
|
||||||
pass "a gateway that will not stop stops the install before anything is seeded"
|
pass "a gateway that will not stop stops the install before anything is seeded"
|
||||||
|
|
||||||
|
# Upstream's installer rewrites the gateway itself and only warns when it will
|
||||||
|
# not start again, so a gateway that was running has to be running afterwards.
|
||||||
|
new_home start-fails
|
||||||
|
mkdir -p "$test_home/.config/systemd/user"
|
||||||
|
printf 'ExecStart=/usr/bin/node /usr/lib/node_modules/openclaw/dist/index.js gateway --port 18789\n' >"$test_home/.config/systemd/user/openclaw-gateway.service"
|
||||||
|
touch "$test_home/active-openclaw-gateway.service"
|
||||||
|
OMARCHY_TEST_START_FAIL=1 run omarchy-install-openclaw-cli --now && fail "a moved gateway that does not start fails the install"
|
||||||
|
grep -q "Could not move the OpenClaw gateway service" "$test_tmp/output" && grep -q "is not running now" "$test_tmp/output" ||
|
||||||
|
fail "a moved gateway that does not start is named, and so is its being stopped" "$(cat "$test_tmp/output")"
|
||||||
|
pass "a gateway that was running is running again from the runtime, or the install fails saying it is stopped"
|
||||||
|
|
||||||
|
# With the runtime already in place nothing is seeded, so the move is Omarchy's.
|
||||||
|
new_home runtime-first
|
||||||
|
run omarchy-install-openclaw-cli --now || fail "--now sets OpenClaw up" "$(cat "$test_tmp/output")"
|
||||||
|
mkdir -p "$test_home/.config/systemd/user"
|
||||||
|
printf 'ExecStart=/usr/bin/node /usr/lib/node_modules/openclaw/dist/index.js gateway --port 18789\n' >"$test_home/.config/systemd/user/openclaw-gateway.service"
|
||||||
|
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 ]] ||
|
||||||
|
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"
|
||||||
|
|
||||||
# The migration moves only machines that have the package, and waits for the
|
# The migration moves only machines that have the package, and waits for the
|
||||||
# package that seeds.
|
# package that seeds.
|
||||||
new_home migration-none
|
new_home migration-none
|
||||||
|
|||||||
Reference in new issue
Block a user