package.jsonとpackage-lock.jsonの運用方法について

package.jsonとpackage-lock.jsonの運用方法について

はじめに

パッケージ管理ツールnpmでのpackage.jsonとpackage-lock.jsonの違いや運用方法など、曖昧な点が多かったので調べてまとめてみました。基本的な内容かと思いますが、 参考になれば幸いです。

なぜpackage-lock.jsonが必要なのか?

npmのv5以降に導入されたpackage-lock.jsonですが、npm installを実行した際にインストールされるパッケージのバージョンをpackage-lock.jsonに記載されたバージョンで固定することができ、全ての開発者で同じパッケージの環境を再現することができます。一方、package.jsonだけしかない場合では、npm installしたときに、それぞれの開発者で別バージョンのパッケージをインストールしてしまう可能性があります。

Git管理下では結局どのように運用すればいいの

package-lock.jsonもpackage.jsonと同様にコミットしてGit管理に含める必要があります。

package.jsonだけではなく、package-lock.jsonもGit管理に含めることによって先述したようにインストールされるパッケージのバージョンを固定することができ、それぞれの開発者の環境でpackage-lock.jsonに指定されたバージョンの環境をnpm installによって再現することができるからです。

なので、ファイルが巨大になってしまうnode_moduleディレクトリのみgitignoreしてgit管理から外しておいて、package.jsonとpackage-lock.jsonは両方ともコミットするようにしましょう。

package-lock.jsonがある場合とない場合の違い

実際にpackage-lock.jsonがある場合にパッケージのバージョンが固定されているかどうかpackage-lock.jsonがある場合とない場合の違いを確認してみました。(npmのバージョンはv6.10.1)

こちらのサンプルでインストールするパッケージはlodashを使用しました。仮にlodashのv4.8.0が最新版だった時点でnpm install lodashを実行してパッケージをインストールしたとすると、以下のようなpackage.jsonになるかと思います。(npm install lodash@4.8.0と同じ結果になります)

"dependencies": {
    "lodash": "^4.8.0"
}

ここでlodashのバージョンは「^4.8.0」となっていますが、これは4.8.0以上の4.x.xのバージョンだったらなんでもOK、という意味です。npm lsを実行すると、以下のようにv4.8.0がインストールされたことがわかります。

lodash@4.8.0

package-lock.jsonなしでnpm installした場合

では、このようにv4.8.0が最新版だった時点でインストールされて更新されたpackage.jsonのみがコミットされていたブランチをチェックアウトし、package-lock.jsonがない状態で本日(2019年8月8日)にnpm installを実行すると、以下のようにpakcage-lock.jsonが生成されます。npm lsを実行すると、 現時点での最新版v4.17.15が実際にインストールされていることがわかります。

"dependencies": {
    "lodash": {
        "version": "4.17.15",
        "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
        ...
    }
}
lodash@4.17.15

つまり、package-lock.jsonなしではpackage.jsonが全く同一でも、npm installを実行したタイミングによっては実際にインストールされるパッケージのバージョンが異なるということになります。

これはpackage.jsonでのlodashのバージョンの指定が”^4.8.0″(4.8.0以上の4.x.xのバージョンだったらなんでもOK)となっているために、現時点での最新版であるv4.17.15がダウンロードされたためです。

https://semver.npmjs.com/

こちらのサービスは^などを使ってpackage.jsonでパッケージのバージョンを指定した場合の、該当するバージョンをわかりやすく示してくれます。下の画像の緑色のバージョンが^4.8.0と指定したときの該当のバージョンで、これの一番最新のv4.17.15がインストールされたということがわかります。

上のケースでもしpackage-lock.jsonがコミットされていた場合

それでは、npm install lodashを実行してv 4.8.0のパッケージをインストールした時点で、package-lock.jsonが生成され、次のようなpackage-lock.jsonがコミットされてい場合はどうでしょうか。(実際には実は上のような例でもともとv 4.8.0をインストールしたのが2016年頃としているので、pakcage-lock.json自体がその時点では存在しないのですが、説明のための仮の話としてご容赦ください・・!このサンプルではnpm install lodash@4.8.0のようにして2016年あたりの最新版のv4.8.0を指定してインストールしています。)

"dependencies": {
    "lodash": {
        "version": "4.8.0",
        "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.8.0.tgz",
        ...
    }
}

