そろそろlockファイルを理解するための最初のページ【composer.lock/package-lock.json】


.lockファイルって邪魔だよね。って思っている人いたら校舎裏来てください。優しく解説します。

こんにちは! @ykhirao です。最近ジョインさせていただいた案件で composer.lock のバージョンが本番が一番新しくて、ローカルが一番古い素敵な状況を発見しました

本番環境でSSHして composer update かけたんだなあーー。そっかーーー、バグ起きたらどないすんねん、。。。。。。

というのが私の感想で、実際にnpmパッケージを使ってバグを再現しながら
ゆっくり解説していこうと思います。どうぞ最後までよろしくお願いします。!!!

このQiitaを呼んで得ること

  • あーー、ほんとだ。バグが入る可能性があるんだ!
  • lockファイルはちゃんとコミットするね!

というお気持ちになれると思います。

このQiitaで解説しないこと

  • 詳しい依存関係の解消法
  • packageの公開の仕方
  • むずかしいはなし

です!

さて時系列を見てみよう!

こんな事故が起こる可能性がありますよって話をします。

時系列 OSSのmypackage.js ローカル 本番サーバー
1日目 v1.0.0公開 動いている 動いている
2日目 ローカルでmypackage.jsをinstall
動作確認OK、明後日本番に適応させる
3日目 v1.0.1公開 バグあり
4日目 本番サーバーで新しいコードを反映、install。本番が動かなくなってしまった。ぴえん
もちろんソースコードはローカルと同じだし、ローカルで動いていたのになんで…?
5日目 v1.0.2公開 バグ修正された
6日目 v1.0.2を適応ローカル確認OK
今度はlockファイルを本番にアップロードする予定!
7日目 package-lock.jsonを使ってinstallする!今度は動いた!

npmでパッケージを作りながら説明する

OSSっぽい動作をするpackageフォルダと、ローカル環境と本番環境っぽいフォルダを作る。

yk@yk ~ % cd qiita 
yk@yk qiita % mkdir package
yk@yk qiita % mkdir local
yk@yk qiita % mkdir production
yk@yk qiita % ll
total 0
drwxr-xr-x   5 yk  staff   160  7 11 00:34 .
drwxr-xr-x+ 83 yk  staff  2656  7 11 00:33 ..
drwxr-xr-x   2 yk  staff    64  7 11 00:34 local
drwxr-xr-x   2 yk  staff    64  7 11 00:34 package
drwxr-xr-x   2 yk  staff    64  7 11 00:34 production
yk@yk qiita % cd package 
yk@yk package % npm init -y
Wrote to /Users/yk/qiita/package/package.json:

{
  "name": "package",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}


yk@yk package % vim package.json
yk@yk package % vim index.js
yk@yk package % npm publish --access=public
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No description
npm WARN [email protected] No repository field.

up to date in 0.429s
found 0 vulnerabilities

/Users/yk/.nodebrew/node/v13.8.0/lib/node_modules/package -> /Users/yk/qiita/package

yk@yk package % cat index.js 
const text = "sample"

module.exports = text;

index.jsにただただ "sample" という文字列を返す動作を記述して、パッケージとして認識させる。
@y_hirao/mypackage というパッケージをnpmに公開した。

localフォルダでいろいろゴリゴリしてみる。

yk@yk local % npm init -y
Wrote to /Users/yk/qiita/local/package.json:

{
  "name": "local",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@y_hirao/mypackage": "^1.0.0"
  },
  "devDependencies": {},
  "description": ""
}


yk@yk local % cat index.js 
const text = require("@y_hirao/mypackage");
console.log(text)

yk@yk local % node index.js 
sample

sampleという文字列が返却されました。!!!!!!!!!

このときpackage.jsonを見ると ^ キャレット指定なのでバージョンアップは 1.系 は守られるけど残りはすべてアップデートされます。

  "dependencies": {
    "@y_hirao/mypackage": "^1.0.0"
  },

さてパッケージの方を更新します。 v1.0.2 にしましょう!

