.rubocop_todo.yml の反映漏れを CI で防止する
.rubocop_todo.yml
の運用を少し楽にするテクニックの紹介です。
.rubocop_todo.yml とは
既存のプロジェクトに RuboCop を導入すると、違反が大量出てしまい、解消に苦慮するという問題があります。その問題を解決する方法の1つが .rubocop_todo.yml
という設定ファイルです。
.rubocop_todo.yml
は、Ruby の静的コード解析ツールである RuboCop によって自動生成されます。このファイルの設定を読み込むことで、プロジェクト内の現在のコードベースに存在する違反を一時的に無視することができます。
この設定を駆使することで、段階的に RuboCop の違反を解消することができるようになっています。
.rubocop_todo.yml とプロダクトコードの乖離
便利な .rubocop_todo.yml
ですが、運用するにあたって不便な点があります。それは、プロダクトコードと .rubocop_todo.yml
を追従していく必要があることです。
理想的には、プロダクトコードの変更に合わせて .rubocop_todo.yml
も適切に更新するのが望ましいですが、しばしば更新が忘れられることも多いです。その結果、後から気づいた人が更新するという運用になりがちです。
生成した .rubocop_todo.yml の差分をCIで検出する
.rubocop_todo.yml
の反映漏れは、CIを使うことで割と簡単に検知できます。
具体的には、以下のような step を組み込みます。
-
rubocop --regenerate-todo
を実行する - その後のワークツリーで
git diff --exit-code
を実行する
rubocop --regenerate-todo
は前回.rubocop_todo.yml
のTODOリストを生成したオプションで再生成するものです。同じオプションで実行されることが担保できるため、今回の要件に合います。
Regenerate the TODO list using the same options as the last time it was generated with --auto-gen-config (generation options can be overridden).
--regrenerate-todo - Basic Usage :: RuboCop Docs
git diff --exit-code
は差分があれば場合に終了コード1、無ければ終了コード0を返します。
Make the program exit with codes similar to diff(1). That is, it exits with 1 if there were differences and 0 means no differences.
Git - git-diff Documentation
これにより rubocop --regenerate-todo
で差分が生じることで、.rubocop_todo.yml
とプロダクトコードが乖離していることを検知します。
上記の step で差分を検知した後は、その差分を自動でコミットしたりする仕組みと紐づけることで、人が管理せずに .rubocop_todo.yml
の反映できます。
.rubocop_todo.yml の差分から Pull Request を作成する GitHub Actions の設定例
最後に .rubocop_todo.yml
を自動でプロダクトコードに追従する GitHub Actions のワークフローの例を紹介します。
作成しているPRに対して、.rubocop_todo.yml
に差分があれば、その差分を解消するPRを自動で作成するものです。
on: pull_request
jobs:
check-code-generation:
runs-on: ubuntu-latest
permissions:
contents: write
packages: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- run: bundle exec rubocop --regenerate-todo
- name: Check rubocop todo
id: rubocop-todo-config
continue-on-error: true
run: git diff --exit-code
- uses: peter-evans/create-pull-request@v7
if: steps.rubocop-todo-config.outcome != 'success'
with:
commit-message: 'chore: Update code using `rubocop --regenerate-todo`'
branch: ${{ github.ref_name }}/propose-generated-diff
base: ${{ github.event.pull_request.head.ref }}
delete-branch: true
title: 'chore: Update code using `rubocop --regenerate-todo`'
body: 'refs. #${{ github.event.number }}'
Check rubocop todo
で差分を確認しますが、この時点でCIを終了したくないので continue-on-error: true
を設定しています。後続のステップで差分がある場合(git diff --exit-code
が終了コード1の時)をif: steps.rubocop-todo-config.outcome != 'success'
で確認して、PRを作成しています。
この例ではPRの作成に peter-evans/create-pull-request を使っていますが、他のカスタムアクションやGitHub APIで自作しても良いです。
作成中のPRに差分があれば、以下のようなPRが作成されます。
実際に使う場合は rubocop による違反をもみ消さないよう、Lintを行った後に上記の仕組みを入れるようにしましょう。
まとめ
簡単に作れるのでぜひ試してみてください。
参考
今回紹介したCIは、弊社の開発者ブログから着想を得て作成しました。
Discussion