reviewdog🐶を飼ってGitLab-CI上で静的解析しませんか？

# はじめに
レビュワーの工数削減とヒューマンエラーを防ぎコードの品質をさらに向上させていく取り組みの一環として、**自動コードレビュー（静的解析）** を検討しました。

そこで、`reviewdog`というツールを用いて実装しようとしたのですが、
reviewdogは`github-actions`ベースで開発がされているため、gitlabでの使用に関しては、ほとんど事例・記事はありませんでした。

導入まで少しだけ苦労したので、この記事がgitlab上でreviewdogの使用を想定している人に向けて参考になれば幸いです。


## reviewdogとは？
Go言語で記述されたOSS。
各種 linter 解析の検出結果を MR（またはPull Request） にコメントする形で指摘してくれるツール

以下のreviewdog開発者の記事を読むと概要が掴めると思います。

[Reviewdog を飼ってコードレビューや開発を改善しませんか](http://haya14busa.com/reviewdog/)

![スクリーンショット 2023-04-02 8.23.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/679cf1eb-38d4-2bc8-52f9-dfdbc7b4615b.png)


## 導入までの全体感
1. reivewdog開発用ブランチ(`feature/reviewdog`）を作成
1. `feature/reviewdog`→`develop`ブランチへのMRを作成する
1. `.gitlab-ci.yml`にreviewdog導入のスクリプトを書く
1. 変更をpushしてGitlab CI（パイプライン）を走らせ、Gitlab CIコンソールベースで確認しデバッグを行う
1. 3,4を、エラーがなくなりreviewdogが正常に機能するまで繰り返す
1. `develop`ブランチにマージする



# ▼ 準備（テスト用リポジトリを使用する場合）
reviewdog導入テスト用のリポジトリを使用する場合はこちらから開始。
※通常は、既存リポジトリに対してreviewdog用のブランチを切って作業する方法で良い

1. テスト用新規リポジトリ作成（例：`reviewdog-test`など）
1. `.gitlab-ci.yml`の修正
1. 試しにpushするとパイプラインが実行されない。どうやらgitlab runnerが登録されていないよう
    ```
    this job is stuck because you don't have any active runners that can run this job.
    ```
4. gitlab runnerの登録

    **【gitlab runnerとは？】**
    GitLabのCI/CDパイプラインはリポジトリのルートにある `.gitlab-ci.yml` という`yamlファイル`によって構成されます。
    Runnerは `.gitlab-ci.yml` ファイル に記載された処理を実行するアプリケーション
    
    ```bash
    # gitlabインスタンスで実行
    # 新規リポジトリでのrunnerの登録
    $ gitlab-runner register
    ~
    ~
    => Runner registered successfully. Feel free to start it, but if its running already the config should be automatically reloaded!
    # gitlab-runnerの起動
    $ gitlab-runner run
    ```
    
    
    
    任意のタグでgitlab runnerが登録されていることを確認
    
    ![reviewdog0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/ff0fba78-6dc2-1c04-8979-3949928ac214.png)

    
    パイプラインを走らすと無事実行された！

    - 参考
    https://www.gitlab.jp/blog/2022/09/26/runner-summary/#gitlab%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%ABrunner%E3%82%92%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B
    



# ▼ reviewdog導入
## gitlabにreviewdogユーザーの作成
MRにコメントを追加するAPIトークンを取得するため、管理者メニューからユーザー追加でreviewdog用のユーザーを作成

![reviewdog1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/92852d1e-9391-21f9-8d13-e19c41aecb34.png)


reviewdogユーザーでgitlabにログインし[APIアクセストークン](https://www.gitlab.jp/blog/2021/12/06/access-token/)の取得を選択


適切な権限をつけてAPIアクセストークンを発行

![reviewdog2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/67a9b547-2dd0-7994-ff00-12ffbbd28d99.png)




> ※注意点
プロジェクト単位でアクセストークンを発行することもできますが、その場合アイコンを設定できないためレビューがややたんぱくになってしまう

## gitlab-runnerにタグを追加
パイプラインを実行するときのstageを既存の処理と分けたいので、新しいタグ（`reviewdog`）を追加する
![reviewdog3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/f7cdef09-c318-74c7-b2ae-0e8795917ee8.png)

## 開発用ブランチ作成
ブランチ戦略は`Git Flow`を採用

`develop`ブランチから`feature/reviewdog`ブランチを作成。

以降の作業は`feature/reviewdog`ブランチで行うこと。

## `.gitlab-ci.yml`に追記
reviewdogの導入は基本的には`.gitlab-ci.yml`上で行う

`.gitlab-ci.yml`にjobを作成し、job内でreviewdogのコマンドを叩くことで実行される

ここでは、例として、以下３つのlinterの設定を行う
- **textlint**
    - README.mdの校正
- **ESlint**
    - フロントエンド（Vue.js）用のlinter
- **rubocop**
    - バックエンド（ruby）用のlinter



### 共通部分に追記
- `variables`に先ほど取得したreviesdogユーザーのAPIトークンキーを追加する
- ESlint用のパス設定用の環境変数を設定
- stagesに`reviewdog`を追加する（パイプライン実行時にstageを`test`とは分けたかったため）

    ```yml:.gitlab-ci.yml
    variables:
      # reviewdogユーザーから発行したAPIトークンを設定.これがないとreviewdogがコメントできない。
      REVIEWDOG_GITLAB_API_TOKEN: 発行したアクセスキー
      # eslintのチェック対象とするdir/fileパスを指定。指定したパスに変更があったときのみreviewdog-eslintを実行する。
      ESLINT_TARGET_PATH: app/javascript/packs/
    
    stages:
      - test
      - reviewdog
    
    ```
### textlint

- textlintインストール
```bash
$ npm init -y  # package.jsonがすでに存在すればスキップ。だいたいすでに存在しているはず。
$ npm install --save-dev textlint # --save-devをつけることで開発環境だけに適用
$ npx textlint -v
=> v11.6.3

# textlint のルールは、本体とは別に Node パッケージとして提供されているので、ルールごとに次のようにインストール

# 「ら」抜き言葉を検出するルール
$ npm install --save-dev textlint-rule-no-dropping-the-ra
# 「〜である。〜ます。」の混在を検出するルール
$ npm install --save-dev textlint-rule-no-mix-dearu-desumasu
```

```yml:.gitlab-ci.yml
reviewdog-textlint:
  stage: reviewdog
  variables:
    # 「reviewdog: fail to get diff: failed to get merge-base commit: exit status 1」エラーへの対処
    GIT_STRATEGY: clone
    GIT_DEPTH: 0
  tags:
    - reviewdog
  allow_failure: true # CIに失敗してもMRをマージ可能にする設定
  rules:
    # ソースブランチがfeature/XXX かつ ターゲットブランチがdevelopブランチのときのみパイプラインに追加
    - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
    # ソースブランチがhotfix/XXX かつ ターゲットブランチがmasterブランチのときのみパイプラインに追加
    - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^hotfix/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
  script:
    - echo "############ reviewdog-textlint start.....############"
    - echo "source_branch:$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"
    - echo "target_branch:$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
    # nvmのinstall
    - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
    # nvm: command not foundへの対応
    - source ~/.nvm/nvm.sh
    # nvm install --lts 版だとエラーになるので、v16を使用。
    - nvm install 16.16.0
    # installの確認
    - node -v
    - npm -v
    # このリポジトリにあるpackage.jsonに記載された内容を元にnode moduleの読み込み
    - npm install
    # reviewdogのinstall.  bin/ 配下に実行ファイルがインストールされる
    - wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s
    # *.md ファイルを対象にtextlintを実行
    - npx textlint -f checkstyle ./ | bin/reviewdog -f=checkstyle -name="textlint" -reporter=gitlab-mr-discussion
```



### eslint
```yml:.gitlab-ci.yml
reviewdog-eslint:
  stage: reviewdog
  variables:
    # 「reviewdog: fail to get diff: failed to get merge-base commit: exit status 1」エラーへの対処
    GIT_STRATEGY: clone
    GIT_DEPTH: 0
  tags:
    - reviewdog
  allow_failure: true # CIに失敗してもMRをマージ可能にする設定
  rules:
    # ソースブランチがfeature/XXX かつ ターゲットブランチがdevelopブランチのときのみパイプラインに追加
    - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
      changes:
        - $ESLINT_TARGET_PATH
    # ソースブランチがhotfix/XXX かつ ターゲットブランチがmasterブランチのときのみパイプラインに追加
    - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^hotfix/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
      changes:
        - $ESLINT_TARGET_PATH
  script:
    - echo "############ reviewdog-eslint start.....############"
    - echo "source_branch:$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"
    - echo "target_branch:$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
    # nvmのinstall
    - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
    # nvm: command not foundへの対応
    - source ~/.nvm/nvm.sh
    # nvm install --lts 版だとエラーになるので、v16を使用。
    - nvm install 16.16.0
    # installの確認
    - node -v
    - npm -v
    # このリポジトリにあるpackage.jsonに記載された内容を元にnode moduleの読み込み
    - npm install
    # reviewdogのinstall.  bin/ 配下に実行ファイルがインストールされます
    - wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s
    # -f checkstyle 対象とするdir/fileパス指定
    # 例:app/javascript/packs/**/**/*.{ts,js,vue} → ts,js,vueファイル限定
    # 例:app/javascript/packs/**/**/*.{ts,js,vue}, app/javascript/packs/**/*.{ts,js}  → 複数指定可能。但し、指定した拡張子のファイルがパスに存在していないといけない。
    # 例:app/javascript/packs/ → packs配下の全てのファイル
    - npx eslint -f checkstyle $ESLINT_TARGET_PATH | bin/reviewdog -f=checkstyle -name="eslint" -reporter=gitlab-mr-discussion
```

### rubocop
```yml:.gitlab-ci.yml
rspec:
  image: ruby:2.7.2
  stage: test
  tags:
    - testing
    - reviewdog
  before_script:
    - hogehoge
  script:
    - hogehoge
  after_script:
    # reviewdog ✖︎ rubocopとの連携を実行
    - echo "############ reviewdog-rubocop start.....############"
    - wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s
    - bundle exec rubocop | bin/reviewdog -f=rubocop -reporter=gitlab-mr-discussion
  artifacts:
    paths:
      - coverage/
    expire_in: 10 days
```


# ▼ 動作確認
MergeRequestを作成し、literが警告を出した箇所を確認すると、reviewdogがコメントをしてくれます。
ここまで確認できたら成功です。

アイコンが愛くるしいですね🐶

- textlint
![reviewdog5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/bff1450f-c44e-c5e2-87eb-46c34cf24e97.png)

- eslint
![reviewdog6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/c505083d-725d-3c5b-dbbc-2bb9e3df1b52.png)

- rubocop
![reviewdog7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669969/d9fd9628-e080-6af7-54a8-8317b6e99a88.png)


# ▼ChatGPTの活用

余談ですが、ChatGPTを使用するとある程度のヒントをくれます。

細かい修正はご自身の環境でデバッグしながら行う必要があります。


以下に、質問の例を示しておきます。プロンプトエンジニアリングの観点から、「**段階的かつ論理的に**」という言葉を入れると良いです。

> あなたは優秀なソフトウェアエンジニアです。
> gitlab上にreviewdogを用いて静的解析を行いたいです。
.gitlab-ci.ymlにreviewdog導入のスクリプトを記述してたいです。
例を示して、段階的かつ論理的に教えてください。

# ▼参考記事
## reviewdog / linter
 - reviewdoc
[README](https://github.com/reviewdog/reviewdog)

- 開発者の話
[Reviewdog を飼ってコードレビューや開発を改善しませんか](http://haya14busa.com/reviewdog/)

- textlint
    - [textlint + Reviewdog + GitLab CI でアドベントカレンダーの校正をした話](https://www.forcia.com/blog/002374.html)
    - [よりよい文書を書くための校正ツール「textlint」のSmartHR用ルールプリセットを公開しました！](https://shanaiho.smarthr.co.jp/n/n881866630eda)


## gitlab ci 
- gitlab ci
https://qiita.com/yurano/items/a7804d987ccff37b1a9d
https://qiita.com/ynott/items/1ff698868ef85e50f5a1
https://qiita.com/kytiken/items/a95ef8c1fccfc4a9b089

- .gitlab-ci.yml
https://www.sobyte.net/post/2022-09/reviewdog-gitlab/
https://dev.classmethod.jp/articles/sample-to-understand-rules/


- gitlab runer
https://blogs.networld.co.jp/entry/2022/03/10/090000