yk@yk package % cat package.json 
{
  "name": "@y_hirao/mypackage",
  "version": "1.0.2",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

yk@yk package % cat index.js    
const text = "sample2"
throw 'エラーを出したい気分';

module.exports = text;
yk@yk package % npm publish   

このパッケージをそのまま使うとthrowされます。

さてローカルでinstallしてみましょう!

yk@yk local % npm install
npm WARN [email protected] No description
npm WARN [email protected] No repository field.

audited 1 package in 0.411s
found 0 vulnerabilities

yk@yk

あれ、エラーが起きませんね。
これは 一度ダウンロードしたパッケージはlockファイルというものでバージョン指定されているので、新しく勝手にバージョンがあがったりしないのです。

さてローカルから本番環境に package.json と index.js をコピーしてnpm installして動かしてみましょう!

yk@yk production % cp ../local/package.json package.json
yk@yk production % cp ../local/index.js index.js        
yk@yk production % npm i
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN local@1.0.0 No description
npm WARN local@1.0.0 No repository field.

added 1 package and audited 1 package in 2.811s
found 0 vulnerabilities

yk@yk production % node index.js 

/Users/yk/qiita/production/node_modules/@y_hirao/mypackage/index.js:2
throw 'エラーを出したい気分';
^
エラーを出したい気分
(Use `node --trace-uncaught ...` to show where the exception was thrown)

さて、、、本番環境でinstallを走らせるとエラーがおきました。。。。

npm install したときに package-lock.json がないと package.json を使って ^1.0.0 が指定されているので 1.系で一番新しいもの つまり今回だとバグが混入している v1.0.2 がインストールされたことになります。

lockファイルを見てみましょう。

yk@yk production % cat package-lock.json 
{
  "name": "local",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "@y_hirao/mypackage": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/@y_hirao/mypackage/-/mypackage-1.0.2.tgz",
      "integrity": "sha512-W2RoQsC1FVnHxximVJMovEvfl/3WNI95EjGxPbMlmyuDZ+0SImS76dOII/0AiP4cVVjXZUbFzaLs9h6l8TrrFQ=="
    }
  }
}

まさしく v1.0.2 が指定されていますね!localだともちろん最初に npm install したときに最新だった @y_hirao/mypackagev1.0.0 が使われています。

yk@yk local % cat package-lock.json 
{
  "name": "local",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "@y_hirao/mypackage": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/@y_hirao/mypackage/-/mypackage-1.0.0.tgz",
      "integrity": "sha512-auQ5grIpWd0IRkMR3rn6xRjIhGZqBu/Q/g1lDkS5AWakAysrZ3iQAIRngbgBGBAFlPZxQ/LCVYdAW+3VLxeSEw=="
    }
  }
}

更にインストールされているフォルダを確認しても

yk@yk production % cat node_modules/@y_hirao/mypackage/index.js
const text = "sample2"
throw 'エラーを出したい気分';

module.exports = text;
yk@yk production % 

どう見てもエラーが起きそうです。

package-lock.jsonをlocalからコピーしてきましょう。

yk@yk production % cp ../local/package-lock.json package-lock.json
yk@yk production % npm i
npm WARN local@1.0.0 No description
npm WARN local@1.0.0 No repository field.

updated 1 package and audited 1 package in 0.45s
found 0 vulnerabilities

yk@yk production % cat node_modules/@y_hirao/mypackage/index.js
const text = "sample"

module.exports = text;
yk@yk production % node index.js 
sample
yk@yk production % 

lockファイルを持ってきてインストールするだけで、エラーが起きなくなりました!

まとめ

本番環境で lockファイルごと更新する composer update とかlockファイルの存在しない状態での npm install とか、そんなことはやめましょう。ってことを主張したいです!!!!!

ローカルでしっかり確認してlockファイルを作り、それを本番にアップロードして、lockファイルの状態で依存パッケージをインストールしちゃってください。。

インストールする時期によって最新のバージョンが使われるので、もしそのバージョンにバグがあるのなら本番死んじゃいますよ。。

終わりに

新しい会社に入って色々根本的に変えたいところがあるので、最近はdevopsの領域をごりごり触らせていただいてます。環境構築、開発フローの策定とかもろもろやらせてもらっているのでかなり楽しいです

読んでいただきありがとうございました。lockファイル入門したい方、事故りそうな不安があるかたは同僚と一緒に「弊社のパッケージ依存関係の運用どうする…?」と一度話し合ってみてください。

ありがとうございました。

.