用 nvm alias default version 更改預設的 node 版本,但 shell 重開後依然是原來的 node 版本

結論

根據官網 Automatically call nvm use,在 ~/.bashrc 檔案最後面加上

cdnvm() {
    cd "$@";
    nvm_path=$(nvm_find_up .nvmrc | tr -d '\n')

    # If there are no .nvmrc file, use the default nvm version
    if [[ ! $nvm_path = *[^[:space:]]* ]]; then

        declare default_version;
        default_version=$(nvm version default);

        # If there is no default version, set it to `node`
        # This will use the latest version on your machine
        if [[ $default_version == "N/A" ]]; then
            nvm alias default node;
            default_version=$(nvm version default);
        fi

        # If the current version is not the default version, set it to use the default version
        if [[ $(nvm current) != "$default_version" ]]; then
            nvm use default;
        fi

        elif [[ -s $nvm_path/.nvmrc && -r $nvm_path/.nvmrc ]]; then
        declare nvm_version
        nvm_version=$(<"$nvm_path"/.nvmrc)

        declare locally_resolved_nvm_version
        # `nvm ls` will check all locally-available versions
        # If there are multiple matching versions, take the latest one
        # Remove the `->` and `*` characters and spaces
        # `locally_resolved_nvm_version` will be `N/A` if no local versions are found
        locally_resolved_nvm_version=$(nvm ls --no-colors "$nvm_version" | tail -1 | tr -d '\->*' | tr -d '[:space:]')

        # If it is not already installed, install it
        # `nvm install` will implicitly use the newly-installed version
        if [[ "$locally_resolved_nvm_version" == "N/A" ]]; then
            nvm install "$nvm_version";
        elif [[ $(nvm current) != "$locally_resolved_nvm_version" ]]; then
            nvm use "$nvm_version";
        fi
    fi
}
alias cd='cdnvm'
cd $PWD

那麼,只要專案根目錄裡面,沒有用 .nvmrc 檔指定 node 版本,就會用 default 的版本。如果有 .nvmrc 檔的話,會優先使用 .nvmrc 檔指定 node 版本。

還沒做任何嘗試前的環境

作業系統:MacOS Big Sur 11.5.1

shell: bash

echo $NVM_DIR 印出來的東西:/Users/flora/.nvm

which node 印出來的東西: /Users/flora/.nvm/versions/node/v10.15.0/bin/node

which npm 印出來的東西:/Users/flora/.nvm/versions/node/v10.15.0/bin/npm

which iojs:(什麼都沒印出來)

npm config get prefix 印出來的東西:/Users/flora/.nvm/versions/node/v10.15.0

npm root -g 印出來的東西:/Users/flora/.nvm/versions/node/v10.15.0/lib/node_modules

nvm ls 印出來的東西:

->     v10.15.0
       v10.24.1
       v14.16.0
