Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sourcing nvm.sh exits with code 3 when .nvmrc is present and version is not installed #1985

Open
pvinis opened this issue Jan 19, 2019 · 51 comments
Labels
needs followup We need some info or action from whoever filed this issue/PR.

Comments

@pvinis
Copy link

pvinis commented Jan 19, 2019

When I source nvm.sh like . "$HOME/.nvm/nvm.sh" for manual install and like . $(brew --prefix nvm)/nvm.sh, the exit code is 3.

Even though nvm works after that, in bash scripts with set -e, the script stops there, since the command exits with a non-zero value.

Is there a reason this fails?
Is there a reason it exits with 3, if it's not an actual failure?
What can we do about that?

Some extra info:

Details
nvm --version: v0.34.0
$TERM_PROGRAM: iTerm.app
$SHELL: /bin/zsh
$SHLVL: 1
$HOME: /Users/pvinis
$NVM_DIR: '$HOME/.nvm'
$PATH: $HOME/.gem/ruby/2.5.1/bin:$HOME/.rubies/ruby-2.5.1/lib/ruby/gems/2.5.0/bin:$HOME/.rubies/ruby-2.5.1/bin:$NVM_DIR/versions/node/v11.0.0/bin:$HOME/.config/yarn/global/node_modules/.bin:$HOME/.yarn/bin:$HOME/Library/Python/3.7/bin:$HOME/.fastlane/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/opt/X11/bin:$HOME/.rvm/bin
$PREFIX: ''
$NPM_CONFIG_PREFIX: ''
$NVM_NODEJS_ORG_MIRROR: ''
$NVM_IOJS_ORG_MIRROR: ''
shell version: 'zsh 5.3 (x86_64-apple-darwin18.0)'
uname -a: 'Darwin 18.2.0 Darwin Kernel Version 18.2.0: Mon Nov 12 20:24:46 PST 2018; root:xnu-4903.231.4~2/RELEASE_X86_64 x86_64'
OS version: Mac 10.14.2 18C54
curl: /usr/bin/curl, curl 7.54.0 (x86_64-apple-darwin18.0) libcurl/7.54.0 LibreSSL/2.6.4 zlib/1.2.11 nghttp2/1.24.1
wget: not found
git: /usr/local/bin/git, git version 2.19.1
grep: /usr/bin/grep, grep (BSD grep) 2.5.1-FreeBSD
awk: /usr/bin/awk, awk version 20070501
sed: illegal option -- -
usage: sed script [-Ealn] [-i extension] [file ...]
       sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]
sed: /usr/bin/sed,
cut: illegal option -- -
usage: cut -b list [-n] [file ...]
       cut -c list [file ...]
       cut -f list [-s] [-d delim] [file ...]
cut: /usr/bin/cut,
basename: illegal option -- -
usage: basename string [suffix]
       basename [-a] [-s suffix] string [...]
basename: /usr/bin/basename,
rm: illegal option -- -
usage: rm [-f | -i] [-dPRrvW] file ...
       unlink file
rm: /bin/rm,
mkdir: illegal option -- -
usage: mkdir [-pv] [-m mode] directory ...
mkdir: /bin/mkdir,
xargs: illegal option -- -
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]
             [-L number] [-n number [-x]] [-P maxprocs] [-s size]
             [utility [argument ...]]
xargs: /usr/bin/xargs,
nvm current: v11.0.0
which node: $NVM_DIR/versions/node/v11.0.0/bin/node
which iojs: iojs not found
which npm: $NVM_DIR/versions/node/v11.0.0/bin/npm
npm config get prefix: $NVM_DIR/versions/node/v11.0.0
npm root -g: $NVM_DIR/versions/node/v11.0.0/lib/node_modules
  • nvm ls output:
Details
        v8.12.0
       v10.12.0
->      v11.0.0
         system
