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

Respect previously nvm-loaded node version when re-sourcing #1315

Merged
merged 1 commit into from
Jun 10, 2018
Merged

Respect previously nvm-loaded node version when re-sourcing #1315

merged 1 commit into from
Jun 10, 2018

Conversation

zeorin
Copy link
Contributor

@zeorin zeorin commented Nov 21, 2016

Sometimes nvm.sh may be sourced more than once (e.g. when using direnv and nvm-zsh's lazy-loading feature). If nvm has already loaded a version of node that is not system, don't change that version.

In practice this means that if the currently active version of node is not system, nvm.sh will not change the version of node that is active.

If the version is system, it will still activate default.

@zeorin
Copy link
Contributor Author

zeorin commented Nov 21, 2016

Just thought of something: if there's no system node version, what happens then? What does nvm_ls_current return? none? Should I include this in the check somehow?

@ljharb
Copy link
Member

ljharb commented Nov 21, 2016

Yes, it returns "none".

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be sourcing nvm.sh more than once that wouldn't be checking for nvm functions already existing?

nvm.sh Outdated
nvm use --silent "$VERSION" >/dev/null
elif nvm_rc_version >/dev/null 2>&1; then
nvm use --silent >/dev/null
if [ "_$NVM_CURRENT" = '_system' ]; then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current behavior is that whenever I re-source nvm.sh, I get the auto-used version. With this change, that won't be the case.

(also, yes, this needs to handle "none")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, that's the idea. The fact that NVM assumes, whenever it's sourced, that the environment is uninitialized is a hindrance to me.

(I've pushed a fix to handle none.)

@zeorin
Copy link
Contributor Author

zeorin commented Nov 21, 2016

nvm functions not existing in the current shell doesn't mean that nvm hasn't been executed or that the environment isn't set up to use a specific version of node. Those functions simply may not have made it back up the scope chain.

My particular use case is when a shell script that direnv executes sources nvm.sh and then runs nvm use (there's an .nvmrc file in the folder, too). However, nvm would have been loaded and executed in the sub-shell that direnv uses for this. The environment variables do persist into my shell, but the functions don't.

I'm also using zsh-nvm, with lazy-loading, meaning that when I run nvm for the 1st time in a session myself, nvm.sh is being sourced. Sometimes this is after direnv has run and set up the environment with nvm's help.

Without this fix, that means as soon as I run a global npm cli executable (which are also lazy-loaded by zsh-env) in a direnv managed folder, nvm thinks it's running for the 1st time and changes the node version from the version specified in the .nvmrc file and already loaded by nvm (when it was executed by direnv) to default.

With this fix, the loaded version of node persists.

@ljharb
Copy link
Member

ljharb commented Nov 21, 2016

I'll leave this open while I think more about it, but as discussed in #1308, there's a few reasons I'm hesitant. "lazy loading" is not a supported use case for nvm - it's just one some people choose to employ, at their own risk - and if the nvm functions aren't "in scope", then (even if nvm originally set up your PATH) nvm is not actually being used. The functions need to be sourced in your main shell (not in a subshell) for you to be using nvm.

@zeorin
Copy link
Contributor Author

zeorin commented Nov 21, 2016

I appreciate that lazy loading isn't officially supported. But without it, NVM is simply too much overhead to be worth it for me. Using lazy loading for NVM changed my shell startup time on my laptop from 4+ seconds to <0.5 seconds. I'm a big tmux user and I'm opening up shells all. the. time. so that startup time has a big impact in my ability to remain in a "flow" state when developing.

Of course the functions have to be loaded for NVM to do its work. Respecting a previously loaded version of node (by another instance of NVM, even if that instance is no longer present in the current shell) opens up possibilities for NVM to be used in more non-interactive ways, not just by direnv, but also by deployment tools & workflows, and many other as-yet-unimagined uses.

Neither #1316 nor this pull request break anything (as far as the tests are able to indicate), so there should be no impact on current users. I do realize that both these pull requests are decisions about the future of NVM, how it might be used, where it fits in. I appreciate your considered attention to these requests.

@zeorin
Copy link
Contributor Author

zeorin commented Jan 19, 2017

if the nvm functions aren't "in scope", then (even if nvm originally set up your PATH) nvm is not actually being used. The functions need to be sourced in your main shell (not in a subshell) for you to be using nvm.

In my use case, as soon as any nvm command is issued in the shell (direnv has run nvm use in a sub-shell at this point), or node, npm or globally installed npm module is executed, nvm is then loaded (by the lazy loader). Then, nvm's initialization loads the functions into the current shell (and clobbers the existing env vars that the direnv-executed nvm set up—hence this pull request).

@zeorin
Copy link
Contributor Author

zeorin commented Jun 20, 2017

@ljharb I've been struggling with this build issue for ages. If I run the code inside the test manually (after first running test/sourcing/setup) it all works just fine. I cannot reproduce this issue on my local machine.

Would you mind taking a look to see if you can determine why this test is failing?

NVM_LS_CURRENT="$(nvm ls current | strip_colors | \grep -o v0.10.1)"
[ "_$NVM_LS_CURRENT" = '_v0.10.1' ] || die "'nvm ls current' did not return '-> v0.10.1', got '$NVM_LS_CURRENT'"
[ "_$NVM_LS_CURRENT" = '_v0.10.1' ] || die "'nvm ls current' did not return '-> v0.10.1', got '$NVM_LS_CURRENT_NOT_GREPPED'"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be helpful here to do nvm ls ; die and see what shows up

@zeorin zeorin changed the title Don't change the node version if nvm.sh is re-sourced Respect previously nvm-loaded node version when re-sourcing Sep 7, 2017
@zeorin
Copy link
Contributor Author

zeorin commented Sep 9, 2017

@ljharb I managed to figure out how to run the tests locally (for shell in sh dash bash zsh; do node_modules/.bin/urchin -f -s $shell test/sourcing; done;), and as a result I was able to figure out why the test was failing even though it was working 100% fine if I simply executed the test commands in the shell manually.

Basically, it seems that on the TravisCI VM, nvm is loaded before the tests are run, and the sub shell in which a test is run inherits that environment. In test/sourcing/setup, nvm is unloaded, which cleans up that script's local environment, but not the ancestor's one, which is what the test script inherits.

Since this commit specifically makes nvm check whether or not there's already an nvm-managed version of node loaded by inspecting the environment, it won't load the default alias because nvm-specific enviroment variables are present. Working as intended.

My solution is to fix the test by unloading nvm again in the test itself (and caching and setting NVM_DIR, because that is unset on unload1), which sets up the environment correctly as if nvm had not yet been loaded.

Rebased on master.

1: Is this desired behaviour? If someone has an NVM_DIR that isn't $HOME/.nvm, and they source nvm.sh again without first setting NVM_DIR, is that a problem? Or is it simply up to them to set the correct NVM_DIR before sourcing? I feel like not unsetting NVM_DIR would be a Good Thing, feels like it would make nvm-related scripts more portable.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zeorin thanks for the explanation; I think I understand.

nvm.sh Outdated
elif nvm_rc_version >/dev/null 2>&1; then
nvm use --silent >/dev/null
if [ "_$NVM_CURRENT" = '_none' ] || \
[ "_$NVM_CURRENT" = '_system' ]; then

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@ljharb ljharb merged commit eabd7ab into nvm-sh:master Jun 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants