Skip to content

Commit 88876b9

Browse files
committed
[Fix] nvm use: do not clobber man's default search path
`man` consults its own configured search path only when MANPATH holds an empty entry; a list without one replaces the default outright. `nvm use` produced exactly that, so activating a version could hide every system man page. Contribute the empty entry as a trailing one, so that nvm's directory keeps precedence over the system's, and only when the list has none already, so that repeated `nvm use` calls are idempotent and an empty entry the user placed stays where they put it. The `$(manpath)` snapshot this replaces went stale the moment man's configuration changed, and cost a subprocess per `nvm use`. It was also dead: `local MANPATH` scoped the assignment to `nvm()`, so `export` did not outlive the call and MANPATH went untouched whenever it started out unset. Only ksh, where `local` is not a builtin, ever ran it. `nvm deactivate` now unsets MANPATH where nvm's was the only real entry, rather than leaving a bare `:` behind. Reported and diagnosed by @al0ksar in #3890, which used a leading empty entry instead. That one accrues one more colon on every `nvm use`, which `nvm deactivate` then leaves behind, and it loses to the system's man pages wherever nvm's bin directory is not first on PATH. Supersedes #2077. Refs #3890 Refs #2077
1 parent ccb49e5 commit 88876b9

4 files changed

Lines changed: 95 additions & 8 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ nvm exposes the following environment variables:
870870
- `NVM_CD_FLAGS` - used to maintain compatibility with zsh.
871871
- `NVM_RC_VERSION` - version from .nvmrc file if being used.
872872
873-
Additionally, nvm modifies `PATH`, and, if present, `MANPATH` and `NODE_PATH` when changing versions.
873+
Additionally, nvm modifies `PATH` when changing versions, along with `MANPATH` wherever a `manpath` command exists, and `NODE_PATH`, if present.
874874
875875
The following environment variables can be set to configure `nvm install`:
876876