default -> v11 (-> v11.0.0)
node -> stable (-> v11.0.0) (default)
stable -> 11.0 (-> v11.0.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/dubnium (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.14.4 (-> N/A)
lts/carbon -> v8.12.0
lts/dubnium -> v10.13.0 (-> N/A)
  • How did you install nvm? (e.g. install script in readme, Homebrew):
    Tried both. I mostly do from brew though, since it's easier.

  • What steps did you perform?
    source the file

  • What happened?
    worked, but exits with non-zero value

  • What did you expect to happen?
    to exit with a 0 value since everything seemingly went well

  • Is there anything in any of your profile files (.bashrc, .bash_profile, .zshrc, etc) that modifies the PATH?
    I ran this without any $PATH alterations. In general I don't have anything in there that touch nvm. Only to include brew stuff.

@pvinis
Copy link
Author

pvinis commented Jan 19, 2019

The issue template does not work well with the details.. I tried to fix it but I failed.

@ljharb
Copy link
Member

ljharb commented Jan 20, 2019

Note that nvm is not supported with homebrew - if you brew uninstall it, and install it the proper way (in the readme), do you see the same behavior?

@ljharb
Copy link
Member

ljharb commented Jan 20, 2019

Thanks for the -x output, but it's not clear to me where it's erroring :-/ it's getting to the nvm_tree_contains_path call on line 2178 (via the nvm_die_on_prefix call on line 2995), and the only variable compared with -n after that is NVM_USE_OUTPUT - which is declared with local on line 2977.

The exit code 3 is returned from nvm in a few places:

  • if nvm ls-remote fails via nvm_remote_versions
  • if nvm ls fails
  • if nvm_version_dir fails (called internally, unlikely)
  • if nvm_version_path fails (called internally, unlikely)
  • if nvm_version fails

One possibility is, could you edit nvm.sh to change every return 3 to a different 2-digit number, and then see which callsite is causing the error?

@ljharb ljharb added the needs followup We need some info or action from whoever filed this issue/PR. label Jan 20, 2019
@pvinis
Copy link
Author

pvinis commented Jan 20, 2019

brew or manual, both give me this exit code.

@pvinis
Copy link
Author

pvinis commented Jan 20, 2019

that's a good idea. I'll try that tomorrow morning since I'm on my phone now.

Is there a reason actually that it returns the same number Ina few places? why not use different numbers for different places and different errors by default?

@ljharb
Copy link
Member

ljharb commented Jan 20, 2019

Typically I use unique numbers for each code path; but as nvm has gotten more complex, they've blurred together a bit. It would be a reasonable (but technically breaking) change to make sure every exit code in the app is unique (at least for things more complex than simple 0/1 predicates).

@pvinis
Copy link
Author

pvinis commented Jan 20, 2019

omg I changed all numbers to unique ones (except the 312 :p)
screen shot 2019-01-20 at 09 25 01
and it exits with 0.

I'll start putting them back to 3 and see how when it breaks again.

@pvinis
Copy link
Author

pvinis commented Jan 20, 2019

Hm, ok on my home macbook its 0 exit even with a freshly installed one. That's good. Then I'll check tomorrow at work with the problematic machine, and see what could be the issue.

@ljharb
Copy link
Member

ljharb commented Jan 20, 2019

Thanks for your efforts! I’m looking forward to fixing this, and perhaps convincing me it’s time for unique exit codes :-D

@pvinis
Copy link
Author

pvinis commented Jan 21, 2019

I found the reproducible way!

With nvm not in the system, uninstalled, cleared etc, no rc files, no nodes installed, try this:

  • go in a project dir with a .nvmrc
  • install nvm
  • source the nvm.sh file.
    it exits with code 3 that comes from https://github.com/creationix/nvm/blob/master/nvm.sh#L2944.
    nvm is correctly sourced, and works fine, but it prints that i need run nvm install which makes sense, but also returns with 3.

Basically, the problem here is sourcing nvm in a dir that has a .nvmrc in it. If I install and source outside, then go in the dir, it's fine, and when I do nvm use it will complain and return 3, which is great.

I don't know how this should be handled. I am basically trying to setup a project with ci, and in the ci I am already in the dir, and I install nvm, then source nvm, then nvm install, then nvm use. This order would work if sourcing would not try to do nvm use inside it I'm guessing.

What do you think?

@ljharb
Copy link
Member

ljharb commented Jan 21, 2019

Interesting. You can source with --no-use, or with --install to autoinstall - does this address it for you?

(I'd expect nvm to be sourced long before any CI system cd's into a project directory)

@pvinis
Copy link
Author

pvinis commented Jan 21, 2019

I'll try it.

Well, if the ci is using a node image, usually nvm is there already. If it's booting a macos image though, nvm is probably missing, so the init happens, cds in my project and then I get control, when I would start installing nvm and other things that I need.

@ljharb
Copy link
Member

ljharb commented Feb 25, 2019

This might be due to a bug in bash 3 (which ships on Macs) which makes set -e exit even on tests that exit nonzero.

@pvinis
Copy link
Author

pvinis commented Feb 25, 2019

To be honest, I switched to nodenv since last month. Feel free to keep this issue open to solve this if it's still a problem, or close it if you feel it's not relevant anymore.

nvm, thank you for your service :D

@ljharb
Copy link
Member

ljharb commented Feb 25, 2019

Sorry to hear that :-)

I believe I've solved it with 58d0933, so I'll close.

@ljharb ljharb closed this as completed Feb 25, 2019
@pvinis
Copy link
Author

pvinis commented Feb 25, 2019

I will try master out of curiosity :D

@JoseLion
Copy link

JoseLion commented Apr 4, 2021

@ljharb I just tested on Ubuntu 18.04.5 LTS with version v0.38.0, and I'm still getting the issue. This is usually a problem in CI environments. For instance, on CircleCI, when using the Node Orb, the installation will fail if you have a .nvmrc file in the working directory. Removing the .nvmrc file solves the issue, though. I hope this info is useful, and I'm also attaching an image of the output, which could be useful too 🙂

image

Cheers!

@ljharb
Copy link
Member

ljharb commented Apr 4, 2021

@JoseLion is this the same setup, then? Circle is sourcing nvm after cd-ing into the project’s directory, and before running nvm install? If so, this might be something that circle’s image could fix.

@JoseLion
Copy link

JoseLion commented Apr 5, 2021

I'm not sure I understand why CircleCI's image could fix it 😅
But just for clarity, I'm installing NVM (and NodeJS) manually in the job's steps, which is exactly what the Official Node Orb does:

jobs:
  build-web:
    working_directory: ~/repo/web

    docker:
      - image: cimg/base:stable

    steps:
      - checkout:
          path: ~/repo

      - run:
          name: Install NodeJS
          command: |
            curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
            echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
            echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
            source $BASH_ENV
            nvm install
            nvm alias default
            echo 'nvm use default &>/dev/null' >> $BASH_ENV

Any run command is executed in the working_directory where the .nvmrc file usually is. I'm actually trying to use that file when nvm install and nvm alias default are executed. Of course, as a workaround, I can do cd .. just before fetching the installer and set the NodeJS version manually in the command, but that's not ideal if I already have a file to track and centralize the version, right?

Also, it would be harder to for the Official Node Orb to use this workaround since it would have to check the working directory for a .nvmrc file and do cd <somewhere?> if the file exists. Which again, it's not ideal because not all projects will have the .nvmrc file.

I really hope this helps to clarify the issue, NVM is a great tool and the .nvmrc file is very helpful too! 🙂

@ljharb
Copy link
Member

ljharb commented Apr 5, 2021

Right - the issue is that nvm expects that if an .nvmrc file is present with a node version in it, that before cd-ing into that directory, you'd source nvm. So, in your case, you can change to this:

          command: |
            curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
            echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
            echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --install' >> $BASH_ENV
            source $BASH_ENV

(note that nvm alias default just echoes the current default alias, and that the nvm use default seems unnecessary)

@JoseLion
Copy link

JoseLion commented Apr 5, 2021

@ljharb got you, that makes sense! I used --install as you suggested, and it worked perfectly. 👍🏼

Do you think it would be possible to add a fix for this? Probably ignoring the .nvmrc file when nvm.sh is sourced? (I'm not sure if that would break other features, though 🤔). If not, probably it's something worth adding to the documentation to help other folks understand what could cause this exit code 3 issue, WDYT? 🙂

@ljharb
Copy link
Member

ljharb commented Apr 5, 2021

It would break other features, unfortunately :-/ specifically, the feature to auto-use the nvmrc version at source time, which in non-CI use cases, is already installed.

There may be a way to either detect CI, and do something different; or, only when sourcing nvm.sh, avoid failing when an nvmrc version is not present.

@ljharb ljharb reopened this Apr 5, 2021
@ljharb ljharb changed the title sourcing nvm.sh exits with code 3 Apr 5, 2021
@JoseLion
Copy link

JoseLion commented Apr 5, 2021

That would be awesome! Thanks 🙂 🎉

@agarzola
Copy link

I’m experiencing this issue in our Platform.sh configuration. Here’s what we’re running (following the most recently-suggested solution above):

hooks:
    build: |
        set -e
        unset NPM_CONFIG_PREFIX
        curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.38.0/install.sh | dash
        # Add required environment variables to profile.
        echo 'export NVM_DIR="$PLATFORM_APP_DIR/.nvm"' >> $PLATFORM_APP_DIR/.profile
        echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --install' >> $PLATFORM_APP_DIR/.profile
        # Source profile.
        . $PLATFORM_APP_DIR/.profile
        nvm current
        # TODO: Specifying the version should not be necessary, but Platform
        # builds fail without a specific version.
        nvm install
        nvm use
        nvm install-latest-npm

(Note that in Platform.sh environments, $PLATFORM_APP_DIR resolves to the directory where our application is installed.)

nvm is exiting with 3. What am I missing?

@bpossolo
Copy link

bpossolo commented Mar 14, 2022

i ran into this problem on CircleCI when trying to source the nvm.sh file in the project directory (where an .nvmrc file is present).
it would exit with error code 3 and no helpful message.
i solved it by explicitly setting the NVM_DIR variable before sourcing nvm.sh

echo 'export NVM_DIR=~/.nvm' >> $BASH_ENV
echo 'source ~/.nvm/nvm.sh' >> $BASH_ENV
source $BASH_ENV

edit: looks like this didn't fix it after all...

@ljharb
Copy link
Member

ljharb commented Mar 14, 2022

#1985 (comment) remains my position; a PR to improve the error message would be much appreciated.

@bpossolo
Copy link

bpossolo commented Mar 16, 2022

it turns out my fix (defining NVM_DIR before sourcing nvm.sh) didn't actually fix it...

The issue is that you’re setting -e; you shouldn’t be (largely ever). nvm does not survive that mode.
this

that option is being set by circleci when executing the build.
it is helpful to have the build stop when the build script encounters an error.

more philosophically, it seems pretty counterintuitive that the nvm.sh script would fail prematurely simply due to the presence of another completely valid and core nvm-related file (.nvmrc).

a helpful error message would spare debugging time but it still requires the user to jump through weird hoops to get it to work.

if there is already a command line option (--install) to explicitly install node in the same step as sourcing, it feels like it would make sense for the default behaviour (without that option) to simply ignore an .nvmrc file
if that doesn't make sense to be the default behaviour, then a new option (i.e. --ignore-nvmrc) would be equally helpful in overcoming this issue.

as it stands, the .nvm.sh script is failing prematurely which may not be a problem for the time being (since apparently nothing important happens after the failure), but it feels brittle/dangerous to assume this will always be the case.
in other words, if the premature failure is indeed irrelevant, one could argue it should "fail" with a zero status code and a warning logged to the console

@ljharb
Copy link
Member

ljharb commented Mar 16, 2022

You can unset it in your circle config, no?

I completely agree that a helpful error message isn't as good as improving the core problem, but it's a start.

The default behavior is "use", the optional behavior is "install". Both (must) read .nvmrc when present.

I think the action items here are initially to improve the error message.

After that is completed, it's worth considering some of these options:

  1. configure your CI to start its shell "not in the repo (which has .nvmrc)"
  2. disable set -e, because enabling it is a widespread bad practice
  3. add some kind of new option or changed behavior so it doesn't fail, but you don't end up with the node version you want
  4. add --install to the sourcing options in your profile file, which would result in you actually getting the node version you want
@bpossolo
Copy link

i tried adding --install as you suggested in line 4 and tested it out on my local workstation.
it looks like it's attempting to install the wrong version of node (see below)...
notice the project .nvmrc file contains 16.2.0 but the source nvm.sh step is reporting 10.4.0

M-14292:~/projects/member-management-ui$ nvm ls
->      v10.4.0
       v10.14.2
       v12.10.0
       v12.18.4
       v12.20.0
       v14.15.4
        v16.2.0
default -> 10.4.0 (-> v10.4.0)
node -> stable (-> v16.2.0) (default)
stable -> 16.2 (-> v16.2.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/gallium (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.10 (-> N/A)
lts/fermium -> v14.19.0 (-> N/A)
lts/gallium -> v16.14.1 (-> N/A)

M-14292:~/projects/member-management-ui$ cat .nvmrc 
16.2.0

M-14292:~/projects/member-management-ui$ . ~/.nvm/nvm.sh --install
v10.4.0 is already installed.

M-14292:~/projects/member-management-ui$ echo $?
0
@ljharb
Copy link
Member

ljharb commented Mar 16, 2022

ah, the default alias takes precedence over .nvmrc when present. If you nvm unalias default you should be closer to the state CI is in.

@omarrelativity
Copy link

omarrelativity commented Aug 26, 2023

Still running into this issue with v0.39.4. My workaround is to rename .nvmrc, source nvm, then rename back. This is my Makefile target:

nvm:
	# Have to rename .nvmrc then source nvm or it errors, see https://github.com/nvm-sh/nvm/issues/1985#issuecomment-456022013
	mv .nvmrc .nvmrc_temp

	# Need source nvm this way in make, see https://github.com/nvm-sh/nvm/issues/1446#issuecomment-816446454
	. ${NVM_DIR}/nvm.sh && mv .nvmrc_temp .nvmrc && nvm install && nvm use 
@ljharb
Copy link
Member

ljharb commented Aug 26, 2023

@omarrelativity you can source it with --no-use and skip the nvmrc rename step, and if you source it with --install instead, it'll do all that in one go for you.

@omarrelativity
Copy link

omarrelativity commented Aug 26, 2023

I tried both options already, still get Error 3:

nvm-install:
	. ${NVM_DIR}/nvm.sh --install
	
nvm-no-use:
	. ${NVM_DIR}/nvm.sh --no-use
@ljharb
Copy link
Member

ljharb commented Aug 26, 2023

and you have +e set, since -e isn't a good thing to be enabling? if so, then that's a legitimate bug i'd love more info on.

@omarrelativity
Copy link

I'm not sure what is set, I'm running this in a simple ubuntu:latest Docker container with nvm/curl/make/azure-cli/pulumi installed. Makefile doesn't have anything fancy in it.

$SHELLOPTS shows braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

Doing echo $$SHELLOPTS in the Makefile shows an empty line as output.

@ljharb
Copy link
Member

ljharb commented Aug 26, 2023

@omarrelativity i think this warrants a separate issue, since those sourcing options should work. mind filing it?

@omarrelativity
Copy link

Issue created #3177

@rpatterson
Copy link

I encountered this and under the same conditions I also reproduced #1769, so I think these issues are related, maybe duplicates depending on the root cause.

rpatterson added a commit to rpatterson/project-structure that referenced this issue Oct 16, 2023
@dssjoblom
Copy link

Still encountering this while trying to install NVM in a Dockerfile based on Debian bullseye.

ethitter added a commit to penske-media-corp/github-workflows-wordpress that referenced this issue Aug 7, 2024
ethitter added a commit to penske-media-corp/github-workflows-wordpress that referenced this issue Aug 7, 2024
@ghostd
Copy link

ghostd commented Aug 19, 2024

I experimented the same issue when i needed to use nvm in a Jenkins pipeline for a project with .nvmrc file.

My first attempt was:

stage('Install Node.js') {
    steps {
        script {
            sh '''
                [ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh" --install
            '''
        }
    }
}

Investigation

After digging i found:

  • nvm.sh does not "see" the arguments, and it always runs in "use" mode ($# always equals 0).
  • by default, the Jenkins shell step invokes /bin/sh
  • on the server (Ubuntu 22.04.4 LTS) hosting Jenkins, /bin/sh is a symbolic link to dash

Reproduction

I can reproduce the issue with the following steps:

  • in a directory with a .nvmrc file, create a tmp.txt file with the following content [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --no-use
  • with a user which has no nvm loaded, execute the following command: /bin/sh -xe tmp.txt

Workaround

I changed the shell used to run my Jenkins step with bash and it works:

stage('Install Node.js') {
    steps {
        script {
            sh '''#!/bin/bash -xe
                [ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh" --install
            '''
        }
    }
}

BTW, I tried the workaround of temporarily renaming .nvmrc, and it also worked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs followup We need some info or action from whoever filed this issue/PR.