0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

fnmでNode.jsのバージョン管理を高速化する — 開発現場での導入・チーム運用・CI活用

0
Posted at

Node.jsを使った開発では、プロジェクトごとに異なるNode.jsバージョンを要求されることがあります。

例えば、次のような状況です。

  • 既存システムはNode.js 18
  • 新規プロジェクトはNode.js 22
  • 保守案件では古いNode.jsが必要
  • ローカル環境とCI環境のNode.jsバージョンが一致しない
  • メンバーごとにNode.jsのバージョンが異なり、不具合の再現条件が揃わない

このような問題を解決するために利用するのが、Node.jsのバージョン管理ツールです。

本記事では、Rustで実装された高速なNode.jsバージョン管理ツール fnm(Fast Node Manager) を、実際の開発現場でどのように導入・運用するかを解説します。


fnmとは

fnmは、複数のNode.jsバージョンをインストールし、プロジェクトごとに切り替えるためのツールです。

正式名称は Fast Node Manager です。

主な特徴は次のとおりです。

  • Rust製で起動が高速
  • macOS、Linux、Windowsに対応
  • 単一バイナリで動作
  • .node-version に対応
  • .nvmrc に対応
  • ディレクトリ移動時の自動切り替えに対応
  • Bash、Zsh、Fish、PowerShellなどに対応

公式リポジトリでも、クロスプラットフォーム対応、単一ファイル、起動速度、.node-version.nvmrcへの対応が主要機能として挙げられています。


開発現場でfnmを使うメリット

1. プロジェクトごとにNode.jsを切り替えられる

プロジェクトAではNode.js 20、プロジェクトBではNode.js 22というように、複数バージョンを共存させられます。

projects/
├── legacy-api/       Node.js 18
├── admin-frontend/   Node.js 20
└── new-platform/     Node.js 22

システム全体のNode.jsを入れ替える必要がないため、既存案件への影響を抑えられます。

2. 開発メンバーの環境を統一できる

プロジェクトルートにバージョンファイルを置いてGit管理することで、チーム全員が同じNode.jsバージョンを使用できます。

.node-version
22.14.0

3. ディレクトリ移動だけでバージョンを切り替えられる

--use-on-cdを有効にすると、プロジェクトディレクトリへ移動した際に、.node-versionまたは.nvmrcを検出してNode.jsバージョンを自動的に切り替えられます。

cd ~/projects/legacy-api
node --version
# v18.x.x

cd ~/projects/new-platform
node --version
# v22.x.x

手動で毎回fnm useを実行する必要がなくなります。

4. Windowsを含む複数OSで運用しやすい

fnmはmacOS、Linux、Windowsに対応しています。

フロントエンドチームはmacOS、業務システムチームはWindows、サーバーやCIはLinuxという構成でも、同じ仕組みを採用できます。


fnmのインストール

macOS

Homebrewを利用します。

brew install fnm

公式リポジトリでは、macOSおよびLinux向けのHomebrewインストール方法が案内されています。

インストールを確認します。

fnm --version

Linux

インストールスクリプトを利用します。

curl -fsSL https://fnm.vercel.app/install | bash

インストールスクリプトを利用する場合、curlunzipが必要です。

Ubuntuでは、事前に次のようにインストールできます。

sudo apt update
sudo apt install -y curl unzip

その後、fnmをインストールします。

curl -fsSL https://fnm.vercel.app/install | bash

Windows

wingetを使う場合

winget install Schniz.fnm

Scoopを使う場合

scoop install fnm

Chocolateyを使う場合

choco install fnm

これらのWindows向けインストール方法は、公式リポジトリでも案内されています。

インストール後は、ターミナルを開き直して確認します。

fnm --version

シェルの初期設定

fnmは、fnm envの出力をシェルで評価することで、Node.jsへのパスや環境変数を設定します。公式ドキュメントでは、実行時のシェル推測を避けるため、--shellでシェルを明示する方法が推奨されています。

