From af8648e00e99df5bb6c62ccb57bd1fe049d4e1f8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 26 Apr 2026 06:23:33 +0100 Subject: [PATCH] fix(installer): make apt installs noninteractive --- CHANGELOG.md | 1 + scripts/install.sh | 46 +++++++++++++++++++++------------ test/scripts/install-sh.test.ts | 31 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 test/scripts/install-sh.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c1271d45ec2..638274fee4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Docs: https://docs.openclaw.ai - Gateway/update: fail package updates when the restarted managed gateway reports the wrong version, avoiding false-success mixed-version restarts after macOS LaunchAgent updates. Fixes #71835. Thanks @abhinas90 and @jsompis. - Plugins/runtime deps: surface activated plugin load failures in health and fail package-update restart verification or doctor repair when bundled runtime deps still cannot load, avoiding false-success repairs. (#71883) Thanks @Solvely-Colin. - Gateway/Linux: include fnm `aliases/default/bin` in generated service PATHs and let doctor accept either modern fnm aliases or the legacy `current/bin` symlink, avoiding false PATH repair prompts. Fixes #68169. Thanks @richard-scott. +- Installer/Linux: run apt installs with noninteractive dpkg and needrestart settings so fresh Ubuntu 24.04 `curl | bash` installs do not hang while installing Node.js, Git, or build tools. Fixes #41146. Thanks @iht76, @alexcarv318, @cs3gallery, @firofame, and @cgdusek. - WhatsApp: remove ack reactions after a visible reply when `messages.removeAckAfterReply` is enabled, matching other reaction-capable channels. Fixes #26183. Thanks @MrUnforsaken. - Providers/Z.AI: map OpenClaw thinking controls to Z.AI's `thinking` payload and add opt-in preserved thinking replay via `params.preserveThinking`, so GLM 5.x can keep prior `reasoning_content` when requested. Fixes #58680. Thanks @xuanmingguo. - Channels/status: keep read-only channel lists on manifest and package metadata by default, loading setup runtime only for explicit fallback callers. Thanks @shakkernerd. diff --git a/scripts/install.sh b/scripts/install.sh index 3973f48cf23..47677bdcb62 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -577,17 +577,31 @@ is_arch_linux() { return 1 } +apt_get() { + if is_root; then + env DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}" NEEDRESTART_MODE="${NEEDRESTART_MODE:-a}" apt-get "$@" + else + sudo env DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}" NEEDRESTART_MODE="${NEEDRESTART_MODE:-a}" apt-get "$@" + fi +} + +apt_get_update() { + apt_get update -qq +} + +apt_get_install() { + apt_get install -y -qq \ + -o Dpkg::Options::=--force-confdef \ + -o Dpkg::Options::=--force-confold \ + "$@" +} + install_build_tools_linux() { require_sudo if command -v apt-get &> /dev/null; then - if is_root; then - run_quiet_step "Updating package index" apt-get update -qq - run_quiet_step "Installing build tools" apt-get install -y -qq build-essential python3 make g++ cmake - else - run_quiet_step "Updating package index" sudo apt-get update -qq - run_quiet_step "Installing build tools" sudo apt-get install -y -qq build-essential python3 make g++ cmake - fi + run_quiet_step "Updating package index" apt_get_update + run_quiet_step "Installing build tools" apt_get_install build-essential python3 make g++ cmake return 0 fi @@ -1446,10 +1460,10 @@ install_node() { run_quiet_step "Downloading NodeSource setup script" download_file "https://deb.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x" "$tmp" if is_root; then run_quiet_step "Configuring NodeSource repository" bash "$tmp" - run_quiet_step "Installing Node.js" apt-get install -y -qq nodejs + run_quiet_step "Installing Node.js" apt_get_install nodejs else run_quiet_step "Configuring NodeSource repository" sudo -E bash "$tmp" - run_quiet_step "Installing Node.js" sudo apt-get install -y -qq nodejs + run_quiet_step "Installing Node.js" apt_get_install nodejs fi elif command -v dnf &> /dev/null; then local tmp @@ -1536,13 +1550,8 @@ install_git() { elif [[ "$OS" == "linux" ]]; then require_sudo if command -v apt-get &> /dev/null; then - if is_root; then - run_quiet_step "Updating package index" apt-get update -qq - run_quiet_step "Installing Git" apt-get install -y -qq git - else - run_quiet_step "Updating package index" sudo apt-get update -qq - run_quiet_step "Installing Git" sudo apt-get install -y -qq git - fi + run_quiet_step "Updating package index" apt_get_update + run_quiet_step "Installing Git" apt_get_install git elif command -v pacman &> /dev/null || is_arch_linux; then if is_root; then run_quiet_step "Installing Git" pacman -Sy --noconfirm git @@ -2283,6 +2292,11 @@ main() { print_gum_status detect_os_or_die + if [[ "$OS" == "linux" ]]; then + export DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}" + export NEEDRESTART_MODE="${NEEDRESTART_MODE:-a}" + fi + local detected_checkout="" detected_checkout="$(detect_openclaw_checkout "$PWD" || true)" diff --git a/test/scripts/install-sh.test.ts b/test/scripts/install-sh.test.ts new file mode 100644 index 00000000000..d3ecf9463fc --- /dev/null +++ b/test/scripts/install-sh.test.ts @@ -0,0 +1,31 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const SCRIPT_PATH = "scripts/install.sh"; + +describe("install.sh apt behavior", () => { + const script = readFileSync(SCRIPT_PATH, "utf8"); + + it("runs apt-get through noninteractive wrappers", () => { + expect(script).toContain("apt_get()"); + expect(script).toContain('DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}"'); + expect(script).toContain('NEEDRESTART_MODE="${NEEDRESTART_MODE:-a}"'); + expect(script).toContain("sudo env DEBIAN_FRONTEND="); + expect(script).toContain("-o Dpkg::Options::=--force-confdef"); + expect(script).toContain("-o Dpkg::Options::=--force-confold"); + + const rawAptInstalls = script + .split("\n") + .filter((line) => /\b(?:sudo\s+)?apt-get\s+install\b/.test(line)); + expect(rawAptInstalls).toEqual([]); + }); + + it("exports noninteractive apt env during Linux startup", () => { + expect(script).toMatch( + /detect_os_or_die\s+if \[\[ "\$OS" == "linux" \]\]; then\s+export DEBIAN_FRONTEND="\$\{DEBIAN_FRONTEND:-noninteractive\}"\s+export NEEDRESTART_MODE="\$\{NEEDRESTART_MODE:-a\}"\s+fi/m, + ); + expect(script).toContain( + 'run_quiet_step "Configuring NodeSource repository" sudo -E bash "$tmp"', + ); + }); +});