VSCodeでモノレポだとRubocopの拡張機能がうまく動かない問題を解決した話

[SmartHRアドベントカレンダー](https://qiita.com/advent-calendar/2023/smarthr) シリーズ2の18日の記事です。前の記事はT2mさんの[Ruby未経験者のRuby Silver受験記](https://zenn.dev/both3lectrodes/articles/e3c97575855d04)でした。

## 前振り

みなさんRubocop使っていますか？

私は普段VSCodeを使ってRubyやTypeScriptを書いています。現在ジョインしているプロダクトがモノレポ構成なのですが、困ったことに[Rubocop](https://marketplace.visualstudio.com/items?itemName=rubocop.vscode-rubocop)の拡張機能がうまく動かないという問題がありました。

```
.
├── backend  # バックエンド（Rails）のアプリ
└── frontend # フロントエンド（TypeScript）のアプリ
```

のようなディレクトリ構成で、プロジェクトルートでVSCodeを開くとRubocopの起動に失敗してしまいます。

他の拡張機能、例えば[ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)のように`Working Directories` を指定する設定もなさそうです。

CIは整備してあるのでそこに任せるか、仕込んであるhuskyでコミット時に怒られるか、はたまた温かみのある手動実行をするかでお茶を濁していたのですが、いい加減コーディング中に動いてほしいということで調べてみました。

## 先に結論

`bundle binstubs rubocop` してCommand Pathにそのファイルを指定することで解決できたよ

## 原因を調べる

VSCodeのRubocopの拡張機能はいくつかあるのですが、今回はRubocop公式の [Rubocop](https://marketplace.visualstudio.com/items?itemName=rubocop.vscode-rubocop)で確認しています。（[ruby-rubocop](https://marketplace.visualstudio.com/items?itemName=misogi.ruby-rubocop)でも今回と同様の方法で動作することは確認しました）

モノレポ構成だとうまく動かない理由を見てみると以下のエラーがでます。

![スクリーンショット 2023-12-14 7.13.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1823/94241388-a24f-b85e-e62c-617661b0cfb9.png)

出力を見ると、以下のメッセージがでていてbundlerからrubocopが見えていないようでした。

```
[client] Disabling RuboCop extension, because rubocop isn't included in the bundle
```

ならばグローバルにRubocop入れたらあということで `gem install rubocop` してみたものの、backend/Gemfileで指定している諸々を読み込んでくれない（それはそう）ため期待通りに動作してくれない。


Command Pathに `bundle exec rubocop` してもパスが合わずに怒られる。うーん、どうしたものかと思っていたところ `binstub`の存在を思い出したのでした。

## 解決編

binstubとは？は以下の記事に詳しいです。
[binstubをしっかり理解する: RubyGems、rbenv、bundlerの挙動（翻訳+解説）](https://techracho.bpsinc.jp/hachi8833/2021_11_22/25037)

Rubyにおけるbinstubはコマンドを `bundle exec xxxx` 相当で実行してくれるためファイルを作ってくれるので今回の問題の解決が期待できそうです。

## binstubを作成する

```bash
cd /path/to/your/repository
cd /backend # モノレポのRailsディレクトリに移動
bundle binstubs rubocop
```

とすると、Railsのディレクトリ（今回で言うbackend配下）に`bin/rubocop` が生成されます。

## Rubocopの拡張機能に適用する

![スクリーンショット 2023-12-14 7.33.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1823/7531745e-adf1-cbf3-053d-609e7082eb67.png)


ポイントは2つで

### 1. ModeをBundlerのみ**以外**にする

 bundlerのみのModeにしてしまうと `bundle exec bin/rubocop` のように二重にbundlerを経由してしまうので、今回は **Always run** にしてみました。（Run only globallyでも動くことは確認済み）

### 2. Command Pathにbinstubsで生成したrubocopのパスを指定する

今回はRailsのディレクトリが**backend** という名前のため、 `./backend/bin/rubocop` としました。適宜読み替えてください。また、この設定を **ユーザー** 定義で指定していますが、リポジトリごとにディレクトリ名は異なる可能性があるためワークスペースで定義することをおすすめします。

これでファイル編集時にRubocopが動作するようになりました！！めでたしめでたし。

## おまけ

この指定で動作するようになったのですが、保存時にprettierのように自動フォーマットはされません。

[Rubocop拡張機能の説明にもある通り](https://github.com/rubocop/vscode-rubocop?tab=readme-ov-file#configuring-the-vs-code-editor-to-use-rubocop)以下の設定を追加することで保存時に自動フォーマットされるようになります。

### 1. VSCodeの設定 > 書式設定 > Editor: Format On Saveを有効にする

![スクリーンショット 2023-12-14 9.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1823/ea0783c8-42cb-029e-a859-127ebb8af4b2.png)


### 2. settings.jsonでrubyファイルのformatterにrubocopを指定する

```json
  // settings.json
  "[ruby]": {
    "editor.defaultFormatter": "rubocop.vscode-rubocop"
  },
```

### 3. Rubocop拡張機能のRubocop: Autocorrectを有効にする

![スクリーンショット 2023-12-14 9.26.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1823/b8400383-59be-dd85-e2d2-93a4a14d68bb.png)

これでファイル保存時に自動でフォーマットまでしてくれるようになりました。

自動フォーマットをしたくない場合は上記チェックを外せば指摘だけしてくれるようになるのでお好みで設定を変更してください。

みなさん快適なRubocopライフを！