Zsh

macOSの標準シェルであるZshを利用している場合は、~/.zshrcへ追加します。

eval "$(fnm env --use-on-cd --shell zsh)"

設定を読み込みます。

source ~/.zshrc

Bash

~/.bashrcへ追加します。

eval "$(fnm env --use-on-cd --shell bash)"

設定を読み込みます。

source ~/.bashrc

Fish

~/.config/fish/conf.d/fnm.fishを作成し、次を記述します。

fnm env --use-on-cd --shell fish | source

PowerShell

最初にPowerShellプロファイルを作成します。

if (-not (Test-Path $PROFILE)) {
    New-Item $PROFILE -Force
}

プロファイルを開きます。

Invoke-Item $PROFILE

次の行を追加します。

fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression

PowerShellのプロファイル設定方法と--use-on-cdを含む初期化コマンドは、公式リポジトリに掲載されています。

設定後、PowerShellを再起動します。


Node.jsをインストールする

LTS版をインストールする

fnm install --lts

インストール後、そのバージョンを利用します。

fnm use --lts

バージョンを確認します。

node --version
npm --version

バージョンを指定してインストールする

fnm install 22.14.0

インストールしたバージョンへ切り替えます。

fnm use 22.14.0

確認します。

node --version
v22.14.0

メジャーバージョンだけを指定することもできます。

fnm install 22
fnm use 22

ただし、開発現場では再現性を高めるため、プロジェクトのバージョンファイルにはできるだけパッチバージョンまで記述することをおすすめします。


基本コマンド

インストール済みバージョンを確認する

fnm list

環境によっては、次の短縮形も利用できます。

fnm ls

現在のバージョンを確認する

fnm current

Node.js自身のコマンドでも確認できます。

node --version

バージョンを切り替える

fnm use 20
fnm use 22.14.0

バージョンをアンインストールする

fnm uninstall 18.20.5

削除前に、利用中のプロジェクトがないか確認しておきましょう。


デフォルトバージョンを設定する

プロジェクト外で使用する標準バージョンを設定します。

fnm default 22.14.0

新しいターミナルを起動し、確認します。

node --version

プロジェクトごとにNode.jsを固定する

fnmを開発現場へ導入するうえで、最も重要なのがバージョンファイルの運用です。

.node-versionを利用する

プロジェクトルートで次を実行します。

echo "22.14.0" > .node-version

現在使用しているNode.jsバージョンを書き出す場合は、次の方法もあります。

node --version > .node-version

公式リポジトリでも、node --versionの出力を.node-versionへ保存する例が案内されています。

ファイルの内容は次のようになります。

v22.14.0

vを付けずに記述しても運用できます。

22.14.0

.nvmrcを利用する

既にnvmを利用しているプロジェクトでは、.nvmrcをそのまま利用できます。

echo "22.14.0" > .nvmrc

fnmは.node-version.nvmrcの両方に対応しています。

そのため、nvmからfnmへ移行する場合でも、既存リポジトリの変更を最小限にできます。


バージョンファイルはGit管理する

.node-versionまたは.nvmrcは、原則としてGitへコミットします。

git add .node-version
git commit -m "chore: pin Node.js version"

これにより、リポジトリをクローンしたメンバーが同じNode.jsバージョンを利用できます。

バージョンファイルを個人設定として.gitignoreへ追加してしまうと、チーム内でバージョンを統一できません。


開発現場でおすすめの運用方法

1. パッチバージョンまで固定する

次のようにメジャーバージョンだけを記述すると、インストールするタイミングによって利用されるバージョンが変化する可能性があります。

22

再現性を重視する場合は、パッチバージョンまで固定します。

22.14.0

これにより、次の環境を揃えやすくなります。

  • 開発者のローカル環境
  • Dockerビルド
  • CI/CD
  • ステージング環境
  • 本番環境

