Fix installer Node runtime mismatch (#68)
* Fix installer Node runtime mismatch Ensure install.sh uses Node >=22 for npm install steps even when nvm Node 20 is first on PATH. Add a compatibility shim so the installed openclaw command still runs via a supported Node runtime in mixed-version shells. Also fixes shellcheck warning in shim log output. * fix: keep installer compat shim in user-local bin (#68) (thanks @rolandkakonyi) --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
parent
5dcce94bc2
commit
8cee7c3288
@ -8,6 +8,7 @@
|
||||
- Blog: restore the missing Discord link in the VirusTotal partnership post footer (#96, thanks @gandli).
|
||||
- Dependencies: bump `@lucide/astro` to `0.577.0` and sync `bun.lock` (#99, thanks @dependabot).
|
||||
- CI: update Bun setup pin and move the install-smoke Node setup to the pinned `actions/setup-node` v6 SHA (#98, thanks @dependabot).
|
||||
- Installer: recover from older PATH-bound Node runtimes after install, but keep the fallback `openclaw` shim in `~/.local/bin` instead of mutating version-manager bins (#68, thanks @rolandkakonyi).
|
||||
## 2026-02-22
|
||||
|
||||
- Installer: make gum behavior fully automatic (interactive TTYs get gum, headless shells get plain status), and remove manual gum toggles.
|
||||
|
||||
@ -959,6 +959,7 @@ NPM_LOGLEVEL="${OPENCLAW_NPM_LOGLEVEL:-error}"
|
||||
NPM_SILENT_FLAG="--silent"
|
||||
VERBOSE="${OPENCLAW_VERBOSE:-0}"
|
||||
OPENCLAW_BIN=""
|
||||
SELECTED_NODE_BIN=""
|
||||
PNPM_CMD=()
|
||||
HELP=0
|
||||
|
||||
@ -1301,6 +1302,163 @@ check_node() {
|
||||
fi
|
||||
}
|
||||
|
||||
node_major_from_binary() {
|
||||
local node_bin="$1"
|
||||
if [[ -z "$node_bin" || ! -x "$node_bin" ]]; then
|
||||
return 1
|
||||
fi
|
||||
"$node_bin" -p 'process.versions.node.split(".")[0]' 2>/dev/null || true
|
||||
}
|
||||
|
||||
node_is_supported_binary() {
|
||||
local node_bin="$1"
|
||||
local major=""
|
||||
major="$(node_major_from_binary "$node_bin")"
|
||||
if [[ ! "$major" =~ ^[0-9]+$ ]]; then
|
||||
return 1
|
||||
fi
|
||||
[[ "$major" -ge 22 ]]
|
||||
}
|
||||
|
||||
has_supported_node() {
|
||||
local node_bin=""
|
||||
node_bin="$(command -v node 2>/dev/null || true)"
|
||||
if [[ -z "$node_bin" ]]; then
|
||||
return 1
|
||||
fi
|
||||
node_is_supported_binary "$node_bin"
|
||||
}
|
||||
|
||||
prepend_path_dir() {
|
||||
local dir="${1%/}"
|
||||
if [[ -z "$dir" || ! -d "$dir" ]]; then
|
||||
return 1
|
||||
fi
|
||||
local current=":${PATH:-}:"
|
||||
current="${current//:${dir}:/:}"
|
||||
current="${current#:}"
|
||||
current="${current%:}"
|
||||
if [[ -n "$current" ]]; then
|
||||
export PATH="${dir}:${current}"
|
||||
else
|
||||
export PATH="${dir}"
|
||||
fi
|
||||
hash -r 2>/dev/null || true
|
||||
}
|
||||
|
||||
ensure_supported_node_on_path() {
|
||||
if has_supported_node; then
|
||||
SELECTED_NODE_BIN="$(command -v node 2>/dev/null || true)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local -a candidates=()
|
||||
local candidate=""
|
||||
while IFS= read -r candidate; do
|
||||
[[ -n "$candidate" ]] && candidates+=("$candidate")
|
||||
done < <(type -aP node 2>/dev/null || true)
|
||||
candidates+=(
|
||||
"/usr/bin/node"
|
||||
"/usr/local/bin/node"
|
||||
"/opt/homebrew/bin/node"
|
||||
"/opt/homebrew/opt/node@22/bin/node"
|
||||
"/usr/local/opt/node@22/bin/node"
|
||||
)
|
||||
|
||||
local seen=":"
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if [[ -z "$candidate" || ! -x "$candidate" ]]; then
|
||||
continue
|
||||
fi
|
||||
case "$seen" in
|
||||
*":$candidate:"*) continue ;;
|
||||
esac
|
||||
seen="${seen}${candidate}:"
|
||||
|
||||
if node_is_supported_binary "$candidate"; then
|
||||
prepend_path_dir "$(dirname "$candidate")" || continue
|
||||
SELECTED_NODE_BIN="$candidate"
|
||||
ui_info "Using Node.js runtime at ${candidate}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
original_path_node_bin() {
|
||||
if [[ -z "${ORIGINAL_PATH:-}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
PATH="$ORIGINAL_PATH" command -v node 2>/dev/null || true
|
||||
}
|
||||
|
||||
original_path_has_supported_node() {
|
||||
local node_bin=""
|
||||
node_bin="$(original_path_node_bin)"
|
||||
if [[ -z "$node_bin" ]]; then
|
||||
return 1
|
||||
fi
|
||||
node_is_supported_binary "$node_bin"
|
||||
}
|
||||
|
||||
find_openclaw_entry_path() {
|
||||
local npm_root=""
|
||||
npm_root="$(npm root -g 2>/dev/null || true)"
|
||||
if [[ -z "$npm_root" ]]; then
|
||||
return 1
|
||||
fi
|
||||
local entry_js="${npm_root}/openclaw/dist/entry.js"
|
||||
if [[ -f "$entry_js" ]]; then
|
||||
echo "$entry_js"
|
||||
return 0
|
||||
fi
|
||||
local entry_mjs="${npm_root}/openclaw/dist/entry.mjs"
|
||||
if [[ -f "$entry_mjs" ]]; then
|
||||
echo "$entry_mjs"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
install_openclaw_compat_shim() {
|
||||
if [[ "$INSTALL_METHOD" != "npm" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if original_path_has_supported_node; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local node_bin="${SELECTED_NODE_BIN:-}"
|
||||
if [[ -z "$node_bin" ]]; then
|
||||
node_bin="$(command -v node 2>/dev/null || true)"
|
||||
fi
|
||||
if [[ -z "$node_bin" || ! -x "$node_bin" ]] || ! node_is_supported_binary "$node_bin"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local entry_path=""
|
||||
entry_path="$(find_openclaw_entry_path || true)"
|
||||
if [[ -z "$entry_path" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local target_dir="$HOME/.local/bin"
|
||||
ensure_user_local_bin_on_path
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
local shim_path="${target_dir}/openclaw"
|
||||
cat > "$shim_path" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
exec "$node_bin" "$entry_path" "\$@"
|
||||
EOF
|
||||
chmod +x "$shim_path"
|
||||
refresh_shell_command_cache
|
||||
ui_warn "Configured openclaw shim at ${shim_path} for Node $("$node_bin" -v 2>/dev/null || echo '22+')"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Install Node.js
|
||||
install_node() {
|
||||
if [[ "$OS" == "macos" ]]; then
|
||||
@ -2147,6 +2305,14 @@ main() {
|
||||
if ! check_node; then
|
||||
install_node
|
||||
fi
|
||||
ensure_supported_node_on_path || true
|
||||
if ! has_supported_node; then
|
||||
ui_error "Node.js v22+ is required but could not be activated on PATH"
|
||||
echo "Detected node: $(command -v node 2>/dev/null || echo '(not found)')"
|
||||
echo "Current version: $(node -v 2>/dev/null || echo 'unknown')"
|
||||
echo "Install Node.js 22+ manually: https://nodejs.org"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ui_stage "Installing OpenClaw"
|
||||
|
||||
@ -2183,6 +2349,7 @@ main() {
|
||||
|
||||
# Step 5: OpenClaw
|
||||
install_openclaw
|
||||
install_openclaw_compat_shim || true
|
||||
fi
|
||||
|
||||
ui_stage "Finalizing setup"
|
||||
|
||||
@ -510,4 +510,67 @@ echo "==> case: install_openclaw_from_git (deps step uses run_pnpm function)"
|
||||
assert_eq "$deps_cmd" "run_pnpm" "install_openclaw_from_git dependencies command"
|
||||
)
|
||||
|
||||
echo "==> case: install_openclaw_compat_shim (always uses user-local bin)"
|
||||
(
|
||||
root="${TMP_DIR}/case-openclaw-compat-shim"
|
||||
home_dir="${root}/home"
|
||||
selected_bin_dir="${root}/node22/bin"
|
||||
original_bin_dir="${root}/nvm/bin"
|
||||
pkg_dir="${root}/npm-root/openclaw/dist"
|
||||
|
||||
mkdir -p "${home_dir}" "${selected_bin_dir}" "${original_bin_dir}" "${pkg_dir}"
|
||||
: > "${pkg_dir}/entry.js"
|
||||
|
||||
cat >"${selected_bin_dir}/node" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "${1:-}" == "-p" ]]; then
|
||||
echo "22"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${1:-}" == "-v" ]]; then
|
||||
echo "v22.12.0"
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "${selected_bin_dir}/node"
|
||||
|
||||
cat >"${original_bin_dir}/node" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "${1:-}" == "-p" ]]; then
|
||||
echo "20"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${1:-}" == "-v" ]]; then
|
||||
echo "v20.18.0"
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "${original_bin_dir}/node"
|
||||
|
||||
export HOME="${home_dir}"
|
||||
export PATH="/usr/bin:/bin"
|
||||
export INSTALL_METHOD="npm"
|
||||
export SELECTED_NODE_BIN="${selected_bin_dir}/node"
|
||||
export ORIGINAL_PATH="${original_bin_dir}:/usr/bin:/bin"
|
||||
|
||||
ui_warn() { :; }
|
||||
ensure_user_local_bin_on_path() {
|
||||
mkdir -p "${HOME}/.local/bin"
|
||||
export PATH="${HOME}/.local/bin:${PATH}"
|
||||
}
|
||||
refresh_shell_command_cache() { hash -r 2>/dev/null || true; }
|
||||
find_openclaw_entry_path() {
|
||||
echo "${pkg_dir}/entry.js"
|
||||
}
|
||||
|
||||
install_openclaw_compat_shim
|
||||
|
||||
got="$(command -v openclaw || true)"
|
||||
assert_eq "$got" "${home_dir}/.local/bin/openclaw" "install_openclaw_compat_shim wrapper path"
|
||||
)
|
||||
|
||||
echo "OK"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user