モバイルアプリエンジニアの山下です。
チームで開発を進める上でちょっとした"決まりごと"が存在すると思います。
例えば、LCLの開発チームには以下の"決まりごと"が存在します。
- コミットメッセージの先頭にはYouTrackのIssue番号を付ける
- RuboCopで設定したコーディングスタイルになるべく従う
- masterへ直接PUSHはしない
これらを全員が心がけることで運用効率や品質を保つようにしていまが、とはいえコミットする度にIssue番号を入力したり、RuboCopのコマンドを打ったりするのは非常に面倒です。
そこで今回は「git hooks」を利用してコミットやプッシュのタイミングで自動的に実行されるようにしました。
git hooksとは
コミットやプッシュなどのgit操作をトリガーにしてスクリプトを実行する機能です。
.git
ディレクトリ内にhooks
という名前のディレクトリで管理されています。
例えば、コミット時にコミットメッセージに対して何かしらの処理を行うということができます。
これを利用することで手動で行っていた些細なことは自動化することができます。
しかし、git hooksは残念なことにgitで管理できません。(※ 別途、生成スクリプトを用意するなどの策はあります)
これではメンバー全員に設定してもらう必要がありますが、現実はそう浸透しません。
それを解決するためにovercommitを導入しました。
overcommitとは
https://github.com/brigade/overcommit
git hooksの管理・拡張をサポートするGemです。
.overcommit.yml
で実行するコマンドやスクリプトを管理します。
既存のgit hooksを使いまわすことも可能です。
また、あらかじめいくつかの設定が用意されています。
導入
Gitの操作と密に関係するため直接インストールします。
$ gem install overcommit
※ 既にgit hooksを利用している場合は、ファイルをバックアップしておきましょう。
overcommitをインストールする際に初期化される可能性があります。
overcommitをインストール
$ overcommit --install
それでは、さっそくコミットしてみましょう。恐らく以下の警告で失敗すると思います。
Runovercommit --signif you trust the hooks in this repository.
overcommitは、fetchしたymlやスクリプトが不正に書き換えられた場合に備えて、これらの差分が存在した際には署名コマンドを打つ必要があります。少し面倒ですが、安全面との引き換えには仕方がないです。
$ overcommit --sign
上記のコマンドを実行後に再度コミットをしてみましょう。
コミットメッセージのチェックに引っかからなければ、無事にコミットできると思います。
設定
前述の通り、デフォルト設定が存在し有効になっています。以下のコマンドで確認できます。
$ overcommit --list
以下のアスタリスク(*)が付いている項目がデフォルトで設定されている項目です。
https://github.com/brigade/overcommit#built-in-hooks
私の環境ではGitのユーザ名の制約を行う「AuthorName」で引っかかってしまい、コミットができませんでした。
ユーザ名は変えたくないので設定を無効化したいと思います。
.overcommit.ymlに以下の設定を追加します。
PreCommit: AuthorName: enabled: false
変更後は設定を反映させます。
$ overcommit --sign
これで無事にコミットできるようになりました。
RuboCopの実行
RuboCopには自動修正機能が存在します。これを実行するにはコマンドを打つ必要がありますが、今回はコミット時に毎回自動で実行するようにします。
実行したいコマンドは以下です。差分だけを対象にしています。
$ rubocop --auto-correct (git diff master --name-only)
overcommitはRuboCopを含む、様々なLintツールに対応しています。(インストールは別途必要です)
今回は対象ファイルの引数が必要なため、以下のようにcommand
の項目を追加します。
PreCommit: RuboCop: enabled: true command: ['rubocop', '-auto-correct', '$(git diff master --name-only)']
変更後は設定を反映させます。
$ overcommit --sign
これでコミットする際に差分に対して自動でRuboCopの自動修正が実行されます。
コミットメッセージにYouTrackのIssue番号を追加
LCLではYouTrackでIssueを管理しており、ブランチ名やコミットメッセージには対応するYouTrack Issue番号を先頭に記述するようにしています。
YouTrackのIssue番号は以下のような構成になっています。
- project-123
- project_sub-123
前述の通り、毎回記述するのは手間がかかるため、ブランチ名を先頭に記述するスクリプトを作成しgit hooksで管理していました。
今回は、その hook を使いまわしたいと思います。
プロジェクトルート直下のbin
ディレクトリでhooks
フォルダを作成し、更にcommit-msg
ファイルを作成します。
これからは独自のhookはhooks
フォルダ内に追加していきます。
独自のhookを実行させるためには、実行権限を付与する必要があります。
$ chmod +x ./bin/hooks/commit-msg
commit-msgに今回の処理内容をを記載します。
#!/usr/bin/env ruby # コミットメッセージを取得 msg_file = ARGV[0] commit_msg = File.read(msg_file, encoding: Encoding::UTF_8) # 最後の`/`以降の文字列を取得 youtrack_issue_number = `git branch | grep "*"`.sub(/^\*\s/, '').match(/([^\/]+)$/)[0].chomp # YouTrack Issue番号を追加 commit_msg = "#{youtrack_issue_number} #{commit_msg}" unless commit_msg.include?(youtrack_issue_number) # コミットメッセージを上書き File.write(msg_file, commit_msg)
そして、overcommit.ymlに設定を追加します。
CommitMsg: CustomScript: enabled: true required_executable: './bin/hooks/commit-msg'
設定を反映させます。 今回はカスタムスクリプトなのでファイルを指定して署名する必要があります。
$ overcommit --sign commit-msg
以上で、コミットするとコミットメッセージの先頭にブランチ名が追加されると思います。
ブランチ名をIssue番号にすれば要件を満たすことができます。
masterへ直接PUSHできないようにする
先ほど作成したhooks
ディレクトリにpre-push
ファイルを追加し、実行権限を付与します。
$ chmod +x ./bin/hooks/pre-push
ここではPUSH先がmasterを向いている場合、エラーメッセージを流すようにします。
#!/bin/sh while read local_ref local_sha1 remote_ref remote_sha1 do if [[ "${remote_ref##refs/heads/}" = "master" ]]; then echo "masterへPUSHしないでください。" exit 1 fi done
overcommit.ymlに設定を追加します。
PrePush: CustomScript: enabled: true required_executable: './bin/hooks/pre-push'
設定を反映させます。
$ overcommit --sign commit-msg
masterへPUSHするとエラーメッセージが表示されれば成功です。
終わりに
Gitの操作は頻繁に行うので、同じことはなるべく自動化すると塵も積もって大きな時間短縮になると思います。 特に、自分だけで留めるのではなくチームを巻き込んで効率化することで効果は更に大きくなります。
今後は、画像圧縮や特定のファイルのコミット制限などの機能も追加していきたいと考えています。
ルーチンがある方は導入してみてはいかがでしょうか。