2. package.jsonにもNode.js要件を記述する

.node-versionだけでなく、package.jsonenginesにもNode.jsバージョンを記述します。

{
  "name": "example-project",
  "private": true,
  "engines": {
    "node": "22.14.x"
  }
}

役割は少し異なります。

ファイル 主な役割
.node-version fnmが実際に使用するNode.jsバージョン
.nvmrc nvmやfnmなどで使用するNode.jsバージョン
package.jsonengines アプリケーションが対応するNode.jsの条件

複数の仕組みを組み合わせることで、誤ったバージョンの利用を検知しやすくなります。


3. READMEに初期構築手順を書く

新しく参加したメンバー向けに、READMEへ手順を記載します。

## 開発環境

### 必要なツール

- fnm
- Node.js
- pnpm

### セットアップ

```bash
fnm install
fnm use
corepack enable
pnpm install

バージョンファイルが存在するディレクトリでは、引数なしの`fnm install`や`fnm use`によって、対象バージョンを利用できる構成にしておくとスムーズです。

---

## 4. `--use-on-cd`を標準設定にする

チームのセットアップ手順では、次の設定を推奨します。

```bash
eval "$(fnm env --use-on-cd --shell zsh)"

ディレクトリ移動時の自動切り替えを有効にすることで、別プロジェクトのNode.jsバージョンを使ったまま作業するミスを減らせます。


5. package managerも固定する

Node.jsのバージョンだけでなく、npm、pnpm、Yarnなどのバージョン差もビルド結果へ影響することがあります。

package.jsonpackageManagerを利用します。

{
  "packageManager": "pnpm@10.6.5"
}

Corepackを有効化します。

corepack enable

その後、指定されたpackage managerを利用します。

pnpm install

これにより、次の2つを固定できます。

Node.js        .node-version
package manager package.jsonのpackageManager

新規プロジェクトでの導入例

React/Next.jsプロジェクト

mkdir example-frontend
cd example-frontend

echo "22.14.0" > .node-version

fnm install
fnm use

corepack enable

npx create-next-app@latest .

バージョンを確認します。

node --version
npm --version

.node-versionをコミットします。

git add .node-version
git commit -m "chore: add Node.js version"

LaravelとNode.jsを併用するプロジェクト

Laravelプロジェクトでも、ViteやTailwind CSSを利用する場合はNode.jsが必要です。

cd example-laravel-project

echo "22.14.0" > .node-version

fnm install
fnm use

npm ci
npm run build

PHPのバージョン管理とNode.jsのバージョン管理を分離して考えると、環境構築が整理しやすくなります。

PHP      mise / asdf / Docker
Node.js  fnm

既存プロジェクトへ導入する

既存プロジェクトでは、現在正常に動作しているNode.jsバージョンを確認します。

node --version
v20.18.3

そのバージョンを.node-versionへ保存します。

node --version > .node-version

または手動で作成します。

echo "20.18.3" > .node-version

package.jsonにも要件を追加します。

{
  "engines": {
    "node": "20.18.x"
  }
}

その後、CI、Docker、本番環境のNode.jsバージョンも揃えます。


nvmからfnmへ移行する

fnmは.nvmrcを読み込めるため、nvmから比較的移行しやすいツールです。

1. fnmをインストールする

macOSの場合は次のとおりです。

brew install fnm

2. シェル設定を追加する

eval "$(fnm env --use-on-cd --shell zsh)"

3. .nvmrcのバージョンをインストールする

プロジェクトルートで実行します。

fnm install
fnm use

4. 動作を確認する

node --version
npm --version

5. nvmの設定を削除する

動作確認後、.zshrc.bashrcに残っているnvm関連の設定を削除します。

例:

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

nvmとfnmを同時に初期化すると、PATHの順序によって意図しないNode.jsが利用される可能性があります。

移行期間を除き、バージョン管理ツールは1つに統一する方が安全です。


Dockerとfnmの役割分担

