From 499d303746de7dc4bad057581715fe64965eb728 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 24 Aug 2020 22:44:25 -0700 Subject: [PATCH] [Perf] `use`/`nvm_die_on_prefix`: replicate npm config algorithm and remove `npm config` call --- nvm.sh | 87 +++++++++++++---- test/fast/Unit tests/nvm_die_on_prefix | 128 ++++++++++++++++++++----- 2 files changed, 174 insertions(+), 41 deletions(-) diff --git a/nvm.sh b/nvm.sh index cfb29cc..69f5d97 100644 --- a/nvm.sh +++ b/nvm.sh @@ -2180,6 +2180,15 @@ nvm_npm_global_modules() { nvm_echo "${INSTALLS} //// ${LINKS}" } +nvm_npmrc_bad_news_bears() { + local NVM_NPMRC + NVM_NPMRC="${1-}" + if [ -n "${NVM_NPMRC}" ] && [ -f "${NVM_NPMRC}" ] && nvm_grep -Ee '^(prefix|globalconfig) *=' <"${NVM_NPMRC}" >/dev/null; then + return 0 + fi + return 1 +} + nvm_die_on_prefix() { local NVM_DELETE_PREFIX NVM_DELETE_PREFIX="${1-}" @@ -2227,29 +2236,70 @@ nvm_die_on_prefix() { fi fi - if ! nvm_has 'npm'; then - return + # here, npm config checks npmrc files. + # the stack is: cli, env, project, user, global, builtin, defaults + # cli does not apply; env is covered above, defaults don't exist for prefix + # there are 4 npmrc locations to check: project, global, user, and builtin + # project: find the closest node_modules or package.json-containing dir, `.npmrc` + # global: default prefix + `/etc/npmrc` + # user: $HOME/.npmrc + # builtin: npm install location, `npmrc` + # + # if any of them have a `prefix`, fail. + # if any have `globalconfig`, fail also, just in case, to avoid spidering configs. + + local NVM_NPM_BUILTIN_NPMRC + NVM_NPM_BUILTIN_NPMRC="${NVM_VERSION_DIR}/lib/node_modules/npm/npmrc" + if nvm_npmrc_bad_news_bears "${NVM_NPM_BUILTIN_NPMRC}"; then + if [ "_${NVM_DELETE_PREFIX}" = "_1" ]; then + npm config --loglevel=warn delete prefix --userconfig="${NVM_NPM_BUILTIN_NPMRC}" + npm config --loglevel=warn delete globalconfig --userconfig="${NVM_NPM_BUILTIN_NPMRC}" + else + nvm_err "Your builtin npmrc file ($(nvm_sanitize_path "${NVM_NPM_BUILTIN_NPMRC}"))" + nvm_err 'has a `globalconfig` and/or a `prefix` setting, which are incompatible with nvm.' + nvm_err "Run \`${NVM_COMMAND}\` to unset it." + return 10 + fi fi - local NVM_NPM_PREFIX - local NVM_OS - NVM_OS="$(nvm_get_os)" - NVM_NPM_PREFIX="$(npm config --loglevel=warn get prefix)" - if [ ! "${NVM_VERSION_DIR}" -ef "${NVM_NPM_PREFIX}" ] && ! (nvm_tree_contains_path "${NVM_VERSION_DIR}" "${NVM_NPM_PREFIX}" >/dev/null 2>&1); then + local NVM_NPM_GLOBAL_NPMRC + NVM_NPM_GLOBAL_NPMRC="${NVM_VERSION_DIR}/etc/npmrc" + if nvm_npmrc_bad_news_bears "${NVM_NPM_GLOBAL_NPMRC}"; then + if [ "_${NVM_DELETE_PREFIX}" = "_1" ]; then + npm config --global --loglevel=warn delete prefix + npm config --global --loglevel=warn delete globalconfig + else + nvm_err "Your global npmrc file ($(nvm_sanitize_path "${NVM_NPM_GLOBAL_NPMRC}"))" + nvm_err 'has a `globalconfig` and/or a `prefix` setting, which are incompatible with nvm.' + nvm_err "Run \`${NVM_COMMAND}\` to unset it." + return 10 + fi + fi + + local NVM_NPM_USER_NPMRC + NVM_NPM_USER_NPMRC="${HOME}/.npmrc" + if nvm_npmrc_bad_news_bears "${NVM_NPM_USER_NPMRC}"; then + if [ "_${NVM_DELETE_PREFIX}" = "_1" ]; then + npm config --loglevel=warn delete prefix --userconfig="${NVM_NPM_USER_NPMRC}" + npm config --loglevel=warn delete globalconfig --userconfig="${NVM_NPM_USER_NPMRC}" + else + nvm_err "Your user’s .npmrc file ($(nvm_sanitize_path "${NVM_NPM_USER_NPMRC}"))" + nvm_err 'has a `globalconfig` and/or a `prefix` setting, which are incompatible with nvm.' + nvm_err "Run \`${NVM_COMMAND}\` to unset it." + return 10 + fi + fi + + local NVM_NPM_PROJECT_NPMRC + NVM_NPM_PROJECT_NPMRC="$(nvm_find_project_dir)/.npmrc" + if nvm_npmrc_bad_news_bears "${NVM_NPM_PROJECT_NPMRC}"; then if [ "_${NVM_DELETE_PREFIX}" = "_1" ]; then npm config --loglevel=warn delete prefix + npm config --loglevel=warn delete globalconfig else - nvm deactivate >/dev/null 2>&1 - nvm_err "nvm is not compatible with the npm config \"prefix\" option: currently set to \"${NVM_NPM_PREFIX}\"" - if nvm_has 'npm'; then - nvm_err "Run \`npm config delete prefix\` or \`${NVM_COMMAND}\` to unset it." - else - nvm_err "Run \`${NVM_COMMAND}\` to unset it." - fi - if [ "${NVM_OS}" = 'darwin' ]; then - nvm_err "Make sure your username ($(whoami)) matches the one in your \$HOME path." - nvm_err "See the \"macOS Troubleshooting\" section in the docs for more information." - fi + nvm_err "Your project npmrc file ($(nvm_sanitize_path "${NVM_NPM_PROJECT_NPMRC}"))" + nvm_err 'has a `globalconfig` and/or a `prefix` setting, which are incompatible with nvm.' + nvm_err "Run \`${NVM_COMMAND}\` to unset it." return 10 fi fi @@ -3703,6 +3753,7 @@ nvm() { nvm_sanitize_path nvm_has_colors nvm_process_parameters \ nvm_node_version_has_solaris_binary nvm_iojs_version_has_solaris_binary \ nvm_curl_libz_support nvm_command_info nvm_is_zsh nvm_stdout_is_terminal \ + nvm_npmrc_bad_news_bears \ >/dev/null 2>&1 unset NVM_RC_VERSION NVM_NODEJS_ORG_MIRROR NVM_IOJS_ORG_MIRROR NVM_DIR \ NVM_CD_FLAGS NVM_BIN NVM_INC NVM_MAKE_JOBS \ diff --git a/test/fast/Unit tests/nvm_die_on_prefix b/test/fast/Unit tests/nvm_die_on_prefix index 7254e47..f06a9bf 100755 --- a/test/fast/Unit tests/nvm_die_on_prefix +++ b/test/fast/Unit tests/nvm_die_on_prefix @@ -3,6 +3,10 @@ TEST_PWD=$(pwd) TEST_DIR="$TEST_PWD/nvm_die_on_prefix_tmp" +\. ../../../nvm.sh + +TEST_VERSION_DIR="${TEST_DIR}/version" + cleanup () { rm -rf "$TEST_DIR" alias nvm_has='\nvm_has' @@ -18,8 +22,6 @@ die () { [ ! -e "$TEST_DIR" ] && mkdir "$TEST_DIR" -\. ../../../nvm.sh - OUTPUT="$(nvm_die_on_prefix 2>&1)" EXPECTED_OUTPUT="First argument \"delete the prefix\" must be zero or one" EXIT_CODE="$(nvm_die_on_prefix >/dev/null 2>&1; echo $?)" @@ -44,14 +46,6 @@ OUTPUT="$(nvm_die_on_prefix 0 version_dir foo 2>&1)" nvm_has() { return 0; } -npm() { - local args - args="$@" - if [ "_$args" = "_config --loglevel=warn get prefix" ]; then - echo "$(nvm_version_dir new)/good prefix" - fi -} - OUTPUT="$(nvm_die_on_prefix 0 foo "$(nvm_version_dir new)" 2>&1)" [ -z "$OUTPUT" ] || die "'nvm_die_on_prefix' was not a noop when prefix is good; got '$OUTPUT'" @@ -90,18 +84,106 @@ EXIT_CODE="$(export npm_CONFIG_PREFIX=bar ; nvm_die_on_prefix 0 foo "$(nvm_versi [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'npm_CONFIG_PREFIX=bar nvm_die_on_prefix 0 foo' did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" [ "_$EXIT_CODE" = "_4" ] || die "'npm_CONFIG_PREFIX=bar nvm_die_on_prefix 0 foo' did not exit with 4; got '$EXIT_CODE'" -npm() { - local args - args="$@" - if [ "_$args" = "_config --loglevel=warn get prefix" ]; then - echo "./bad prefix" - fi -} -OUTPUT="$(nvm_die_on_prefix 0 foo "$(nvm_version_dir new)" 2>&1)" -EXPECTED_OUTPUT="nvm is not compatible with the npm config \"prefix\" option: currently set to \"./bad prefix\" -Run \`npm config delete prefix\` or \`foo\` to unset it." -EXIT_CODE="$(nvm_die_on_prefix 0 foo "$(nvm_version_dir new)" >/dev/null 2>&1; echo $?)" -[ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' did not error with '$EXPECTED_OUTPUT' with bad prefix set; got '$OUTPUT'" -[ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' did not exit with 10 with bad prefix set; got '$EXIT_CODE'" +# npmrc tests +( + cd "${TEST_DIR}" + touch package.json + + # project: prefix + echo 'prefix=garbage' > .npmrc + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your project npmrc file ($(nvm_sanitize_path "${TEST_DIR}")/.npmrc) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with project .npmrc that has prefix did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with project .npmrc that has prefix did not exit with 10; got '$EXIT_CODE'" + + # project: globalconfig + echo 'globalconfig=garbage' > .npmrc + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your project npmrc file ($(nvm_sanitize_path "${TEST_DIR}")/.npmrc) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with project .npmrc that has globalconfig did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with project .npmrc that has globalconfig did not exit with 10; got '$EXIT_CODE'" + + rm "${TEST_DIR}/.npmrc" || die '.npmrc could not be removed' + + mkdir -p "${TEST_VERSION_DIR}" + GLOBAL_NPMRC="${TEST_VERSION_DIR}/etc/npmrc" + mkdir -p "${TEST_VERSION_DIR}/etc" + + BUILTIN_NPMRC="${TEST_VERSION_DIR}/lib/node_modules/npm/npmrc" + mkdir -p "${TEST_VERSION_DIR}/lib/node_modules/npm/" + + export HOME="${TEST_VERSION_DIR}" + USER_NPMRC="${TEST_VERSION_DIR}/.npmrc" + + # global: prefix + echo 'prefix=garbage' > "${GLOBAL_NPMRC}" + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your global npmrc file ($(nvm_sanitize_path "${GLOBAL_NPMRC}")) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with global npmrc that has prefix did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with global npmrc that has prefix did not exit with 10; got '$EXIT_CODE'" + + # global: globalconfig + echo 'globalconfig=garbage' > "${GLOBAL_NPMRC}" + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your global npmrc file ($(nvm_sanitize_path "${GLOBAL_NPMRC}")) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with global npmrc that has globalconfig did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with global npmrc that has globalconfig did not exit with 10; got '$EXIT_CODE'" + + rm "${GLOBAL_NPMRC}" || die "${GLOBAL_NPMRC} could not be removed" + + # builtin: prefix + echo 'prefix=garbage' > "${BUILTIN_NPMRC}" + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your builtin npmrc file ($(nvm_sanitize_path "${BUILTIN_NPMRC}")) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with builtin npmrc that has prefix did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with builtin npmrc that has prefix did not exit with 10; got '$EXIT_CODE'" + + # builtin: globalconfig + echo 'globalconfig=garbage' > "${BUILTIN_NPMRC}" + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your builtin npmrc file ($(nvm_sanitize_path "${BUILTIN_NPMRC}")) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with builtin npmrc that has globalconfig did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with builtin npmrc that has globalconfig did not exit with 10; got '$EXIT_CODE'" + + rm "${BUILTIN_NPMRC}" || die "${BUILTIN_NPMRC} could not be removed" + + # user: prefix + echo 'prefix=garbage' > "${USER_NPMRC}" + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your user’s .npmrc file ($(nvm_sanitize_path "${USER_NPMRC}")) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with user .npmrc that has prefix did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with user .npmrc that has prefix did not exit with 10; got '$EXIT_CODE'" + + # user: globalconfig + echo 'globalconfig=garbage' > "${USER_NPMRC}" + OUTPUT="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" 2>&1)" + EXPECTED_OUTPUT="Your user’s .npmrc file ($(nvm_sanitize_path "${USER_NPMRC}")) +has a \`globalconfig\` and/or a \`prefix\` setting, which are incompatible with nvm. +Run \`foo\` to unset it." + EXIT_CODE="$(nvm_die_on_prefix 0 foo "${TEST_VERSION_DIR}" >/dev/null 2>&1; echo $?)" + [ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_die_on_prefix 0 foo' with user .npmrc that has globalconfig did not error with '$EXPECTED_OUTPUT'; got '$OUTPUT'" + [ "_$EXIT_CODE" = "_10" ] || die "'nvm_die_on_prefix 0 foo' with user .npmrc that has globalconfig did not exit with 10; got '$EXIT_CODE'" +) cleanup