Every time the NEXO installer ran with NEXO_HOME pointing outside $HOME/.nexo — which is the normal case for any pytest fixture that builds an isolated runtime at tmp_path — it appended a stale export PATH="$NEXO_HOME/bin:$PATH" line to the real ~/.bash_profile, ~/.bashrc, and ~/.zshrc. Over a few test runs the developer’s shell profile ended up peppered with references to directories that no longer existed.
Root cause
Both installer paths — the Python one in src/auto_update.py::_ensure_runtime_cli_in_shell() and the two JavaScript sites in bin/nexo-brain.js (install Step 8 + the migration restore path) — computed the rc file list from Path.home() / os.homedir() regardless of where NEXO_HOME pointed. The check was asymmetric: the write target came from the operator’s real $HOME, but the export PATH value came from NEXO_HOME. With a temporary NEXO_HOME you got a guaranteed-broken line.
Fix
A single helper now gates all three call sites:
def _should_skip_shell_profile_backfill() -> tuple[bool, str]:
flag = os.environ.get("NEXO_SKIP_SHELL_PROFILE", "").strip().lower()
if flag in {"1", "true", "yes", "on"}:
return True, f"NEXO_SKIP_SHELL_PROFILE={flag}"
canonical = managed_nexo_home()
if NEXO_HOME.resolve(strict=False) != canonical.resolve(strict=False):
return True, f"NEXO_HOME={NEXO_HOME} is not the canonical {canonical}"
return False, ""
The JavaScript mirror lives in bin/nexo-brain.js::shouldSkipShellProfileBackfill() and is consulted by both the fresh-install Step 8 and the migration block that restores the operator alias for existing installs.
Escape hatch for intentional isolated installs
NEXO_SKIP_SHELL_PROFILE=1 (also true, yes, on) force-skips the backfill even when NEXO_HOME happens to be canonical. Useful for sandboxed test runners, container smoke tests, or any scenario where you want the runtime installed but explicitly do not want the alias wired into the host shell.
Regression tests
tests/test_auto_update_shell_profile.py lands with the fix and covers five cases:
- pytest tmp dir (non-canonical
NEXO_HOME) → skip. NEXO_SKIP_SHELL_PROFILE=1with canonicalNEXO_HOME→ skip.- Canonical install, flag unset → write.
- All truthy flag values (
1,true,TRUE,yes,on) → skip. - Canonical install with
NEXO_SKIP_SHELL_PROFILE=0→ write (falsy values are ignored).
The pytest CI workflow introduced in v6.0.5 (.github/workflows/tests.yml) is what caught this category of drift on merge. In 6.0.2–6.0.4, three pre-tool test regressions shipped because CI only ran ruff, bandit, and the client-parity checks; pytest itself was never a blocking gate. With tests.yml in place, any future installer change that breaks this contract will fail the PR, not the developer’s ~/.bash_profile.
Housekeeping
This release also removes .github/workflows/tests 2.yml — a duplicate workflow with a space in its name that slipped in alongside the real tests.yml in the v6.0.5 release, plus dozens of stale __pycache__/*\ 2.* files generated by Finder copies on previous commits.
Upgrade
nexo update
Or on a fresh machine:
npm install -g nexo-brain@6.0.6
nexo install
Existing operators who never ran a test harness against the installer will see no behavioural change. Contributors working on packaging / installer code — and anyone who noticed mystery export PATH lines accumulating in their shell profile — get their real shell back.