Dockerを使っている場合、「fnmは不要ではないか」と考えることがあります。

実際には、fnmとDockerは役割が異なります。

ツール 主な役割
fnm ローカル開発環境のNode.js切り替え
Docker コンテナ内の実行環境を固定
.node-version ローカルで利用するバージョンを宣言
Dockerfile コンテナで利用するバージョンを宣言

例えば、.node-versionが次の内容だとします。

22.14.0

Dockerfileも同じバージョンへ揃えます。

FROM node:22.14.0-bookworm-slim

WORKDIR /app

COPY package.json package-lock.json ./

RUN npm ci

COPY . .

RUN npm run build

CMD ["npm", "start"]

Node.jsバージョンを更新する場合は、次の両方を変更します。

.node-version
Dockerfile

RenovateやDependabotなどでDockerイメージを更新している場合は、バージョンファイルとの不一致をレビュー時に確認します。


CI/CDでの利用方針

CI/CD環境では、必ずしもfnm自体を利用する必要はありません。

GitHub Actionsでは、通常はactions/setup-nodeを利用します。

重要なのは、ローカル環境とCI環境で同じNode.jsバージョンを参照することです。

GitHub Actionsの例

.node-versionを読み込んでsetup-nodeへ渡します。

name: CI

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Read Node.js version
        id: node-version
        shell: bash
        run: |
          echo "version=$(tr -d 'v[:space:]' < .node-version)" >> "$GITHUB_OUTPUT"

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ steps.node-version.outputs.version }}
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Test
        run: npm test

      - name: Build
        run: npm run build

この構成では、Node.jsのバージョンを.node-versionへ集約できます。


CI上でfnmを直接利用する例

開発環境と同じ挙動を検証したい場合は、CIへfnmをインストールする方法もあります。

name: CI with fnm

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install fnm
        shell: bash
        run: |
          curl -fsSL https://fnm.vercel.app/install | bash -s -- --skip-shell
          echo "$HOME/.local/share/fnm" >> "$GITHUB_PATH"

      - name: Install Node.js
        shell: bash
        run: |
          eval "$(fnm env --shell bash)"
          fnm install
          fnm use
          node --version
          npm --version

      - name: Install dependencies
        shell: bash
        run: |
          eval "$(fnm env --shell bash)"
          fnm use
          npm ci

      - name: Test
        shell: bash
        run: |
          eval "$(fnm env --shell bash)"
          fnm use
          npm test

ただし、GitHub Actionsではactions/setup-nodeの方がキャッシュ設定などを簡潔に記述できます。

そのため、多くのプロジェクトでは次の分担が現実的です。

ローカル環境:fnm
GitHub Actions:actions/setup-node
バージョン定義:.node-version

Makefileやスクリプトへ組み込む

初期構築手順を自動化する場合、Makefileへ組み込めます。

.PHONY: setup install test build check-node

setup:
	fnm install
	fnm use
	corepack enable
	npm ci

install:
	fnm use
	npm ci

test:
	fnm use
	npm test

build:
	fnm use
	npm run build

check-node:
	@echo "Expected: $$(cat .node-version)"
	@echo "Actual:   $$(node --version)"

ただし、Makefileは各行が別のシェルで実行される場合があります。

fnmの初期化が必要な環境では、次のように1つのシェル内で実行します。

SHELL := /bin/bash

.PHONY: setup

setup:
	eval "$$(fnm env --shell bash)" && \
	fnm install && \
	fnm use && \
	corepack enable && \
	npm ci

開発コンテナやリモート環境で使う

WSL2

WSL2では、Windows側ではなくWSL側へfnmをインストールします。

curl -fsSL https://fnm.vercel.app/install | bash

~/.bashrcまたは~/.zshrcへ追加します。

eval "$(fnm env --use-on-cd --shell bash)"

Windows側のNode.jsとWSL側のNode.jsは別環境として管理します。

