fix(install): skip Homebrew until macOS packages need it

Keep macOS Homebrew setup lazy so users with supported Node and Git can install without admin/Homebrew, while still installing Homebrew before macOS Node or Git package installs.

Updates installer docs and adds focused install.sh coverage for the lazy Git path. Also aligns the live-media provider expectation with current main so built-artifact checks stay green.

Fixes #83232

Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
This commit is contained in:
Sebastien Tardif
2026-05-27 01:48:04 -07:00
committed by GitHub
parent 351aac9f57
commit 527b7c2eed
4 changed files with 25 additions and 9 deletions
+3 -3
View File
@@ -68,14 +68,14 @@ Recommended for most interactive installs on macOS/Linux/WSL.
<Steps>
<Step title="Detect OS">
Supports macOS and Linux (including WSL). If macOS is detected, installs Homebrew if missing.
Supports macOS and Linux (including WSL).
</Step>
<Step title="Ensure Node.js 24 by default">
Checks Node version and installs Node 24 if needed (Homebrew on macOS, NodeSource setup scripts on Linux apt/dnf/yum). OpenClaw still supports Node 22 LTS, currently `22.19+`, for compatibility.
Checks Node version and installs Node 24 if needed (Homebrew on macOS, NodeSource setup scripts on Linux apt/dnf/yum). On macOS, Homebrew is installed only when the installer needs it for Node or Git. OpenClaw still supports Node 22 LTS, currently `22.19+`, for compatibility.
On Alpine/musl Linux, the installer uses apk packages instead of NodeSource; the configured Alpine repositories must provide Node `22.19+` (Alpine 3.21 or newer at the time of writing).
</Step>
<Step title="Ensure Git">
Installs Git if missing using the detected package manager, including apk on Alpine.
Installs Git if missing using the detected package manager, including Homebrew on macOS and apk on Alpine.
</Step>
<Step title="Install OpenClaw">
- `npm` method (default): global npm install
+4 -4
View File
@@ -1914,6 +1914,7 @@ require_sudo() {
install_git() {
if [[ "$OS" == "macos" ]]; then
install_homebrew
run_quiet_step "Installing Git" brew install git
elif [[ "$OS" == "linux" ]]; then
require_sudo
@@ -3100,12 +3101,11 @@ main() {
ui_stage "Preparing environment"
# Step 1: Homebrew (macOS only)
install_homebrew
# Step 2: Node.js
# Step 1: Node.js. macOS package-manager branches install Homebrew lazily
# only when they are about to call brew.
load_nvm_for_node_detection
if ! check_node; then
install_homebrew
install_node
fi
activate_supported_node_on_path || true
+1 -1
View File
@@ -49,7 +49,7 @@ describe("test-live-media", () => {
"openai",
"vydra",
]);
expect(requirePlanEntry(plan, "music").providers).toEqual(["google", "minimax"]);
expect(requirePlanEntry(plan, "music").providers).toEqual(["fal", "google", "minimax"]);
expect(requirePlanEntry(plan, "video").providers).toEqual([
"google",
"minimax",
+17 -1
View File
@@ -826,7 +826,7 @@ describe("install.sh", () => {
it("loads nvm before checking Node.js so stale system Node does not win", () => {
expect(script).toMatch(
/# Step 2: Node\.js\s+load_nvm_for_node_detection\s+if ! check_node; then/,
/# Step 1: Node\.js[\s\S]*?load_nvm_for_node_detection\s+if ! check_node; then/,
);
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-nvm-"));
@@ -890,6 +890,22 @@ describe("install.sh", () => {
expect(output).toContain("version=v22.22.1");
});
it("installs Homebrew lazily before macOS Git installs", () => {
const result = runInstallShell(`
set -euo pipefail
source "${SCRIPT_PATH}"
OS=macos
install_homebrew() { echo "install_homebrew"; }
run_quiet_step() { echo "run_quiet_step:$*"; return 0; }
install_git
`);
expect(result.status).toBe(0);
expect(result.stdout).toMatch(
/install_homebrew\s+run_quiet_step:Installing Git brew install git/,
);
});
it("promotes a supported Linux Node binary over stale PATH entries", () => {
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-node-promote-"));
const staleBin = join(tmp, "usr-local-bin");