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

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

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

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

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

> このQiitaを呼んで得ること

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

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

> このQiitaで解説しないこと

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

です！

## さて時系列を見てみよう！

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

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

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

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

```bash
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
```



```bash
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 package@1.0.0 No description
npm WARN package@1.0.0 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フォルダでいろいろゴリゴリしてみる。

```bash
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` にしましょう！

```bash
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 local@1.0.0 No description
npm WARN local@1.0.0 No repository field.

audited 1 package in 0.411s
found 0 vulnerabilities

yk@yk
```

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

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

```bash
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ファイルを見てみましょう。

```bash
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/mypackage` の `v1.0.0` が使われています。

```bash
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=="
    }
  }
}
```

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

```bash
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からコピーしてきましょう。

```bash
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ファイルを持ってきてインストールするだけで、エラーが起きなくなりました！ :clap::clap::clap::clap::clap: 


## まとめ

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

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

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


## 終わりに


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

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

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


.