確認には次のコマンドを利用します。

which node
node --version
fnm current

VS Code Dev Containers

Dev ContainerではDockerfileのNode.jsバージョンを固定するため、コンテナ内でfnmを使わない構成も一般的です。

FROM node:22.14.0-bookworm

一方、1つの開発コンテナ内で複数Node.jsバージョンを検証したい場合は、fnmを追加できます。

RUN apt-get update \
    && apt-get install -y curl unzip \
    && curl -fsSL https://fnm.vercel.app/install | bash

ただし、通常のアプリケーション開発では、コンテナ自体のNode.jsバージョンを固定する方が構成は単純です。


よくあるトラブルと対処法

fnm: command not found

原因

  • fnmのインストール先がPATHに入っていない
  • シェル設定を読み込んでいない
  • インストール後にターミナルを再起動していない

対処

source ~/.zshrc

または次を確認します。

command -v fnm
echo "$PATH"

node: command not found

fnmはインストールされていても、Node.jsが未インストール、またはfnm envが評価されていない可能性があります。

fnm install --lts
fnm use --lts

現在のシェルだけで初期化する場合は、次を実行します。

eval "$(fnm env --shell zsh)"

Bashの場合は次のとおりです。

eval "$(fnm env --shell bash)"

ディレクトリを移動してもバージョンが切り替わらない

--use-on-cdが設定されているか確認します。

Zshの場合:

eval "$(fnm env --use-on-cd --shell zsh)"

Bashの場合:

eval "$(fnm env --use-on-cd --shell bash)"

また、プロジェクトルートにバージョンファイルがあるか確認します。

ls -la .node-version .nvmrc

fnm useでバージョンが見つからない

対象バージョンが未インストールです。

fnm install
fnm use

または明示的に指定します。

fnm install 22.14.0
fnm use 22.14.0

which nodeがfnm配下を指していない

macOS/Linuxでは次を確認します。

which node

fnm管理下のNode.jsではなく、Homebrew、apt、nvm、asdfなどのNode.jsが先に見つかっている可能性があります。

シェル設定を確認します。

grep -n "fnm\|nvm\|asdf\|node" ~/.zshrc

Node.jsの管理ツールが複数初期化されている場合は、利用するツールを整理します。


VS Codeのターミナルだけバージョンが異なる

VS Codeをシェル設定変更前から起動している場合、古い環境変数を保持している可能性があります。

次を試します。

  1. VS Codeのターミナルを閉じる
  2. VS Codeを終了する
  3. VS Codeを再起動する
  4. 新しいターミナルで確認する
node --version
fnm current
which node

npmのグローバルパッケージが消えた

Node.jsの各バージョンは、基本的に別のグローバルパッケージ領域を持ちます。

例えば、Node.js 20でインストールしたCLIが、Node.js 22では見つからない場合があります。

npm install -g typescript

開発現場では、CLIをグローバルインストールするよりも、プロジェクトのdevDependenciesへ追加する方が再現性を高められます。

npm install --save-dev typescript

実行時はnpxやpackage scriptsを利用します。

npx tsc --version
{
  "scripts": {
    "typecheck": "tsc --noEmit"
  }
}

セキュリティ面での注意

インストールスクリプトをそのままパイプしない選択肢

次のインストール方法は簡単ですが、取得したスクリプトを直接実行します。

curl -fsSL https://fnm.vercel.app/install | bash

セキュリティポリシーが厳しい組織では、いったんファイルへ保存して内容を確認します。

curl -fsSL https://fnm.vercel.app/install -o install-fnm.sh
less install-fnm.sh
bash install-fnm.sh

または、Homebrew、winget、Scoopなど、組織で許可されたパッケージマネージャーを利用します。


CIではバージョンやハッシュを固定する

CI上で常に最新のインストールスクリプトを取得すると、将来的な変更の影響を受ける可能性があります。

