DangerでPRのコード差分にインラインでコメントをつける


# 概要

GitHub上で「PRの変更内容に特定のキーワードが含まれていたら、変更された箇所にコメントをつける」 
というのを調べてやり方がわかったのでメモとして書いていきます。

![スクリーンショット 2020-02-09 11.46.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161845/079e3d28-934e-c84d-bd95-ef6b94c391c5.png)

GitHubActionsやDanger Pluginについて知っているかたは、
「[PRのコードにインラインコメントをつける](#prのコードにインラインコメントをつける)」まで飛ばしてください。

DangerはPullRequestの内容を自動でチェックしてコメントをつけてくれるOSSです。

この記事はDangerのセットアップや基本的な使い方については記載はしていないので、「Dangerについて詳しく知りたい！」というかたは是非ほかのかたの記事を探してみてください。

# サンプル

Rubyは不慣れなのであまり良いコードではないですが確認サンプルを作ってあります。

リポジトリ: https://github.com/rnishimu22001/DangerPlayground
動作のサンプルPR: https://github.com/rnishimu22001/DangerPlayground/pull/2

# 利用環境

「GitHub.com + GitHubActions + Danger」

DangerはRuby版です。

# GitHubActionsの設定

こちらの記事を参考にさせていただきました :pray: 
https://qiita.com/tarumzu/items/87e4d9801d0a413c80f1

https://github.com/rnishimu22001/DangerPlayground/blob/master/.github/workflows/danger.yml

PRを作成したときにGitHubActionsが実行されるように作っています。

```yaml
# どのタイミングで実行するか？今回はPRがopenされたときにこのActionを実行させる。
on: pull_request

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    # Actionsで必要なセットアップをしていく
    - uses: actions/checkout@v1
    - name: Setup Ruby for use with actions
      uses: actions/setup-ruby@v1.0.0
      with:
        version: '2.6'
    - name: Install Danger
      run: |
          gem install bundler
          bundle install
    # Dangerの実行
    - name: Run Danger
      env:
          # Dangerがコメントするために必要？
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: danger
```

# Danger Pluginを作成する

Danger Pluginとしてclassを作成すればDangerの機能をあまり考ずに使えそうなので今回はDanger Pluginで作成しました。

```ruby

# code_review.rbのファイル内

module Danger
    class CodeReview < Plugin
        def code_review
            # コードチェックに関する実装を記述...
        end
    end
end
```

https://github.com/rnishimu22001/DangerPlayground/blob/master/code_review.rb

Dangerfileファイルへの記述はこんな感じです。

```ruby

# importのパスは適宜変えてください。
danger.import_plugin("./code_review.rb")
code_review.code_review
```

https://github.com/rnishimu22001/DangerPlayground/blob/master/Dangerfile

# 変更されたファイルパスを取得する

リファレンスによると`git.added_files`などで追加されたファイル、修正されたファイルのパスが取れます。

> Paths for files that were added during the diff
added_files FileList
Paths for files that were removed during the diff
deleted_files FileList
Paths for files that changed during the diff
modified_files FileList

https://danger.systems/reference.html

各ファイルを取得して変更されたファイルリストを作っていきます。

```ruby

# gitモジュールから変更されたファイルパスを取得する 
file_paths = git.modified_files + git.added_files
```

https://github.com/rnishimu22001/DangerPlayground/blob/master/code_review.rb#L10

# gitの変更情報を取得する

今回はあくまで動作確認のためなのでコード差分の情報を抜き出す簡易なものを作っていきます。
ライブラリとかあればそっちを使うほうが賢いです。


今回はDangerが提供してくれている`diff_for_file`を使って作っていきます。
1ファイルずつのdiffを見れるので制御がしやすいです。

> Details for a specific file in this diff
diff_for_file Git::Diff::DiffFile

https://danger.systems/reference.html

# git diffの中身をパースする

ファイルの差分情報の
`git.diff_for_file(変更のあったファイルパス).patch`を見てみると以下のようになります。

```diff

diff --git a/hello.rb b/hello.rb
index 15aaec7..2e57c3c 100644
--- a/hello.rb
+++ b/hello.rb
@@ -1 +1,2 @@
-puts "hello world"
+# TODO: 後で直す
+puts "hello world!!!"
```

余談ですがいつも見てるこの`git diff`のフォーマットは`Unified Format`というらしいです。
https://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html

| 行の種類 | 対応しそうな正規表現 |
|---|---|
| ファイルの変更情報 | ^@@ |
| 追加された or 修正された行 | ^\+ |
| 削除された or 修正前の行 | ^\- |

以上の内容を元に愚直にパースして変更行の情報を抜き出していきます。
https://github.com/rnishimu22001/DangerPlayground/blob/master/code_review.rb#L26-L65

パースをする際に手こずったことですが、`Danger`からPRにコメントをつける場合に削除された行につけれませんでした。
PRで削除された行は行としてカウントされないようなので気をつけてください。

https://github.com/rnishimu22001/DangerPlayground/blob/master/code_review.rb#L50

削除行にコメントつける方法があれば教えて欲しいです :cry: 

# PRのコードにインラインコメントをつける

知らなかったんですがDangerを使う際によく使う`warn`や`message`などには引数が追加できるようです :fearful: 
https://github.com/danger/danger/blob/master/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb#L63

`file_path`: コメントしたいファイルのパス
`line`: コメントを残したい行

```ruby

# 指定したファイルの行にコメントを残す
warn("コメントしたいメッセージ", file: "./hogehoge.rb", line: 1)
```

https://github.com/rnishimu22001/DangerPlayground/blob/master/code_review.rb#L23

これを実際に実行をしてみてコメントをつけることができました :tada: 

![スクリーンショット 2020-02-09 11.46.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161845/079e3d28-934e-c84d-bd95-ef6b94c391c5.png)

https://github.com/rnishimu22001/DangerPlayground/pull/2#discussion_r375328482

# やりのこしたこと

以下のパターンだとクラッシュしてしまうので変更情報のバリデーションを追加する必要があります。

* 画像などの形式の違うファイル
* リネームのみで変更情報しかないファイル

ほかにも[git diffの中身をパースする](# git diffの中身をパースする)で記載したパターンだとObjective-Cのクラスメソッドも引っかかったりとたくさん穴がありそうなのでライブラリを使うのが良さそうです。

また、自前で作らなくてもキーワード検知のDanger Pluginは探せばありそうな気がしています。