‎nvm.sh‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4257,7 +4257,11 @@ nvm() {
42574257
nvm_err "Could not find ${NVM_DIR}/*/share/man in \${MANPATH}"
42584258
fi
42594259
else
4260-
export MANPATH="${NEWPATH}"
4260+
# `man` treats both of these as unset anyway, so leave nothing behind
4261+
case "${NEWPATH}" in
4262+
'' | ':') unset MANPATH ;;
4263+
*) export MANPATH="${NEWPATH}" ;;
4264+
esac
42614265
if [ "${NVM_SILENT:-0}" -ne 1 ]; then
42624266
nvm_echo "${NVM_DIR}/*/share/man removed from \${MANPATH}"
42634267
fi
@@ -4380,12 +4384,13 @@ nvm() {
43804384
# Change current version
43814385
PATH="$(nvm_change_path "${PATH}" "/bin" "${NVM_VERSION_DIR}")"
43824386
if nvm_has manpath; then
4383-
if [ -z "${MANPATH-}" ]; then
4384-
local MANPATH
4385-
MANPATH=$(manpath)
4386-
fi
4387-
# Change current version
4388-
MANPATH="$(nvm_change_path "${MANPATH}" "/share/man" "${NVM_VERSION_DIR}")"
4387+
MANPATH="$(nvm_change_path "${MANPATH-}" "/share/man" "${NVM_VERSION_DIR}")"
4388+
# `man` consults its configured default path only where the list has an
4389+
# empty entry; a trailing one keeps nvm's directory ahead of it
4390+
case "${MANPATH}" in
4391+
:* | *::* | *:) ;;
4392+
*) MANPATH="${MANPATH}:" ;;
4393+
esac
43894394
export MANPATH
43904395
fi
43914396
export PATH
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
die () { echo "$@" ; exit 1; }
6+
7+
export NVM_DIR="$(cd ../.. && pwd)"
8+
9+
# `--delete-prefix` covers only the npmrc files, so an inherited prefix in the
10+
# environment would still abort `nvm use`
11+
unset PREFIX npm_config_prefix NPM_CONFIG_PREFIX
12+
13+
: nvm.sh
14+
\. ../../nvm.sh
15+
\. ../common.sh
16+
17+
# the block under test is gated on `nvm_has manpath`, and the Alpine CI images
18+
# ship no `manpath`; the output is recognizable so that a reintroduced
19+
# `$(manpath)` call fails the assertions instead of passing vacuously
20+
manpath() {
21+
echo '/usr/share/man'
22+
}
23+
24+
make_fake_node v0.10.2
25+
make_fake_node v0.10.3
26+
27+
MAN_2="$(nvm_version_path v0.10.2)/share/man"
28+
MAN_3="$(nvm_version_path v0.10.3)/share/man"
29+
30+
# nvm has to contribute the empty entry itself when there is nothing to preserve
31+
unset MANPATH
32+
nvm use --delete-prefix v0.10.2 --silent || die "Failed to activate v0.10.2"
33+
[ "${MANPATH}" = "${MAN_2}:" ] || die "MANPATH should be '${MAN_2}:'; got '${MANPATH}'"
34+
35+
nvm deactivate --silent || die "Failed to deactivate v0.10.2"
36+
[ -z "${MANPATH-}" ] || die "MANPATH should be unset again; got '${MANPATH}'"
37+
38+
# nvm's entry first, the empty entry last: the reverse of what a leading empty
39+
# entry produces
40+
MANPATH='/opt/foo/man'
41+
nvm use --delete-prefix v0.10.2 --silent || die "Failed to activate v0.10.2"
42+
[ "${MANPATH}" = "${MAN_2}:/opt/foo/man:" ] || die "MANPATH should be '${MAN_2}:/opt/foo/man:'; got '${MANPATH}'"
43+
44+
# switching versions replaces nvm's entry without accruing more empty ones
45+
nvm use --delete-prefix v0.10.3 --silent || die "Failed to activate v0.10.3"
46+
[ "${MANPATH}" = "${MAN_3}:/opt/foo/man:" ] || die "MANPATH should be '${MAN_3}:/opt/foo/man:'; got '${MANPATH}'"
47+
48+
nvm use --delete-prefix v0.10.2 --silent || die "Failed to reactivate v0.10.2"
49+
[ "${MANPATH}" = "${MAN_2}:/opt/foo/man:" ] || die "MANPATH should be '${MAN_2}:/opt/foo/man:'; got '${MANPATH}'"
50+
51+
nvm deactivate --silent || die "Failed to deactivate v0.10.2"
52+
[ "${MANPATH}" = '/opt/foo/man:' ] || die "MANPATH should be '/opt/foo/man:'; got '${MANPATH}'"
53+
54+
# a value with no empty entry at all still deactivates to nothing
55+
MANPATH="${MAN_2}"
56+
nvm deactivate --silent || die "Failed to deactivate v0.10.2"
57+
[ -z "${MANPATH-}" ] || die "MANPATH should be unset again; got '${MANPATH}'"
58+
59+
# an empty entry the user placed themselves stays where they put it
60+
for TEST_MANPATH in ':/opt/foo/man' '/opt/foo/man:' '/a/man::/b/man'; do
61+
MANPATH="${TEST_MANPATH}"
62+
63+
nvm use --delete-prefix v0.10.2 --silent || die "Failed to activate v0.10.2 with MANPATH '${TEST_MANPATH}'"
64+
[ "${MANPATH}" = "${MAN_2}:${TEST_MANPATH}" ] || die "MANPATH should be '${MAN_2}:${TEST_MANPATH}'; got '${MANPATH}'"
65+
66+
nvm deactivate --silent || die "Failed to deactivate v0.10.2 with MANPATH '${TEST_MANPATH}'"
67+
[ "${MANPATH}" = "${TEST_MANPATH}" ] || die "MANPATH should be '${TEST_MANPATH}'; got '${MANPATH}'"
68+
done

‎test/fast/Unit tests/nvm_change_path‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ NEW_PATH=`nvm_change_path "$EMPTY_PATH" "/bin" "$NVM_DIR/v0.1.2"`
4444
[ "$NEW_PATH" = "$NVM_DIR/v0.1.2/bin" ] || die "Not correctly prepended: $NEW_PATH "
4545

4646

47+
# empty entries select `man`'s own search path, so they have to survive intact
48+
MAN_PATH_WITH_EMPTY_ENTRIES=/opt/foo/man::/opt/bar/man:
49+
50+
# New version dir
51+
NEW_PATH=`nvm_change_path "$MAN_PATH_WITH_EMPTY_ENTRIES" "/share/man" "$NVM_DIR/versions/node/v7.1.0"`
52+
53+
[ "$NEW_PATH" = "$NVM_DIR/versions/node/v7.1.0/share/man:$MAN_PATH_WITH_EMPTY_ENTRIES" ] || die "Not correctly prepended: $NEW_PATH "
54+
55+
# Replacing an existing version dir
56+
NEW_PATH=`nvm_change_path "$NVM_DIR/versions/node/v4.5.0/share/man::/opt/bar/man:" "/share/man" "$NVM_DIR/versions/node/v7.1.0"`
57+
58+
[ "$NEW_PATH" = "$NVM_DIR/versions/node/v7.1.0/share/man::/opt/bar/man:" ] || die "Not correctly changed: $NEW_PATH "
59+
60+
4761
# https://github.com/nvm-sh/nvm/issues/1652#issuecomment-342571223
4862
MAC_OS_NESTED_SESSION_PATH=/usr/bin:/usr/local/bin:$NVM_DIR/versions/node/v4.5.0/bin
4963

0 commit comments

Comments
 (0)