Skip to content

Commit 7514af3

Browse files
cvclaude
authored andcommitted
fix(install): prevent nvm/login shell from resetting PATH in subshells (NVIDIA#651)
* fix(install): prevent nvm/login shell from resetting PATH in subshells Two issues caused install_nemoclaw() subshells to lose the caller's PATH, making install-preflight tests fail on macOS while passing in CI: 1. `bash -lc` sourced /etc/profile which runs path_helper on macOS, resetting PATH to system defaults and hiding the caller's binaries. Fix: replace `bash -lc` with `bash -c` — the parent shell already has the correct PATH, so a login shell is unnecessary. 2. `ensure_nvm_loaded()` unconditionally sourced nvm.sh, which also resets PATH. Fix: skip sourcing when node is already on PATH. Also export `info` and `warn` helpers via `declare -f` so pre_extract_openclaw can use them in the subshell. Additionally: - Fix SC2206 shellcheck warning in version_gte() by using `read -ra` instead of unquoted word splitting into arrays. - Add .shellcheckrc to suppress SC2059 (printf format string with color vars) and SC1091 (dynamically sourced files) across the project. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): drop stray '.' arg from pyright pre-push hook Passing '.' to pyright overrides the include/exclude paths in pyproject.toml, causing it to scan .venv and test files. Without the argument, pyright respects the configured include paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: apply shfmt formatting to shell scripts Auto-formatted by shfmt via pre-commit hooks after merging upstream/main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): exclude test files from pyright strict type-checking The test_endpoint_validation.py file imports pytest which is only available as a dev dependency. Pyright in strict mode cannot resolve it, breaking the pre-push hook. Exclude test files from pyright's include paths since they have their own type discipline via pytest. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): drop stray '.' arg from pyright in blueprint Makefile Same issue as the pre-commit hook fix — passing '.' to pyright overrides the include/exclude paths in pyproject.toml, causing it to scan .venv and site-packages (17k+ errors in CI). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: apply shfmt formatting to brev-setup.sh Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(security): pin apt and pip package versions in Dockerfile Addresses hadolint DL3008, DL3013, DL3042, and DL3059: pin all apt packages to exact versions, pin pyyaml, add --no-cache-dir, and consolidate consecutive RUN instructions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove hadolint ignore and quote pyyaml version pin Remove stale DL3008 suppression (versions are pinned) and add quotes around the pyyaml version specifier for consistency with upstream. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: document -h short alias in usage output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 31c45c9 commit 7514af3

4 files changed

Lines changed: 30 additions & 12 deletions

File tree

.shellcheckrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SC2059: Don't use variables in the printf format string.
2+
# Our info/warn/ok/error helpers and banner intentionally embed ANSI color
3+
# variables in printf format strings. This is safe because the variables
4+
# contain only escape sequences, never user input.
5+
#
6+
# SC1091: Not following sourced file.
7+
# We dynamically source nvm.sh and runtime.sh at paths that shellcheck
8+
# cannot resolve statically.
9+
disable=SC2059,SC1091

Dockerfile

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ FROM node:22-slim@sha256:4f77a690f2f8946ab16fe1e791a3ac0667ae1c3575c3e4d0d4589e9
1212

1313
ENV DEBIAN_FRONTEND=noninteractive
1414

