mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:40:44 +00:00
fix(installer): load nvm before node detection
This commit is contained in:
@@ -72,6 +72,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Installer/macOS: rerun Homebrew install steps without the gum spinner when raw-mode ioctl failures occur, and avoid claiming `node@24` was installed when the Homebrew keg binary is missing. Fixes #70411. Thanks @1fanwang and @dad-io.
|
||||
- Installer: load nvm before Node.js detection so `curl | bash` installs respect nvm-managed Node instead of stale system Node. Fixes #49556. Thanks @heavenlxj.
|
||||
- Docker: copy patched dependency files into runtime images so downstream `pnpm install` layers keep working. Fixes #69224. Thanks @gucasbrg.
|
||||
- Agents/runtime: submit heartbeat, cron, and exec wakeups as transient runtime context instead of visible user prompts, keeping synthetic system work out of chat transcripts. Fixes #66496 and #66814. Thanks @jeades and @mandomaker.
|
||||
- Telegram: include native quote excerpts automatically for threaded replies and reply tags when the original Telegram text is available, without adding another config knob. Fixes #6975. Thanks @rex05ai.
|
||||
|
||||
@@ -1413,6 +1413,24 @@ ensure_default_node_active_shell() {
|
||||
return 1
|
||||
}
|
||||
|
||||
load_nvm_for_node_detection() {
|
||||
local nvm_dir="${NVM_DIR:-}"
|
||||
if [[ -z "$nvm_dir" && -s "$HOME/.nvm/nvm.sh" ]]; then
|
||||
nvm_dir="$HOME/.nvm"
|
||||
fi
|
||||
if [[ -z "$nvm_dir" || ! -s "$nvm_dir/nvm.sh" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
export NVM_DIR="$nvm_dir"
|
||||
# shellcheck disable=SC1090
|
||||
. "$NVM_DIR/nvm.sh" --no-use >/dev/null 2>&1 || . "$NVM_DIR/nvm.sh" >/dev/null 2>&1 || true
|
||||
if command -v nvm >/dev/null 2>&1; then
|
||||
nvm use default --silent >/dev/null 2>&1 || nvm use node --silent >/dev/null 2>&1 || true
|
||||
fi
|
||||
refresh_shell_command_cache
|
||||
}
|
||||
|
||||
check_node() {
|
||||
if command -v node &> /dev/null; then
|
||||
NODE_VERSION="$(node_major_version || true)"
|
||||
@@ -2369,6 +2387,7 @@ main() {
|
||||
install_homebrew
|
||||
|
||||
# Step 2: Node.js
|
||||
load_nvm_for_node_detection
|
||||
if ! check_node; then
|
||||
install_node
|
||||
fi
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const SCRIPT_PATH = "scripts/install.sh";
|
||||
|
||||
function runInstallShell(script: string) {
|
||||
function runInstallShell(script: string, env: NodeJS.ProcessEnv = {}) {
|
||||
return spawnSync("bash", ["-c", script], {
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_INSTALL_SH_NO_RUN: "1",
|
||||
...env,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe("install.sh apt behavior", () => {
|
||||
describe("install.sh", () => {
|
||||
const script = readFileSync(SCRIPT_PATH, "utf8");
|
||||
|
||||
it("runs apt-get through noninteractive wrappers", () => {
|
||||
@@ -41,6 +42,71 @@ describe("install.sh apt behavior", () => {
|
||||
'run_quiet_step "Configuring NodeSource repository" sudo -E bash "$tmp"',
|
||||
);
|
||||
});
|
||||
|
||||
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/,
|
||||
);
|
||||
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-nvm-"));
|
||||
const home = join(tmp, "home");
|
||||
const systemBin = join(tmp, "system-bin");
|
||||
const nvmBin = join(home, ".nvm/versions/node/v22.22.1/bin");
|
||||
mkdirSync(systemBin, { recursive: true });
|
||||
mkdirSync(nvmBin, { recursive: true });
|
||||
mkdirSync(join(home, ".nvm"), { recursive: true });
|
||||
|
||||
const systemNode = join(systemBin, "node");
|
||||
const nvmNode = join(nvmBin, "node");
|
||||
writeFileSync(systemNode, "#!/bin/sh\necho v8.11.3\n");
|
||||
writeFileSync(nvmNode, "#!/bin/sh\necho v22.22.1\n");
|
||||
chmodSync(systemNode, 0o755);
|
||||
chmodSync(nvmNode, 0o755);
|
||||
writeFileSync(
|
||||
join(home, ".nvm/nvm.sh"),
|
||||
[
|
||||
'NVM_DIR="${NVM_DIR:-$HOME/.nvm}"',
|
||||
"export NVM_DIR",
|
||||
"nvm() {",
|
||||
' if [ "$1" = "use" ]; then',
|
||||
' export PATH="$NVM_DIR/versions/node/v22.22.1/bin:$PATH"',
|
||||
" return 0",
|
||||
" fi",
|
||||
" return 0",
|
||||
"}",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
let result: ReturnType<typeof runInstallShell> | undefined;
|
||||
try {
|
||||
result = runInstallShell(
|
||||
[
|
||||
`cd ${JSON.stringify(process.cwd())}`,
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
"set +e",
|
||||
"load_nvm_for_node_detection",
|
||||
"check_node",
|
||||
"status=$?",
|
||||
'printf "status=%s\\npath=%s\\nversion=%s\\n" "$status" "$(command -v node)" "$(node -v)"',
|
||||
"exit $status",
|
||||
].join("\n"),
|
||||
{
|
||||
HOME: home,
|
||||
PATH: `${systemBin}:/usr/bin:/bin`,
|
||||
TERM: "dumb",
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
rmSync(tmp, { force: true, recursive: true });
|
||||
}
|
||||
|
||||
expect(result?.status).toBe(0);
|
||||
const output = result?.stdout ?? "";
|
||||
expect(output).toContain("status=0");
|
||||
expect(output).toContain(`path=${nvmNode}`);
|
||||
expect(output).toContain("version=v22.22.1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("install.sh macOS Homebrew Node behavior", () => {
|
||||
|
||||
Reference in New Issue
Block a user