default -> 10.15.0 (-> v10.15.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v14.16.0) (default)
stable -> 14.16 (-> v14.16.0) (default)
lts/* -> lts/fermium (-> 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
lts/erbium -> v12.22.6 (-> N/A)
lts/fermium -> v14.17.6 (-> N/A)

echo $PATH 印出來的東西:

  /Users/flora/.pyenv/shims:
  /Users/flora/.pyenv/bin:
  /Users/flora/.nvm/versions/node/v10.15.0/bin:
  /Users/flora/bin:
  /usr/local/bin:
  /Users/flora/.pyenv/plugins/pyenv-virtualenv/shims:
  /usr/local/bin:
  /usr/bin:
  /bin:
  /usr/sbin:
  /sbin:
  /Users/flora/Library/Python/3.9/bin:
  /Users/flora/opt/anaconda3/bin:
  /Users/flora/opt/anaconda3/condabin:
  /Users/flora/.pyenv/shims:
  /Users/flora/.pyenv/bin:
  /Users/flora/.nvm/versions/node/v10.15.0/bin:
  /Users/flora/bin:
  /Users/flora/.local/bin:
  /Users/flora/.local/bin

~/.bash_profile(跟 nvm 相關的地方在 20~22 行):

function parse_git_dirty {
  if [[ $(git status 2> /dev/null | tail -n1) == "nothing to commit, working tree clean" ]]; then
    echo "✔ "
  else
   echo "✘ "
  fi
}

function git_branch {
    ref=$(git symbolic-ref HEAD 2> /dev/null) || return;
    echo "("$(parse_git_dirty)${ref#refs/heads/}") ";
}

if [ -f $HOME/.bashrc ]; then
        source $HOME/.bashrc
fi

export PATH=$HOME/bin:/usr/local/bin:$PATH
export PS1='[\[\e[1;32m\]\w\[\e[0m\]] \[\e[1;36m\]$(git_branch)\[\e[0m\]$ '
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/Users/flora/opt/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/Users/flora/opt/anaconda3/etc/profile.d/conda.sh" ]; then
        . "/Users/flora/opt/anaconda3/etc/profile.d/conda.sh"
    else
        export PATH="/Users/flora/opt/anaconda3/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<

~/.bashrc(裡面只有設定跟 python 相關的東西):

eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
export PATH="$PATH:/Users/flora/.local/bin"

問題描述

原本使用的 node 版本是 10.15.0。設定 nvm alias default 14.16.0,然後關掉終端機。重開終端機後 nvm ls 印出的東西

->     v10.15.0
       v10.24.1
       v14.16.0
default -> 14.16.0 (-> v14.16.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v14.16.0) (default)
stable -> 14.16 (-> v14.16.0) (default)
lts/* -> lts/fermium (-> 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
lts/erbium -> v12.22.6 (-> N/A)
lts/fermium -> v14.17.6 (-> N/A)

default 是改成 14.16.0 了,但使用的依然是 10.15.0,而不是我期望的 14.16.0。我不想每次打開終端機都要輸入一次 nvm use default 來切換版本啊。

嘗試過的解決方式

參考 nvm keeps "forgetting" node in new terminal session

把 ~/.bash_profile 中跟 nvm 相關的地方,移到檔案最底下:

function parse_git_dirty {
  if [[ $(git status 2> /dev/null | tail -n1) == "nothing to commit, working tree clean" ]]; then
    echo "✔ "
  else
   echo "✘ "
  fi
}

function git_branch {
    ref=$(git symbolic-ref HEAD 2> /dev/null) || return;
    echo "("$(parse_git_dirty)${ref#refs/heads/}") ";
}

if [ -f $HOME/.bashrc ]; then
        source $HOME/.bashrc
fi

export PATH=$HOME/bin:/usr/local/bin:$PATH
export PS1='[\[\e[1;32m\]\w\[\e[0m\]] \[\e[1;36m\]$(git_branch)\[\e[0m\]$ '

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/Users/flora/opt/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/Users/flora/opt/anaconda3/etc/profile.d/conda.sh" ]; then
        . "/Users/flora/opt/anaconda3/etc/profile.d/conda.sh"
    else
        export PATH="/Users/flora/opt/anaconda3/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

存檔後重新開啟終端機,nvm ls 印出來的東西,沒有使用 default 的 14.16.0,依然是原本的 10.15.0。

echo $PATH 印出來的東西:

/Users/flora/.nvm/versions/node/v10.15.0/bin:
/Users/flora/.pyenv/shims:
/Users/flora/.pyenv/bin:
/Users/flora/bin:
/usr/local/bin:
/Users/flora/.pyenv/plugins/pyenv-virtualenv/shims:
/usr/local/bin:
/usr/bin:
/bin:
/usr/sbin:/sbin:
/Users/flora/Library/Python/3.9/bin:
/Users/flora/opt/anaconda3/bin:
/Users/flora/opt/anaconda3/condabin:
/Users/flora/.pyenv/shims:
/Users/flora/.pyenv/bin:
/Users/flora/.nvm/versions/node/v10.15.0/bin:
/Users/flora/bin:
/Users/flora/.local/bin:
/Users/flora/.local/bin

觀察修改前後 $PATH 的變化:「/Users/flora/.nvm/versions/node/v10.15.0/bin:」一樣是出現兩次,而且都是 10.15.0。不過第一次出現的時間提早了,從第三行提前到第一行。

繼續修改 ~/.bash_profile:在原本的 nvm 指令後面加上新的兩行(第 4~5 行),在 shell 裡面指定要使用預設的版本:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
NODE_DEFAULT_VERSION=$(<"$NVM_DIR/alias/default")
export PATH="$NVM_DIR/versions/node/$NODE_DEFAULT_VERSION/bin":$PATH

存檔後重新開啟終端機,nvm ls 印出來的東西,沒有使用 default 的 14.16.0,依然是原本的 10.15.0。

echo $PATH 印出來的東西:

/Users/flora/.nvm/versions/node/14.16.0/bin:
/Users/flora/.nvm/versions/node/v10.15.0/bin:
/Users/flora/.pyenv/shims:
/Users/flora/.pyenv/bin:
/Users/flora/bin:
/usr/local/bin:
/Users/flora/.pyenv/plugins/pyenv-virtualenv/shims:
/usr/local/bin:
/usr/bin:
/bin:
/usr/sbin:
/sbin:
/Users/flora/Library/Python/3.9/bin:
/Users/flora/opt/anaconda3/bin:
/Users/flora/opt/anaconda3/condabin:
/Users/flora/.pyenv/shims:
/Users/flora/.pyenv/bin:
/Users/flora/.nvm/versions/node/v10.15.0/bin:
/Users/flora/bin:
/Users/flora/.local/bin:
/Users/flora/.local/bin

觀察修改前後 $PATH 的變化:在最上方多了一行「/Users/flora/.nvm/versions/node/14.16.0/bin:」,裡面的版本是我想要用的沒錯,但是為什麼最後出現的不是你啊!另外那兩行咬定青山不放鬆的「/Users/flora/.nvm/versions/node/v10.15.0/bin:」到底是哪來的⋯⋯

which node 印出來的東西依然是 /Users/flora/.nvm/versions/node/v10.15.0/bin/node

說好的「在 $PATH 裡面排得越前面的,越優先使用」呢?難道你不是從 $PATH 去找要執行哪個版本的 node 的嗎? T口T

檢查 shell 語法中用到的 "$NVM_DIR/bash_completion" 檔案。檔案打開後⋯⋯更多的 shell 語法,一時之間也看不懂。搜尋到 bash-completion 這篇文章,看樣子好像是讓我們只要輸入指令開頭幾個字,按 tab 鍵就會自動幫我們寫完後面的指令。兇手應該不是他。

檢查 shell 語法中用到的 "$NVM_DIR/nvm.sh" 檔案,這是個非常長的檔案⋯⋯嗯算了我放棄。我先來試試看更新 nvm。按照官網的說法,輸入指令 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash

終端機說

(base) [~] $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 14926  100 14926    0     0  12934      0  0:00:01  0:00:01 --:--:-- 12934
=> nvm is already installed in /Users/flora/.nvm, trying to update using git
=> => Compressing and cleaning up git repository

=> Appending nvm source string to /Users/flora/.bashrc
bash: line 414: /Users/flora/.bashrc: Permission denied
=> Appending bash_completion source string to /Users/flora/.bashrc
bash: line 421: /Users/flora/.bashrc: Permission denied
=> Close and reopen your terminal to start using nvm or run the following to use it now:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

好像是想要修改我的 ~/.bashrc 檔案但是失敗了。我猜他是想要要在 ~/.bashrc 裡面加上倒數那三行東西。不過那三行已經在我的 ~/.bash_profile 裡面了,我猜應該是沒關係。重新開啟終端機,node 依然不是使用 default 版本⋯⋯

根據官網,在專案根目錄放一個「.nvmrc」檔,裡面寫「v14.16.0」,然後在終端機寫 nvm use 就會使用那個版本。

官網說,為了讓電腦自動執行 nvm use ,要在 ~/.bashrc 裡面加這一段:

cdnvm() {
    cd "$@";
    nvm_path=$(nvm_find_up .nvmrc | tr -d '\n')

    # If there are no .nvmrc file, use the default nvm version
    if [[ ! $nvm_path = *[^[:space:]]* ]]; then

        declare default_version;
        default_version=$(nvm version default);

        # If there is no default version, set it to `node`
        # This will use the latest version on your machine
        if [[ $default_version == "N/A" ]]; then
            nvm alias default node;
            default_version=$(nvm version default);
        fi

        # If the current version is not the default version, set it to use the default version
        if [[ $(nvm current) != "$default_version" ]]; then
            nvm use default;
        fi

        elif [[ -s $nvm_path/.nvmrc && -r $nvm_path/.nvmrc ]]; then
        declare nvm_version
        nvm_version=$(<"$nvm_path"/.nvmrc)

        declare locally_resolved_nvm_version
        # `nvm ls` will check all locally-available versions
        # If there are multiple matching versions, take the latest one
        # Remove the `->` and `*` characters and spaces
        # `locally_resolved_nvm_version` will be `N/A` if no local versions are found
        locally_resolved_nvm_version=$(nvm ls --no-colors "$nvm_version" | tail -1 | tr -d '\->*' | tr -d '[:space:]')

        # If it is not already installed, install it
        # `nvm install` will implicitly use the newly-installed version
        if [[ "$locally_resolved_nvm_version" == "N/A" ]]; then
            nvm install "$nvm_version";
        elif [[ $(nvm current) != "$locally_resolved_nvm_version" ]]; then
            nvm use "$nvm_version";
        fi
    fi
}
alias cd='cdnvm'
cd $PWD

不過,我是把 nvm 相關指令放在 ~/.bash_profile 裡面,所以這一段我也是放在 ~/.bash_profile 裡面,變成這樣:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
NODE_DEFAULT_VERSION=$(<"$NVM_DIR/alias/default")
export PATH="$NVM_DIR/versions/node/$NODE_DEFAULT_VERSION/bin":$PATH
cdnvm() {
    cd "$@";
    nvm_path=$(nvm_find_up .nvmrc | tr -d '\n')

    # If there are no .nvmrc file, use the default nvm version
    if [[ ! $nvm_path = *[^[:space:]]* ]]; then

        declare default_version;
        default_version=$(nvm version default);

        # If there is no default version, set it to `node`
        # This will use the latest version on your machine
        if [[ $default_version == "N/A" ]]; then
            nvm alias default node;
            default_version=$(nvm version default);
        fi

        # If the current version is not the default version, set it to use the default version
        if [[ $(nvm current) != "$default_version" ]]; then
            nvm use default;
        fi

        elif [[ -s $nvm_path/.nvmrc && -r $nvm_path/.nvmrc ]]; then
        declare nvm_version
        nvm_version=$(<"$nvm_path"/.nvmrc)

        declare locally_resolved_nvm_version
        # `nvm ls` will check all locally-available versions
        # If there are multiple matching versions, take the latest one
        # Remove the `->` and `*` characters and spaces
        # `locally_resolved_nvm_version` will be `N/A` if no local versions are found
        locally_resolved_nvm_version=$(nvm ls --no-colors "$nvm_version" | tail -1 | tr -d '\->*' | tr -d '[:space:]')

        # If it is not already installed, install it
        # `nvm install` will implicitly use the newly-installed version
        if [[ "$locally_resolved_nvm_version" == "N/A" ]]; then
            nvm install "$nvm_version";
        elif [[ $(nvm current) != "$locally_resolved_nvm_version" ]]; then
            nvm use "$nvm_version";
        fi
    fi
}
alias cd='cdnvm'
cd $PWD

然後重新打開終端機,就變成 .nvmrc 裡指定的版本了。

疑問:.nvmrc 要放到 .gitignore 裡嗎?我猜應該要放,因為只是要指定本地專案使用的 node 版本。

仔細看官網提供的那段程式碼,裡面有段註解說「# If there are no .nvmrc file, use the default nvm version」。如果沒有 .nvmrc 檔,會使用預設版本!這樣就不用擔心「.nvmrc 要放到 .gitignore 裡嗎?」的事了。

Shell 語法參考資料

Comments

Popular posts from this blog

Alpha Camp 全端開發課程學習心得

在 javascript 用 regular expression 為金額加上千位數分隔符號

shop_platform - sqlalchemy.exc.TimeoutError