dotfilesをNix+home-managerでの管理へ移行するのをきっかけに、各開発環境の管理もNixへの移行を試行しています。
ここでは、JavaScriptのプロジェクト環境のためにnodejs/npmの環境を構築した際にやったことをまとめておきたいと思います。
JavaScriptの開発環境では、プロジェクト毎にnodeの要求versionが違うことがほとんどです。
nodejs環境の切り替えはnvmやnodebrew、最近だとvoltaなどで行うのが一般的かと思います。
ちなみに自分は nodebrew
を利用しています。
今回は、せっかくNixへ移行するので nix-shell
による環境の切り替えで実現したいと思います。
なお、ここでの環境構築はLinuxではなく、MacOS上で実施しています。
nix-shellによる仮想開発環境
nix-shell
はNixに付属しているコマンドです。
必要な依存derivationを構築して指定されたpathに配置した上で、interactive shellを起動してくれます。
このとき、これらの配置されたパッケージは実際のPATH配下のディレクトリにインストールされず、起動されたシェルだけで利用できるようになっています。 これはNixの仕組みによるものです。詳しくは、公式ドキュメントあたりを参照ください。
この仕組みを使うことで、再現可能な開発環境を構築することができます。
ちなみに同じようなツールに、Nix Flakesがあります。 こちらはNixの次世代パッケージ管理ツールで、依存パッケージのバージョンを固定するなどの機能があるようですが、まだあまり調査できていないです。 将来乗り替えていくかもしれません。
もうひとつdevboxがあります。先日v0.2がリリースされ、Nixのインストールも統合されたようです。 こちらはjetpack.io社が開発するNixの上で動作する開発環境構築ツールです。 JSONで依存関係を記述でき、環境をDodkerfileに書き出せることが特徴的な違いのようでした。
なお nix-shell
を開発環境として利用するための情報は、公式WikiのDevelopment environment with nix-shellに詳しいです。
shell.nixで依存を記述する
例えば、Reactのプロジェクトでnodejs 18系とyarnを使い、TypeScriptで開発するために以下のようなパッケージが必要だとします。
- nodejs 18.x/npm
- yarn
- typescript
- typescript-language-server
これを shell.nix
というファイルに記述して、プロジェクトルートなどに配置しておきます。
|
|
同ディレクトリで nix-shell
コマンドを実行すると、ここに記載されているパッケージを配置してシェルを起動してくれます。
このようなプロジェクト毎の環境をそれぞれ用意しておくことで、開発時に nix-shell
することで適宜必要な環境で開発をすることができるようになります。
古いnodejsのpackage作成
2023/1/7現在、nodejsのパッケージとしてNixpkgsで配布されているのはv14.x系より上のversionです。 そのため、これより古いv12やv10などはNixpkgsで取得できません。 従って、これらのパッケージは自分で補完する必要があります。
nodejsのソースを取得してビルドする
これは、Nixpkgsが実際に配布するときに利用している方法です。 これをそのまま再利用してローカルで必要なパッケージをビルドします。
以下の記事にその方法が紹介されていたので、参考にします。
ここでは複数のプロジェクトでの再利用を簡単にするために、 shell.nix
に直接記述せずに ./config/nix/config.nix
に記述することにします。
|
|
packageOverrides
で nodejs-12_x
というパッケージを追加します。上記ブログで紹介されていた、Nixpkgsの nodejs.nix
を再利用しています。
ここで、 nix-env
すると、追加したパッケージが確認できます。
|
|
あとは、 shell.nix
でこのパッケージを利用するだけです。このとき yarn
で利用するnodejsを指定するようにします。
|
|
ここで nix-shell
を実行すると、最初はnodeのビルドが走ります。一度ビルドされればcacheから利用されるようになります。
nodejsのビルド済みバイナリを取得する
一方でnodejsはビルド済みバイナリも配布されているので、時短やビルドエラーのためにそちらを利用したいケースもあるでしょう。
こちらも、以下の記事でバイナリを利用する方法が紹介されていたので参考にします。
nodejs.nix
というファイルを用意して、以下のように記述します。
|
|
元記事のコードではビルドに必要になるツールを指定していますが、これはバイナリ配布物なので実際はtarを解凍して配置するだけです。 なので、不要な依存は削除しています。
また、 stdenv.lib
は現在利用している22.11ではもう利用できなくなっているようなので、そちらも直接libを参照するように変更しています。
なお元のコードは20.03を利用しているのでそのままで動作します。NixOSのissueを参照すると21.x系ではもうdeperecatedになっているようです。
この nodejs.nix
を先のソースコード提供の例と同じように .config/nix/config.nix
から利用してパッケージを定義します。
|
|
あとは、上記の利用例と同じです。 shell.nix
から nodejs-10_x-binary
で利用できます。
direnvとfishの設定
ここまでで必要な実行環境はできていると思いますが、利用しているエディタやシェルとの統合に関する設定も必要です。
実は、 nix-shell
はbashしか利用できません。
私はインタラクティブシェルとしては普段fishを利用しているので、できればfishを使いたいところです。
先に紹介した公式WikiのDevelopment environment with nix-shellでも触れられていますが、これを回避するためにdirenvを利用する方法があります。 またdirenvを利用することで、EmacsからLSPの利用を行うこともできるようになります。
direnvの設定とシェル
まずはdirenvのinstallを以下公式のガイドの通りに実施します。fish向けのhookの設定まで行います。
続いてfish向けのhookの設定を行います。
これは上記のWikiの通り、direnv公式のNix integrationの設定なのですが、以下の設定を記述した .envrc
を用意します。
|
|
あとは $direnv allow .
とすることでfish環境にいながら、配置した shell.nix
の内容をそのディレクトリでだけ反映することができます。
以降は cd
でそのディレクトリに入るだけで shell.nix
の環境に切りかわるようになります。もちろん他のディレクトリに出れば元に戻ります。
まとめ
Nixを利用して、JavaScriptプロジェクトの開発環境を管理する方法についてまとめてみました。 特に古いversionを用意するところが少し手間ですが、一度用意してしまえばそれほど面倒ではないかなと思います。
もしNixをチーム全体で共有できれば、全員で同じ環境を再現することも可能である、というメリットもあると思います。 その場合versionの固定なども検討したいところなので、Flakesの調査も進めたいところです。