15-
# hadolint ignore=DL3008
1615
RUN apt-get update && apt-get install -y --no-install-recommends \
17-
python3 python3-pip python3-venv \
18-
curl git ca-certificates \
19-
iproute2 \
16+
python3=3.11.2-1+b1 \
17+
python3-pip=23.0.1+dfsg-1 \
18+
python3-venv=3.11.2-1+b1 \
19+
curl=7.88.1-10+deb12u14 \
20+
git=1:2.39.5-0+deb12u3 \
21+
ca-certificates=20230311+deb12u1 \
22+
iproute2=6.1.0-3 \
2023
&& rm -rf /var/lib/apt/lists/*
2124

2225
# Create sandbox user (matches OpenShell convention)
@@ -54,7 +57,7 @@ RUN mkdir -p /sandbox/.openclaw-data/agents/main/agent \
5457

5558
# Install OpenClaw CLI and PyYAML for blueprint runner (single layer)
5659
RUN npm install -g openclaw@2026.3.11 \
57-
&& pip3 install --no-cache-dir --break-system-packages "pyyaml==6.0.2"
60+
&& pip3 install --no-cache-dir --break-system-packages "pyyaml==6.0.3"
5861

5962
# Copy built plugin and blueprint into the sandbox
6063
COPY --from=builder /opt/nemoclaw/dist/ /opt/nemoclaw/dist/

install.sh

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ usage() {
126126
printf " ${C_DIM}Options:${C_RESET}\n"
127127
printf " --non-interactive Skip prompts (uses env vars / defaults)\n"
128128
printf " --version, -v Print installer version and exit\n"
129-
printf " --help Show this help message and exit\n\n"
129+
printf " --help, -h Show this help message and exit\n\n"
130130
printf " ${C_DIM}Environment:${C_RESET}\n"
131131
printf " NVIDIA_API_KEY API key (skips credential prompt)\n"
132132
printf " NEMOCLAW_NON_INTERACTIVE=1 Same as --non-interactive\n"
@@ -208,7 +208,10 @@ version_gte() {
208208
}
209209

210210
# Ensure nvm environment is loaded in the current shell.
211+
# Skip if node is already on PATH — sourcing nvm.sh can reset PATH and
212+
# override the caller's node/npm (e.g. in test environments with stubs).
211213
ensure_nvm_loaded() {
214+
command -v node &>/dev/null && return 0
212215
if [[ -z "${NVM_DIR:-}" ]]; then
213216
export NVM_DIR="$HOME/.nvm"
214217
fi
@@ -443,10 +446,10 @@ pre_extract_openclaw() {
443446
install_nemoclaw() {
444447
if [[ -f "./package.json" ]] && grep -q '"name": "nemoclaw"' ./package.json 2>/dev/null; then
445448
info "NemoClaw package.json found in current directory — installing from source…"
446-
spin "Preparing OpenClaw package" bash -lc "$(declare -f pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$(pwd)" \
449+
spin "Preparing OpenClaw package" bash -c "$(declare -f info warn pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$(pwd)" \
447450
|| warn "Pre-extraction failed — npm install may fail if openclaw tarball is broken"
448451
spin "Installing NemoClaw dependencies" npm install --ignore-scripts
449-
spin "Building NemoClaw plugin" bash -lc 'cd nemoclaw && npm install --ignore-scripts && npm run build'
452+
spin "Building NemoClaw plugin" bash -c 'cd nemoclaw && npm install --ignore-scripts && npm run build'
450453
spin "Linking NemoClaw CLI" npm link
451454
else
452455
info "Installing NemoClaw from GitHub…"
@@ -457,11 +460,11 @@ install_nemoclaw() {
457460
rm -rf "$nemoclaw_src"
458461
mkdir -p "$(dirname "$nemoclaw_src")"
459462
spin "Cloning NemoClaw source" git clone --depth 1 https://github.com/NVIDIA/NemoClaw.git "$nemoclaw_src"
460-
spin "Preparing OpenClaw package" bash -lc "$(declare -f pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$nemoclaw_src" \
463+
spin "Preparing OpenClaw package" bash -c "$(declare -f info warn pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$nemoclaw_src" \
461464
|| warn "Pre-extraction failed — npm install may fail if openclaw tarball is broken"
462-
spin "Installing NemoClaw dependencies" bash -lc "cd \"$nemoclaw_src\" && npm install --ignore-scripts"
463-
spin "Building NemoClaw plugin" bash -lc "cd \"$nemoclaw_src\"/nemoclaw && npm install --ignore-scripts && npm run build"
464-
spin "Linking NemoClaw CLI" bash -lc "cd \"$nemoclaw_src\" && npm link"
465+
spin "Installing NemoClaw dependencies" bash -c "cd \"$nemoclaw_src\" && npm install --ignore-scripts"
466+
spin "Building NemoClaw plugin" bash -c "cd \"$nemoclaw_src\"/nemoclaw && npm install --ignore-scripts && npm run build"
467+
spin "Linking NemoClaw CLI" bash -c "cd \"$nemoclaw_src\" && npm link"
465468
fi
466469

467470
refresh_path

scripts/install.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ else
9999
fi
100100

101101
# Ensure nvm environment is loaded in the current shell.
102+
# Skip if node is already on PATH — sourcing nvm.sh can reset PATH and
103+
# override the caller's node/npm (e.g. in test environments with stubs).
102104
ensure_nvm_loaded() {
105+
command -v node &>/dev/null && return 0
103106
if [ -z "${NVM_DIR:-}" ]; then
104107
export NVM_DIR="$HOME/.nvm"
105108
fi

0 commit comments

Comments
 (0)