swiftlintを差分のあるファイルにだけpre-commitでかける
背景
最近ジョインしたプロジェクトでは、数年前にswiftlintをプロジェクトに追加したはいいのですがCIなどでチェックしていない、lintツールの運用ルールを明確にしていない、開発者によってはswiftlintを気にしてない、などの理由でlintのwarningが大量に溜まりlintツールがそもそも機能しなくなっていました。
技術的な負債を解消するために既存のコードをメンテする工数が取れないけど、lintくらいは運用してコードの品質を担保したい。
折衷案として、私のチームでは「新規に自分で作ったファイル」「自分が変更したファイル」に対してのみlintのwarningを解消する責任を負うというルールにしました。
ということで導入した差分のあるファイルにのみswiftlintをかけるpre-commitのスクリプトを紹介します。
方針
git commit
した際に拡張子が.swift
で差分のあるファイルのみをincludeするswiftlintの設定ファイルを生成してlint→warningが出たらcommitができないようにします。
前準備
commit時に生成されるswiftlintの設定ファイルはgitで管理する必要がないのでgitignoreします。
.swiftlint.precommit.yml
また、もとになる.swftlit.yml
も当然ながら用意しておきましょう。
pre-commitのスクリプト
本編です。これだけです。忙しい人はプロジェクト内の.githooks/pre-commitとかにこれコピペして使ってください。
git_diff=$(git diff --cached --name-only --diff-filter=AM -- '*.swift')
if [ -n "$git_diff" ]; then
cp .swiftlint.yml .swiftlint.precommit.yml
echo 'included:' >> .swiftlint.precommit.yml
echo "$git_diff" | sed -e "s/^/ - /" >> .swiftlint.precommit.yml
echo 'lint the following files'
echo "$git_diff" | sed -e "s/^/ - /"
echo ""
else
echo 'No changed .swift file'
exit 0;
fi
lint_result=$(swiftlint --reporter emoji --quiet --config .swiftlint.precommit.yml)
if [ -n "$lint_result" ]; then
echo 'Some issues found.'
echo '====================================='
echo "$lint_result" | sed -e "s/^/>> /"
echo '====================================='
echo ''
exit 1;
else
echo 'No issues found!'
fi
exit 0
解説
git_diff=$(git diff --cached --name-only --diff-filter=AM -- '*.swift')
git_diffという変数に差分がある拡張子が.swift
のファイル名一覧を入れておきます。
if [ -n "$git_diff" ]; then
~~~~
else
echo 'No changed .swift file'
exit 0;
fi
git_diffが空=.swiftの差分がないならpre-commitを正常に終了してそのままcommitさせます。
cp .swiftlint.yml .swiftlint.precommit.yml
echo 'included:' >> .swiftlint.precommit.yml
echo "$git_diff" | sed -e "s/^/ - /" >> .swiftlint.precommit.yml
もとになる.swiftlint.yml
を.swiftlint.precommit.yml
にコピーして、末尾に
included:
- HasDiff.swift
- HasDiffViewController.swift
のようにgit_diffの中身のファイルをincludeする設定を書き込みます。
lint_result=$(swiftlint --reporter emoji --quiet --config .swiftlint.precommit.yml)
lint_resultにswiftlintの結果を入れます。
if [ -n "$lint_result" ]; then
~~~~~~~
exit 1;
else
echo 'No issues found!'
fi
lint_resultがあれば(=なにかしらのwarningがある)exit 1で異常系終了、なければ特に何もせず正常に終了します。
pre-commitがエラーで終了するとcommitができないので、これを使うことでlintが通っていないコードがcommitされることはありません。
導入
各自のローカルでpre-commitの設定を行ってください。
$ chmod +x .githooks/pre-commit
$ git config --local core.hooksPath .githooks/
実際に運用してみてのうまみ
まとめ的な感じで1カ月くらい運用した感想と効果をまとめます。
- そもそもcommitできなくすることで"apply lint"とか"lint fix"みたいなcommitを後追いですることがなくなった
- lintルールができたことでレビューもしやすくなった
- ただローカルでpre-commitの設定をする必要があるので、そこの情報共有が漏れるとかなり脆弱なlint運用ではある
まぁ結構プロジェクトにはメリットが出たかなと思います。
Discussion