RuboCopのバージョンを最新に保つための技術

このエントリーをはてなブックマークに追加

はじめに

この記事は Ruby Advent Calendar 2022 の 19 日目の記事です。

本記事では RuboCop のバージョンアップに追従するために使える補助ツールや RuboCop の新機能の紹介をしたいと思います。
RuboCop を最新に保つということはとても良い点があります。

それは誤検知が発生するケースや、誤った自動修正をするケースが最新版であれば修正されている場合があります。
新しい cop(= RuboCop のルール)をすぐに使わない方針だったとしても非常に嬉しい点だと思います。

例を挙げると最新の RuboCop 1.40 でも以下のように沢山の Bug fixes の対応が入っています。

沢山のBug fixesがされている様子

RuboCop とは?

RuboCopのロゴ

ご存知だとは思いますが、紹介させていただきます。

RuboCop は、Ruby の静的解析ツール(Linter)及びコードフォーマッターです。

デフォルトでは、RuboCop チームが管理しているRuby Style Guideで概説されているガイドラインの多くが適用されます。
コードを解析して発見された問題を報告するだけでなく、RuboCop はそれらの問題を自動的に修正することもできます。

https://github.com/rubocop/rubocop

RuboCop のバージョンを最新に保つための技術

ここからは RuboCop のバージョンを最新に保つために作成した補助ツールを紹介したいと思います。
もし、便利そう!と思っていただければご活用いただけると嬉しく思います。

もし何か問題があった場合はお気兼ねなくお知らせください。

rubocop-updater

https://github.com/ydah/rubocop-updater

RuboCop を更新して.rubocop_todo.ymlを再生成し、新しく追加された cop のすべての違反を除外したコミットを追加した、Pull Request を作成するカスタムアクションです。

このカスタムアクションを起動すると、以下のコマンド実行後ごとにコミットを積んで Pull Request を作成します。

  1. bundle exec rubocop --auto-gen-config
  2. bundle update --conservative rubocop
  3. bundle exec rubocop --auto-gen-config

使い方

手動でワークフローを実行する場合のワークフローの例は以下の通りです。

# .github/workflows/rubocop-updater.yml
name: rubocop-updater
on:
  workflow_dispatch:
    inputs:
      update_target:
        type: choice
        description: Name of the RuboCop (or extension) to be updated
        options:
          - rubocop
          - rubocop-performance
          - rubocop-rails
          - rubocop-rspec
        required: true
      conservative_update:
        type: boolean
        description: Is conservative update?
        default: true
        required: true

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: ydah/rubocop-updater@v0
        with:
          label: rubocop-update
          update_target: ${{ github.event.inputs.update_target }}
          conservative_update: ${{ github.event.inputs.conservative_update }}

この例だと Actions > rubocop-updater を選択して、Run workflow から以下のように実行することができます。

ワークフローを手動で実行する

すると、以下のような Pull Request が作成されます。

ワークフローを手動で実行する

rubocop-todo-regenerator

https://github.com/ydah/rubocop-todo-regenerator

特定のラベルを PR につけると .rubocop_todo.yml を再生成したコミットを積んでくれるカスタムアクションです。

RuboCop のバージョンを上げる時には新規に追加された違反はひとまず .rubocop_todo.yml に追加してバージョンアップのみを実施することが多いと思います。
ただ、大きなプロジェクトだと .rubocop_todo.yml の再生成に時間がかかることがあります。

そこで、 bundle update rubocop だけをしたコミットを積んだ Pull Request を作ってしまって、このワークフローを起動します。
そうすることで GitHub Actions に.rubocop_todo.yml の再生成のコミットを積むのを任せることができます。

ワークフローの例は以下の通りです。

# .github/workflows/rubocop-todo-regenerator.yml
name: rubocop-todo-regenerator
on:
  pull_request:
    types:
      - labeled
jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: ydah/rubocop-todo-regenerator@main
        with:
          github_token: ${{ secrets.WRITABLE_GITHUB_TOKEN }}

RuboCop 1.40 で追加したオプションについて

ここまでは作ってきた RuboCop のバージョンを上げるための補助的なツールを紹介しましたが、RuboCop 1.40 で追加した RuboCop のバージョンアップに使える機能を紹介します。

Add --[no-]auto-gen-enforced-style CLI option #11205
https://github.com/rubocop/rubocop/pull/11205

このパッチでは --[no-]auto-gen-enforced-style という CLI オプションを追加しています。

一部の cop は EnforcedStyle を設定可能なオプションとして持っています。
例えば、Style/AccessModifierDeclarations が該当します。

ドキュメント

auto-gen-config を実行して、.rubocop-todo.ymlを作成する場合、以下のどちらかの通りに違反を抑制します。

  • ファイル単位で該当の cop の違反の抑制
  • cop そのものを無効化する設定を追加して抑制 (規定の違反ファイル数を超過した場合1)

ですが、EnforcedStyle を持つ cop については動作が異なります。
.rubocop_todo.yml を生成する際に、そのスタイルの設定を追加することによって違反を抑制する場合があります。

例を挙げると Style/ClassAndModuleChildren の場合、以下の二つのスタイルがあります。

EnforcedStyle: nested (default)

# good
# have each child on its own line
class Foo
  class Bar
  end
end

EnforcedStyle: compact

# good
# combine definitions as much as possible
class Foo::Bar
end

全てが compact のスタイルで書かれていた場合には auto-gen-config を実行した場合に .rubocop_todo.yml は以下のように生成します。

Style/ClassAndModuleChildren:
  EnforcedStyle: compact

通常の場合には、既に統一されているスタイルがあった場合に、それを知ることができるため便利な機能です。
しかし、--auto-gen-configしても違反が残ってしまうケースがありました。

それは Layout/SpaceInsideHashLiteralBraces の以下のようなケースです。

# test.rb
a = {

}

この場合に bundle exec rubocop --auto-gen-config を実行すると .rubocop_todo.yml は以下のように生成します。

# .rubocop_todo.yml

# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyleForEmptyBraces.
# SupportedStyles: space, no_space, compact
# SupportedStylesForEmptyBraces: space, no_space
Layout/SpaceInsideHashLiteralBraces:
  EnforcedStyle: space

この状態で再度 bundle exec rubocop を実行すると、以下の違反が残っていることがわかります。

test.rb:1:6: C: [Correctable] Layout/SpaceInsideHashLiteralBraces: Space inside empty hash literal braces detected.
a = { ...

これは確かに全てのファイルで EnforcedStyle: space というスタイルに適合したコードなので、EnforcedStyle: space を設定することで違反を抑制しようとします。
しかし、この cop には以下の設定オプションも存在しています。

EnforcedStyleForEmptyBraces: no_space (default)

# The `no_space` EnforcedStyleForEmptyBraces style enforces that
# empty hash braces do not contain spaces.

# bad
foo = { }
bar = {    }
baz = {
}

# good
foo = {}
bar = {}
baz = {}

なので、実際には違反が残り続けるという現象が発生していました。

そのため、--no-auto-gen-enforced-style オプションを追加しました。
auto-gen-config 実行時に指定することで、たとえ全てのファイルで同じスタイルで書かれていたとしても EnforcedStyle を持たない他の cop と同様の動作となります。

是非、同様のケースでバージョンだけを上げて後でゆっくりと TODO を解消したい場合にはご活用ください。
今回紹介したオプション以外にも便利なオプションはありますので詳細は以下をご確認ください。

https://docs.rubocop.org/rubocop/configuration.html#automatically-generated-configuration


  1. デフォルトは 15 ファイルです