From 732f891a06250cde8054bdc79d6a46c463ce3354 Mon Sep 17 00:00:00 2001 From: Pip <292420120+agent-p1p@users.noreply.github.com> Date: Sun, 12 Jul 2026 20:26:27 +0200 Subject: [PATCH] fix(installer): restart upgraded gateway agents Restart active Hermes and OpenClaw wn-agent user services after replacing their binaries and units. Keep enable --now for fresh installs and preserve no-start/no-service paths. Fixes #834 --- .../hermes/marmot/tests/test_dev_scripts.sh | 7 + .../openclaw/marmot/test/dev-scripts.sh | 7 + .../test_installer_systemd_service.sh | 173 ++++++++++++++++++ scripts/install-hermes-marmot.sh | 16 +- scripts/install-openclaw-marmot.sh | 16 +- 5 files changed, 213 insertions(+), 6 deletions(-) create mode 100755 integrations/test_installer_systemd_service.sh diff --git a/integrations/hermes/marmot/tests/test_dev_scripts.sh b/integrations/hermes/marmot/tests/test_dev_scripts.sh index e2dd8f76..351e4c1d 100755 --- a/integrations/hermes/marmot/tests/test_dev_scripts.sh +++ b/integrations/hermes/marmot/tests/test_dev_scripts.sh @@ -93,6 +93,7 @@ source "$default_root/env.sh" [ ! -e "$default_root" ] [ -x "$repo_root/scripts/install-hermes-marmot.sh" ] +"$repo_root/integrations/test_installer_systemd_service.sh" hermes installer_dry_run="$( env -u MARMOT_HOME -u MARMOT_AGENT_SOCKET \ WN_AGENT_SHA="9.9.9" \ @@ -140,6 +141,12 @@ case "$installer_dry_run" in *"wn-agent-hermes.service"* | *"org.marmot.wn-agent.hermes.plist"* ) ;; *) echo "Hermes installer dry-run did not use the Hermes-specific service identity" >&2; exit 1;; esac +if [ "$(uname -s)" = Linux ]; then + case "$installer_dry_run" in + *"would enable/start, or restart if already active, systemd user service: wn-agent-hermes.service"* ) ;; + *) echo "Hermes installer dry-run did not describe active-service upgrade behavior" >&2; exit 1;; + esac +fi case "$installer_stdin_dry_run" in *"wn-agent-"*"9.9.9.tar.gz"* ) ;; *) echo "Hermes installer stdin dry-run did not use WN_AGENT_SHA asset suffix" >&2; exit 1;; diff --git a/integrations/openclaw/marmot/test/dev-scripts.sh b/integrations/openclaw/marmot/test/dev-scripts.sh index f578d736..5cadcf4a 100755 --- a/integrations/openclaw/marmot/test/dev-scripts.sh +++ b/integrations/openclaw/marmot/test/dev-scripts.sh @@ -103,6 +103,7 @@ source "$default_root/env.sh" [ ! -e "$default_root" ] [ -x "$repo_root/scripts/install-openclaw-marmot.sh" ] +"$repo_root/integrations/test_installer_systemd_service.sh" openclaw installer_dry_run="$( env -u MARMOT_HOME -u MARMOT_AGENT_SOCKET \ WN_AGENT_SHA="9.9.9" \ @@ -150,6 +151,12 @@ case "$installer_dry_run" in *"wn-agent-openclaw.service"* | *"org.marmot.wn-agent.openclaw.plist"* ) ;; *) echo "OpenClaw installer dry-run did not use the OpenClaw-specific service identity" >&2; exit 1;; esac +if [ "$(uname -s)" = Linux ]; then + case "$installer_dry_run" in + *"would enable/start, or restart if already active, systemd user service: wn-agent-openclaw.service"* ) ;; + *) echo "OpenClaw installer dry-run did not describe active-service upgrade behavior" >&2; exit 1;; + esac +fi case "$installer_stdin_dry_run" in *"openclaw-marmot-plugin-9.9.9.tgz"* ) ;; *) echo "OpenClaw installer stdin dry-run did not use expected plugin asset" >&2; exit 1;; diff --git a/integrations/test_installer_systemd_service.sh b/integrations/test_installer_systemd_service.sh new file mode 100755 index 00000000..eb4fb805 --- /dev/null +++ b/integrations/test_installer_systemd_service.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +flavor="${1:?usage: test_installer_systemd_service.sh }" +allow_hex="$(printf '11%.0s' {1..32})" + +case "$flavor" in + hermes) + installer="$repo_root/scripts/install-hermes-marmot.sh" + service="wn-agent-hermes.service" + host_command="hermes" + configure_flag="--no-configure-hermes" + ;; + openclaw) + installer="$repo_root/scripts/install-openclaw-marmot.sh" + service="wn-agent-openclaw.service" + host_command="openclaw" + configure_flag="--no-configure-openclaw" + ;; + *) + echo "unknown installer flavor: $flavor" >&2 + exit 2 + ;; +esac + +[ -x "$installer" ] +bash -n "$installer" + +if [ "$(uname -s)" != Linux ]; then + exit 0 +fi + +fixture_root="$(mktemp -d)" +trap 'rm -rf "$fixture_root"' EXIT +mock_bin="$fixture_root/mock-bin" +mkdir -p "$mock_bin" + +cat >"$mock_bin/curl" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +destination="" +url="" +while [ "$#" -gt 0 ]; do + case "$1" in + -o) destination="$2"; shift 2 ;; + http*) url="$1"; shift ;; + *) shift ;; + esac +done +if [[ "$url" == *.sha256 ]]; then + printf '%s\n' fixture-hash >"$destination" +else + : >"$destination" +fi +EOF + +cat >"$mock_bin/shasum" <<'EOF' +#!/usr/bin/env bash +printf '%s\n' fixture-hash +EOF + +cat >"$mock_bin/tar" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +archive="" +destination="" +while [ "$#" -gt 0 ]; do + case "$1" in + -xzf) archive="$2"; shift 2 ;; + -C) destination="$2"; shift 2 ;; + *) shift ;; + esac +done +archive_name="$(basename "$archive")" +case "$archive_name" in + wn-agent-*) + platform="${archive_name#wn-agent-}" + platform="${platform%-9.9.9.tar.gz}" + output_dir="$destination/wn-agent-$platform" + mkdir -p "$output_dir" + cat >"$output_dir/wn-agent" <<'SCRIPT' +#!/usr/bin/env bash +if [ "${1:-}" = bootstrap ]; then + printf '%s\n' '{"account_id_hex":"aa","welcomer_account_ids_hex":["bb"]}' +fi +SCRIPT + chmod +x "$output_dir/wn-agent" + ;; + hermes-marmot-plugin-*) + mkdir -p "$destination/hermes-marmot-plugin" + ;; + *) + echo "unexpected archive: $archive_name" >&2 + exit 1 + ;; +esac +EOF + +cat >"$mock_bin/systemctl" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +printf '%s\n' "$*" >>"$SYSTEMCTL_LOG" +if [ "${1:-}" = --user ] && [ "${2:-}" = is-active ]; then + [ "$SYSTEMCTL_ACTIVE" = 1 ] +fi +EOF + +cat >"$mock_bin/sleep" <<'EOF' +#!/usr/bin/env bash +exit 0 +EOF + +cat >"$mock_bin/$host_command" <<'EOF' +#!/usr/bin/env bash +exit 0 +EOF +chmod +x "$mock_bin"/* + +run_case() { + local name="$1" + local active="$2" + local log_file="$3" + shift 3 + local case_root="$fixture_root/$name" + + mkdir -p "$case_root" + : >"$log_file" + SYSTEMCTL_ACTIVE="$active" \ + SYSTEMCTL_LOG="$log_file" \ + HOME="$case_root/home" \ + HERMES_HOME="$case_root/hermes-home" \ + OPENCLAW_HOME="$case_root/openclaw-home" \ + MARMOT_HOME="$case_root/marmot-home" \ + MARMOT_INSTALL_PREFIX="$case_root/install" \ + PATH="$mock_bin:/usr/bin:/bin" \ + WN_AGENT_SHA="9.9.9" \ + MARMOT_RELEASE_TAG="wn-agent-v9.9.9-test" \ + "$installer" --yes "$configure_flag" --allow-welcomer "$allow_hex" "$@" >/dev/null 2>&1 +} + +assert_log_equals() { + local log_file="$1" + local expected="$2" + local actual + actual="$(cat "$log_file")" + if [ "$actual" != "$expected" ]; then + printf 'unexpected systemctl calls for %s installer\nexpected:\n%s\nactual:\n%s\n' \ + "$flavor" "$expected" "$actual" >&2 + return 1 + fi +} + +fresh_log="$fixture_root/systemctl-fresh.log" +run_case fresh 0 "$fresh_log" +assert_log_equals "$fresh_log" "--user daemon-reload +--user is-active --quiet $service +--user enable --now $service" + +upgrade_log="$fixture_root/systemctl-upgrade.log" +run_case upgrade 1 "$upgrade_log" +assert_log_equals "$upgrade_log" "--user daemon-reload +--user is-active --quiet $service +--user enable $service +--user restart $service" + +no_start_log="$fixture_root/systemctl-no-start.log" +run_case no-start 1 "$no_start_log" --no-start-wn-agent +assert_log_equals "$no_start_log" "" + +no_service_log="$fixture_root/systemctl-no-service.log" +run_case no-service 1 "$no_service_log" --no-service +assert_log_equals "$no_service_log" "" diff --git a/scripts/install-hermes-marmot.sh b/scripts/install-hermes-marmot.sh index 5c1bbf3d..3d5e2e6b 100755 --- a/scripts/install-hermes-marmot.sh +++ b/scripts/install-hermes-marmot.sh @@ -473,6 +473,16 @@ install_macos_service() { log "installed and started LaunchAgent: $label" } +enable_or_restart_systemd_user_service() { + local service="$1" + if systemctl --user is-active --quiet "$service"; then + run systemctl --user enable "$service" || return 1 + run systemctl --user restart "$service" || return 1 + else + run systemctl --user enable --now "$service" || return 1 + fi +} + install_linux_user_service() { local service_dir service program relay if ! command -v systemctl >/dev/null 2>&1; then @@ -484,7 +494,7 @@ install_linux_user_service() { if [ "$DRY_RUN" -eq 1 ]; then log "would install systemd user unit $service" - log "would run: systemctl --user enable --now $MARMOT_AGENT_SERVICE_NAME.service" + log "would enable/start, or restart if already active, systemd user service: $MARMOT_AGENT_SERVICE_NAME.service" return 0 fi @@ -511,8 +521,8 @@ install_linux_user_service() { warn "systemctl --user daemon-reload failed; falling back to a temporary wn-agent process" return 1 fi - if ! run systemctl --user enable --now "$MARMOT_AGENT_SERVICE_NAME.service"; then - warn "systemctl --user enable --now $MARMOT_AGENT_SERVICE_NAME.service failed; falling back to a temporary wn-agent process" + if ! enable_or_restart_systemd_user_service "$MARMOT_AGENT_SERVICE_NAME.service"; then + warn "could not enable/start systemd user service $MARMOT_AGENT_SERVICE_NAME.service; falling back to a temporary wn-agent process" return 1 fi log "installed and started systemd user service: $MARMOT_AGENT_SERVICE_NAME.service" diff --git a/scripts/install-openclaw-marmot.sh b/scripts/install-openclaw-marmot.sh index 8d0d861e..55dae51c 100755 --- a/scripts/install-openclaw-marmot.sh +++ b/scripts/install-openclaw-marmot.sh @@ -392,6 +392,16 @@ install_macos_service() { log "installed and started LaunchAgent: $label" } +enable_or_restart_systemd_user_service() { + local service="$1" + if systemctl --user is-active --quiet "$service"; then + run systemctl --user enable "$service" || return 1 + run systemctl --user restart "$service" || return 1 + else + run systemctl --user enable --now "$service" || return 1 + fi +} + install_linux_user_service() { local service_dir service program relay if ! command -v systemctl >/dev/null 2>&1; then return 1; fi @@ -401,7 +411,7 @@ install_linux_user_service() { if [ "$DRY_RUN" -eq 1 ]; then log "would install systemd user unit $service" - log "would run: systemctl --user enable --now $MARMOT_AGENT_SERVICE_NAME.service" + log "would enable/start, or restart if already active, systemd user service: $MARMOT_AGENT_SERVICE_NAME.service" return 0 fi @@ -426,8 +436,8 @@ install_linux_user_service() { warn "systemctl --user daemon-reload failed; falling back to a temporary wn-agent process" return 1 fi - if ! run systemctl --user enable --now "$MARMOT_AGENT_SERVICE_NAME.service"; then - warn "systemctl --user enable --now $MARMOT_AGENT_SERVICE_NAME.service failed; falling back to a temporary wn-agent process" + if ! enable_or_restart_systemd_user_service "$MARMOT_AGENT_SERVICE_NAME.service"; then + warn "could not enable/start systemd user service $MARMOT_AGENT_SERVICE_NAME.service; falling back to a temporary wn-agent process" return 1 fi log "installed and started systemd user service: $MARMOT_AGENT_SERVICE_NAME.service"