とある事情により、複数のプロジェクトでパッケージ管理を yarn から pnpm に移行した。
CI ツールが GitHub Actions だったり、 CircleCI だったりしたので、それぞれどんな感じに書けるのかって話を久々の投稿としてアウトプットしておく。
共通してやること
yarn や npm から pnpm への移行を決めたらまず、 pnpm を取得する必要がある(当たり前
現時点での最新は pnpm v9 なので、以下よりお好きな方法で取得されたし。
私は Homebrew を使用した。
brew install pnpm
各プロジェクトでパッケージ管理をどのようにしているかによるが、ここでは npm や yarn からの移行を想定しているので、各プロジェクトの lock ファイルがある場所で以下 cli コマンドを実行する。
pnpm import
これで、新たに pnpm-lock.yaml を生成してくれるので、手元での移行はこれでおしまいなのでめちゃくちゃ簡単。不要になった旧パッケージ管理ツールの lock ファイルはこの場で削除しておく。
GitHub Actions
CI のレシピも公式ドキュメントがあるので、基本的にはそのままパクれば良い。
yarn
name: CI
on: [push]
env:
CI: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18.x
- run: yarn
- run: yarn build
- name: Upload elements
uses: actions/upload-artifact@v3
with:
name: hoge-dist
path: dist/hoge
pnpm
name: CI
on: [push]
env:
CI: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
package_json_file: package.json
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: pnpm
- run: pnpm install
- run: pnpm build
- name: Upload hoge
uses: actions/upload-artifact@v3
with:
name: hoge-dist
path: dist/hoge
pnpm へ置き換えるついでに、 Node のバージョンも package.json を参照するようにしたりすると便利かも。その辺のオプションは以下を参照
CircleCI
yarn
install_node_dependencies:
steps:
- restore_cache:
name: Restore cache
keys:
- node-{{ arch }}-{{ checksum ".node-version" }}-{{ checksum "yarn.lock" }}
- run:
name: yarn install
command: |
yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
- save_cache:
name: Save npm cache
key: node-{{ arch }}-{{ checksum ".node-version" }}-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
pnpm
install_node_dependencies:
steps:
- restore_cache:
name: Restore pnpm Package Cache
keys:
- node-{{ arch }}-{{ checksum ".node-version" }}-{{ checksum "pnpm-lock.yaml" }}
- run:
name: Install pnpm package manager
command: |
sudo corepack enable
sudo corepack prepare pnpm@latest-9 --activate
pnpm config set store-dir ~/.cache/pnpm
- run:
name: pnpm install
command: |
pnpm install --frozen-lockfile
- save_cache:
name: Save pnpm package cache
key: node-{{ arch }}-{{ checksum ".node-version" }}-{{ checksum "pnpm-lock.yaml" }}
paths:
- ~/.cache/pnpm
記述自体は簡単ですね。こんな感じで、ビルドとかテストしてるとこをよしなに書き換えれば良さそう。
雑記
まあ難しいのは yarn から移行する際、パッケージ管理が雑にされていると、アプリケーションが壊れることもあるので、リグレッションテストなどでの確認はしっかりしましょうという感じですかね。
あ、ここまで書いて触れてなかったですが今回の移行は yarn v1 からの移行です。yarn v1 は全ての依存パッケージがフラットに node_modules フォルダへ配置されます。
project
├── node_modules
│ ├── react
│ ├── react-dom
│ ├── lodash
│ ├── ...
一方で、 pnpm はプロジェクトの node_modules フォルダには依存関係のツリー構造が反映されます。各パッケージの依存関係はそのパッケージ自身の node_modules フォルダに配置されます。
project
├── node_modules
│ ├── .pnpm
│ │ ├── react@17.0.2
│ │ │ └── node_modules
│ │ │ ├── lodash@4.17.21
│ │ └── react-dom@17.0.2
│ │ └── node_modules
│ │ ├── react@17.0.2 (シンボリックリンク)
│ ├── react
│ ├── react-dom
│ ├── ...
.pnpm フォルダ内に依存パッケージが実際にインストールされ、 node_modules 内のパッケージはその .pnpm 内のパッケージを参照するシンボリックリンクとして存在します。
直接インストールされたパッケージのみに対しては、シンボリックリンクが作成されますが、それらの依存関係はそのパッケージ自身の node_modules に格納される為、ツリー構造が反映され、依存関係の隔離が実現されます。
で、これで何が起きるかというと、従来フラットにインストールされていたが、package.json に含めていなかったライブラリをプロダクションコード内でインポートしてたりするとコケます(当たり前
なんでそうなってんねんってことが起こるのも世の常なので、くれぐれもパッケージ管理ツール移行は慎重に。