このようにpackage-lock.jsonがある状態でnpm installを実行した場合は、package-lock.jsonに指定されているバージョン(ここではv4.8.0)がインストールされ、package-lock.jsonに差分は発生しません。 npm lsを実行しても、以下のようにv4.8.0がインストールされていることがわかります。

lodash@4.8.0

4.8.0よりも新しいバージョンが存在し(記事執筆の時点ではv4.17.15)、かつpackage.jsonに記載されている条件を満たしているのですが、package-lock.jsonに明記されているバージョンであるv4.8.0が優先されています。

よって、package-lock.jsonがコミットされていれば、全ての開発者でpackage-lock.jsonに明記された同じバージョンのパッケージのインストールが実現できることになります。

npm ciとnpm installの違いとは?

package-lock.jsonについて調べると、stackoverflowなども含めてもnpm installではなく、npmのv5.7.1から利用可能になったコマンド、npm ciを使うように勧めている記事もあります。

https://docs.npmjs.com/cli/ci.html

npm ciとはドキュメントによると継続的インテグレーション(CI)環境などでnpm installの代わりに使用が推奨されているコマンドです。こちらもnpm installと同様にpackage-lock.jsonをもとにパッケージのインストールを行うのですが、npm installとは違い以下のような特徴があるようです。

  • node_moduleフォルダは実行時に一旦削除されてからインストールが行われる
  • package.jsonとpackage-lock.jsonのバージョンに違いがある場合はエラーを返し、いずれのファイルの上書きも行わない

継続的インテグレーション(CI)環境以外の開発の場面で、例えばリポジトリからブランチをプルした後に開発者の各環境でパッケージのインストールを実行するのに例えば以下の記事などでもnpm installの代わりにnpm ciが推奨されているようです。

https://medium.com/better-programming/npm-ci-vs-npm-install-which-should-you-use-in-your-node-js-projects-51e07cb71e26

実際は通常の開発環境下ではnpm installで問題なさそう

しかし、実際には各開発者の環境で同一のバージョンのパッケージを揃えるという目的では通常通りnpm installを使うことでpackage-lock.jsonがあれば問題なくバージョンを揃えることができます。なので通常のローカルの開発環境下などではnpm installで問題なさそうです。

では、なぜnpm installは時に信頼されず、npm ciが推奨されていることがあるのでしょうか。それはnpmにpackage-lock.jsonが導入されたバージョン(v5.0.0)の後のいくつかのバージョンでは、不必要にpackage-lock.jsonがアップデートされてしまったり、逆にpackage.jsonに変更があった場合でもpackage-lock.jsonに変更があった場合でもpackage-lock.jsonが更新されなかったりといろいろとユーザーに混乱が生じていたようです。

そのためにnpm installというのはあまり信用できず、v5.7.1で導入されたnpm ciの使用が推奨されていることもあるようですが、どのバージョンで完全に修正されたのかは調べてもはっきりとしなかったのですが、実際には最近のバージョン(リリースログを見ても分かりづらかったのですが、2017年にはリリースされていた少なくともv5.4.2以降あたり?)のnpmではnpm installでpackage-lock.jsonが不必要に更新されるというような問題はなく、きちんとバージョンの固定をしてくれるようです。

npm installの時発生する問題についての参考リンク
https://stackoverflow.com/questions/45022048/why-does-npm-install-rewrite-package-lock-json
https://npm.community/t/package-lock-json-contains-dynamic-version/6080/4

まとめ

今までなんとなく理解していたpackage-lock.jsonについて調べてみたところ、過去のバージョンでの不具合などいろいろな経緯があったことがわかりました。古いバージョンのnpmではyarnなどに比べてpackage-lock.jsonでのパッケージの固定に関してあまり信用できないという部分があったようです。

しかし、最新版のnpmではこのあたりの問題は解決されて、pakcage-lock.jsonは信頼できるものとなっているようです。また、余談ですがインストールのスピードなども現在のnpmではyarnに比べて遜色ないものとなっているようです。なので、これからは安心してnpmを使っていきましょう!

we are hiring

優秀な技術者と一緒に、好きな場所で働きませんか

株式会社もばらぶでは、優秀で意欲に溢れる方を常に求めています。働く場所は自由、働く時間も柔軟に選択可能です。

現在、以下の職種を募集中です。ご興味のある方は、リンク先をご参照下さい。