From c7a1b344da04b76c2dcd250211e31d76c9f5bf13 Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Thu, 15 Oct 2026 18:13:32 +0530 Subject: [PATCH] [Fix] `install.sh`: only write `$HOME` when `$NVM_DIR` is inside it The profile line was built by replacing a leading `$HOME` with `sed`, without a path boundary, and with `$HOME` treated as a regex. With `HOME=/home/me` and `NVM_DIR=/home/me2/nvm`, or with `HOME=/` and `NVM_DIR=/usr/local/nvm`, the profile got `export NVM_DIR="$HOME2/nvm"` or `export NVM_DIR="$HOMEusr/local/nvm"`, which expand to the wrong directory, so nvm does not load in new shells. A `.` in `$HOME` matched any character, a `:` in it broke the `sed` expression, and a trailing slash on it left `$HOME.nvm`. Match `$HOME` literally, as a whole path component, and leave the path absolute when `$HOME` is empty. --- install.sh | 8 ++- .../install_script/nvm_do_install_home_prefix | 65 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100755 test/install_script/nvm_do_install_home_prefix diff --git a/install.sh b/install.sh index 518123da..5f29f29b 100755 --- a/install.sh +++ b/install.sh @@ -495,7 +495,13 @@ nvm_do_install() { local NVM_PROFILE NVM_PROFILE="$(nvm_detect_profile)" local PROFILE_INSTALL_DIR - PROFILE_INSTALL_DIR="$(nvm_install_dir | command sed "s:^$HOME:\$HOME:")" + PROFILE_INSTALL_DIR="$(nvm_install_dir)" + # an empty `$HOME` would make every absolute path look like it is inside it + if [ -n "${HOME-}" ]; then + case "${PROFILE_INSTALL_DIR}/" in + "${HOME}"/*) PROFILE_INSTALL_DIR="\$HOME${PROFILE_INSTALL_DIR#"${HOME}"}" ;; + esac + fi SOURCE_STR="\\nexport NVM_DIR=\"${PROFILE_INSTALL_DIR}\"\\n[ -s \"\$NVM_DIR/nvm.sh\" ] && \\. \"\$NVM_DIR/nvm.sh\" # This loads nvm\\n" diff --git a/test/install_script/nvm_do_install_home_prefix b/test/install_script/nvm_do_install_home_prefix new file mode 100755 index 00000000..567a2cc7 --- /dev/null +++ b/test/install_script/nvm_do_install_home_prefix @@ -0,0 +1,65 @@ +#!/bin/sh + +die () { echo "$@" ; cleanup ; exit 1; } + +cleanup() { + unset -f install_nvm_from_git install_nvm_as_script nvm_detect_profile nvm_has + unset -f nvm_check_global_modules nvm_install_node + unset -f setup cleanup die assert_nvm_dir_line + unset NVM_ENV METHOD PROFILE + rm -rf "${TMPDIR_FOR_TEST-}" 2>/dev/null +} + +setup() { + NVM_ENV=testing \. ../../install.sh + + # Mock installation functions to do nothing + install_nvm_from_git() { :; } + install_nvm_as_script() { :; } + nvm_check_global_modules() { :; } + nvm_install_node() { :; } + + # Mock nvm_has to return true for git + nvm_has() { + case "$1" in + git) return 0 ;; + xcode-select) return 1 ;; + *) return 1 ;; + esac + } + + # no profile found, so the lines to add are printed + nvm_detect_profile() { + echo "" + } +} + +TMPDIR_FOR_TEST="$(mktemp -d)" +TEST_HOME="${TMPDIR_FOR_TEST}/home" +mkdir -p "${TEST_HOME}/.nvm" "${TEST_HOME}2/nvm" "${TMPDIR_FOR_TEST}/a.b/.nvm" "${TMPDIR_FOR_TEST}/axb/.nvm" "${TMPDIR_FOR_TEST}/c:d/.nvm" + +# `nvm_do_install` unsets every `nvm_` function when it finishes, so each run sources the installer and mocks afresh. +assert_nvm_dir_line() { + setup + OUTPUT="$(HOME="$1" PROFILE='' METHOD='' NVM_DIR="$2" nvm_do_install 2>/dev/null)" + EXPECTED="export NVM_DIR=\"$3\"" + echo "${OUTPUT}" | command grep -qxF "${EXPECTED}" || die "HOME=$1 NVM_DIR=$2: expected line >${EXPECTED}<; got >${OUTPUT}<" +} + +# inside $HOME: written relative to $HOME +assert_nvm_dir_line "${TEST_HOME}" "${TEST_HOME}/.nvm" '$HOME/.nvm' + +# a sibling directory whose name starts with $HOME is not inside $HOME +assert_nvm_dir_line "${TEST_HOME}" "${TEST_HOME}2/nvm" "${TEST_HOME}2/nvm" + +# with HOME=/, every absolute path starts with $HOME +assert_nvm_dir_line '/' "${TEST_HOME}2/nvm" "${TEST_HOME}2/nvm" + +# $HOME is matched literally, not as a regex +assert_nvm_dir_line "${TMPDIR_FOR_TEST}/a.b" "${TMPDIR_FOR_TEST}/axb/.nvm" "${TMPDIR_FOR_TEST}/axb/.nvm" +assert_nvm_dir_line "${TMPDIR_FOR_TEST}/c:d" "${TMPDIR_FOR_TEST}/c:d/.nvm" '$HOME/.nvm' + +# with an empty HOME, no path is inside it +assert_nvm_dir_line '' "${TEST_HOME}/.nvm" "${TEST_HOME}/.nvm" + +cleanup