anyenv + ndenv を試す

2018年11月9日 0 開発 , ,

Node 11 へ更新したら mapbox/node-sqlite3 が動作しなくなった。#1063 によれば既に修正の準備はできているものの CI サービス AppVeyor が Node 11 対応していないので待ちとなっているらしい。

これまでも node-sqlite3 のように node-gyp を利用した npm で Node バージョン更新による問題に遭遇してきたが、概ね短期間で対応された。そのため Homebrew で Node を最新バージョンにしていたのだけど、今回は 2 週間を経ても未解決である。ちょうど直近で node-sqlite3 を業務利用する機会があるため、これは困る。

また自身も npm 開発することもあって、いつかは *env 系ツールで複数 Node バージョンを検証できなくてはなぁと思っていた。Current 以外の動作テストは Travis CI に丸投げしていたが、特定 Node バージョンで問題が起きた時は環境を切り替えて検証しなければならない。というわけで今回の問題を契機に anyenv + ndenv を試すことにした。

anyenv をインストールする

riywo/anyenv の手順に従い anyenv をインストール。まずは git clone。HOME 直下に .anyenv という名前で git clone する。

$ git clone https://github.com/riywo/anyenv ~/.anyenv

次に anyenv へパスを通す。手順だと echo でプロファイル末尾に出力しているが、既存の設定を確認してからにしたいので .bash_profile を vi で開く。

$ cd
$ vi .bash_profile

内容を確認して問題なければ好みの位置 (export をまとめている箇所など) や末尾に以下を追記して保存する。

export PATH="$HOME/.anyenv/bin:$PATH"
eval "$(anyenv init -)"

その後、有効化。手順では exec $SHELL -l になっている。今回は .bash_profile を自身で編集したから source コマンドで読み直した。これは単に手癖の話。

$ source .bash_profile

anyenv のインストール手順は Linux や macOS を想定しているようで Windows はどうするのだろう?と思ったが、現在は WSL があるからそちらでどうぞ、ということなのかもしれない。Windows 版のインストール手順をググって見つけた WSLでWindowsにLinux開発環境を構築する – Qiita では実際そうしている。

インストールに成功したことを確認するため、anyenv の提供する *env を表示。

$ anyenv install -l
Available **envs:
  Renv
  crenv
  denv
  erlenv
  exenv
  goenv
  hsenv
  jenv
  luaenv
  ndenv
  nenv
  nodenv
  phpenv
  plenv
  pyenv
  rbenv
  sbtenv
  scalaenv
  swiftenv

無事、成功したようだ。こうして見ると対応環境が多い。今回の対象となる Node 系だけでも ndenv、nenv、nodenv の 3 種類ある。これらを個別にインストールしたり設定するのは非常に面倒なので実にありがたい。

anyenv-update をインストールする

anyenv 管理下の *env を更新するために便利なプラグイン znz/anyenv-update をインストールする。これは anyenv の README からリンクされているので公認と考えてよいだろう。

まずはインストール先となるディレクトリーを作成。anyenv の手順でパスを通してあるから、その配下に入れる。作成後、実際にディレクトリーが存在することを確認。

$ mkdir -p $(anyenv root)/plugins
$ ls $(anyenv root)
README.md   bin         completions envs        libexec     plugins     share

anyenv-update をインストールして存在を確認。

$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
$ ls $(anyenv root)/plugins
anyenv-update

これを入れると

$ anyenv update

により *env をまとめて更新できる。

Homebrew 管理下 Node を削除する

まずはグローバルな npm を確認。必要なら ndenv に移行した後で入れ直すため、名前を控えておくこと。

$ npm ls -g --depth=0
/usr/local/lib
├── asar@0.14.3
├── eslint@5.3.0
├── eslint-config-standard@11.0.0
├── eslint-config-standard-react@6.0.0
├── eslint-plugin-import@2.13.0
├── eslint-plugin-node@7.0.1
├── eslint-plugin-promise@3.8.0
├── eslint-plugin-react@7.10.0
├── eslint-plugin-standard@3.1.0
├── npm@6.4.1
└── npm-check-updates@2.14.2

次にこれらをまとめてアンインストール。その後に npm が残っていないことを確認。

$ npm uninstall npm -g
removed 387 packages in 6.033s
$ npm ls -g --depth=0
-bash: /usr/local/bin/npm: No such file or directory

Node 本体をアンインストール。その後にバージョン表示コマンドを実行して失敗 = Node 消滅を確認。

$ brew uninstall node
Uninstalling /usr/local/Cellar/node/11.1.0... (3,936 files, 47.0MB)
$ node -v
-bash: node: command not found

Homebrew 環境が壊れていないことを確認。

$ brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: Broken symlinks were found. Remove them with `brew prune`:
  /usr/local/share/man/man5/package-lock.json.5
  /usr/local/share/man/man7/removing-npm.7
  /usr/local/share/man/man7/semver.7

依存関係の警告が出ているので brew prune を実行、回復したことを確認。

$ brew prune
Pruned 3 symbolic links and 10 directories from /usr/local
$ brew doctor
Your system is ready to brew.

これで Homebrew 管理下 Node を削除できた。もし which node して残骸があるようなら rm コマンドなどを実行して消す。私の環境では検出されなかったから、これで終わり。

ndenv をインストールする

anyenv 経由で ndenv をインストールする。実行してみて気づいたのだが anyenv install XXXX だけでは ndenv がコマンドとして認識されず exec $SHELL -l によるシェル再起動が必要だった。

インストール後、バージョン表示して存在を確認。

$ anyenv install ndenv
$ exec $SHELL -l
$ ndenv -v
ndenv 0.4.0-4-ga339097

ndenv の提供する Node バージョンを確認。Node だけでなく io.js も管理されている。

$ ndenv install -l
Available versions:
...前略
v10.12.0
v10.13.0
v11.0.0
v11.1.0
iojs-v1.0.0
後略...

Node バージョン管理

ndenv を用意したので

  • グローバルを最新となる Node 11
  • node-sqlite3 を必要とするプロジェクトでは Node 10

という環境を構築してみる。まずは各バージョンの最新版を入れる。インストール後には ndenv rehash を実行する必要あり。

$ ndenv install v11.1.0
$ ndenv install v10.13.0
$ ndenv rehash

Node 11 をグローバルに設定してから Node と npm のバージョンを確認。

$ ndenv global v11.1.0
$ node -v
v11.1.0
$ npm -v
6.4.1

成功。ndenv では Node と npm を一緒にインストールしてくれる。

試しに簡単なプログラムを実行したり、既存プロジェクトで npm test を起動するなどしてみたところ正常に動作することを確認できた。

次は特定のプロジェクトだけ Node 10 を有効化してみる。対象となる Node プロジェクトに移動してから以下を実行。その後にバージョン確認してみる。

$ ndenv local v10.13.0
$ node -v
v10.13.0

おお、無事に Node 10 が適用されている。すごい。

ndenv help local の解説を読むと .node-version というファイルを作成して ndenv はそこに記述された Node バージョンへ切り替えているとのことだった。設定がファイルになっているため、これをリポジトリーで共有すれば ndenv を利用するユーザー間で Node バージョンを固定することも可能。よい仕組みだ。

実際、node-sqlite3 を利用している既存プロジェクトを Node 10 に固定し、正常に動作することも確認できた。

まとめ

anyenv + ndenv で自由に Node バージョンを制御できる環境を手に入れた。契機となる node-sqlite3 互換問題も解決したので満足度が高い。今回は ndenv を試したが、他の言語による開発でも役立つだろう。