とあるプロジェクトで lint-staged
を仕掛けて楽しようとしたら思わぬところでハマったので解決策を書いておく。
やったこと
プロジェクトで使ってるlinterとformatterを手動でかけるのがめんどくさいので git commit
時に走るようにセットアップしていた。
使っていたツールは以下。
-
angular-cli@7.3.9
: angular のプロジェクトなのでng lint
をlinterとして使っている。 -
husky@2.7.0
: pre-commit hook を仕掛けるのに使っている。 -
prettier@1.18.2
: formatter -
pretty-quick@1.11.1
:git add
されたファイルにだけ prettier を実行するのに使っている。 -
lint-staged@9.2.3
:git add
されたファイルにだけ linter を実行するのに使っている。 -
ng-lint-staged@0.1.6
:ng lint
をlint-staged
と一緒に使うために必要。
セットアップ
{
// (省略)
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*": [
"pretty-quick --staged",
"ng-lint-staged lint --fix --",
"git add"
]
}
}
ふつうはこれでだいたい動く。
ハマったこと
ある日突然チームメンバーの一人が「コミットしようとしたらよく分からないエラーが出る」とのこと。
よく分からないとはなんぞやと思ってエラーメッセージを見せてもらうと、文字化けしていて本当によく分からない。(最後まで文字化けは解読できなかった。)
その時の状況は下記のような感じ。
- 死んだコマンドは
ng-lint-staged lint --fix --
らしい - そのメンバーが使用していた PC は Windows10 で、ターミナルには PowerShell を使用
- 僕が使用しているのは MacBook Pro で、ターミナルには fish を使用
-
git commit --no-verify
でとりあえずコミットしてもらい、僕のPCで再現させようとするも再現しない。 - 手元に Windows 機もあったのでそっちで試してみると再現。
- コミットしようとしていたファイルは 150ファイル超
実行中のログをよくよく眺めてみると、途中で 「ステージングされているファイルが多すぎて、この環境ではコマンドが意図しない動作をすることがあるよ」 というようなメッセージがあることを発見。
調べてみると、 Windows の cmd.exe ではコマンドとして渡せる文字列は 8192 文字が限界 という記述を発見。
PowerShell でも似たような制限があるのではないかと推測した。
解決策
lint-staged
では lint-staged.config.js
という名前で設定ファイルが書け、こちらだと実行するコマンドを自由に書き換えることができるようです。
package.json
から lint-staged
に関する記述を削除し、 lint-staged.config.js
に移行します。
const path = require("path");
const isTooManyFiles = (filenames) => filenames.length > 20;
const toRelative = (absPaths) => absPaths.map((file) => path.relative(process.cwd(), file));
module.exports = {
"{src,cypress}/**/*.ts": (filenames) => {
const relativePaths = toRelative(filenames);
console.log(`{src,cypress}/**/*.ts: targets are ${relativePaths.length} files.`);
const lintCmd = isTooManyFiles(relativePaths) ? "ng lint --fix" : `ng lint --fix --files "${relativePaths.join(",")}"`;
const gitCmd = isTooManyFiles(relativePaths) ? "git add src/ cypress/" : `git add ${relativePaths.join(" ")}`;
return ["pretty-quick --staged", lintCmd, gitCmd];
},
"package.json": (filenames) => ["fixpack", `git add ${toRelative(filenames).join(" ")}`],
};
すごく見づらいスクリプトですが、やっていることはざっくり以下。
- ステージングされているファイルが20以上あったら、個別にlint/formatするのを諦めて全体をlint/formatする。
- ついでに
ng lint --files
オプションに合うように引数リストをパースする。これによってng-lint-staged
を使う必要がなくなる。 - ついでのついでに
package.json
に変更があったらfixpack
して整形しておく。
まとめ
コミットはこまめにしようね…。
(squash したらこうなることもあるのかな?)