厳密な環境では、次の方法を検討します。

  • リリースバージョンを固定する
  • バイナリのチェックサムを検証する
  • 社内アーティファクトリポジトリへ配置する
  • Dockerイメージへあらかじめ組み込む
  • CIでは公式のNode.jsセットアップActionを利用する

チーム向けの推奨構成

筆者が開発現場で採用する場合は、次の構成にします。

.node-version

22.14.0

package.json

{
  "private": true,
  "engines": {
    "node": "22.14.x"
  },
  "packageManager": "pnpm@10.6.5",
  "scripts": {
    "dev": "vite",
    "lint": "eslint .",
    "typecheck": "tsc --noEmit",
    "test": "vitest run",
    "build": "vite build"
  }
}

.zshrc

eval "$(fnm env --use-on-cd --shell zsh)"

セットアップコマンド

fnm install
fnm use
corepack enable
pnpm install

GitHub Actions

- name: Read Node.js version
  id: node-version
  shell: bash
  run: |
    echo "version=$(tr -d 'v[:space:]' < .node-version)" >> "$GITHUB_OUTPUT"

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: ${{ steps.node-version.outputs.version }}
    cache: pnpm

この構成では、Node.jsバージョンの基準を.node-versionへ集約できます。


バージョンアップの進め方

Node.jsのバージョンアップは、単に.node-versionを書き換えるだけでなく、次の順序で進めます。

1. 新しいバージョンをインストールする

fnm install 22.15.0
fnm use 22.15.0

2. バージョンファイルを更新する

echo "22.15.0" > .node-version

3. 依存関係を再インストールする

npmの場合:

rm -rf node_modules
npm ci

pnpmの場合:

rm -rf node_modules
pnpm install --frozen-lockfile

4. 静的解析とテストを実行する

npm run lint
npm run typecheck
npm test
npm run build

5. Dockerfileを更新する

FROM node:22.15.0-bookworm-slim

6. CIで検証する

プルリクエスト上で、次を確認します。

  • lint
  • typecheck
  • unit test
  • integration test
  • build
  • Docker build
  • E2E test

Node.js更新だけのプルリクエストとして分離すると、不具合発生時に原因を追跡しやすくなります。


fnmを採用するときの判断基準

fnmは次のようなチームに向いています。

  • Node.jsプロジェクトを複数扱う
  • nvmのシェル起動速度が気になる
  • Windows、macOS、Linuxが混在している
  • .nvmrcを維持したまま移行したい
  • ディレクトリ移動時に自動切り替えしたい
  • Node.js専用のシンプルな管理ツールを使いたい

一方で、Node.js以外にもPython、Ruby、Go、Java、Terraformなどをまとめて管理したい場合は、miseやasdfなどのマルチランタイム管理ツールも候補になります。

ツールの優劣だけでなく、チームが管理したい対象や、既存環境との整合性を基準に選ぶことが重要です。


まとめ

fnmを利用すると、Node.jsのバージョン切り替えを高速かつシンプルに管理できます。

特に開発現場では、fnmをインストールするだけでなく、次の運用までセットで導入することが重要です。

  • .node-versionまたは.nvmrcをGit管理する
  • パッチバージョンまで固定する
  • --use-on-cdで自動切り替えを有効にする
  • package.jsonenginesも設定する
  • package managerのバージョンも固定する
  • DockerfileとCIのNode.jsバージョンを揃える
  • グローバルCLIへの依存を減らす
  • バージョン更新を独立したプルリクエストにする

fnmの導入目的は、単にNode.jsを切り替えることではありません。

ローカル、CI、Docker、本番環境でNode.jsバージョンを揃え、開発環境の再現性を高めることが、本質的な目的です。

Node.jsのバージョン差によるトラブルが多いチームでは、fnmとバージョンファイルの運用をセットで導入してみてください。


参考資料

  • Schniz/fnm GitHubリポジトリ
  • fnm公式インストールガイド
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?