GitHub Apps + Dangerをやってみる

# はじめに
この記事はand factory.inc Advent Calendar 2022 2日目の記事です。
昨日は　@y-okudera さんの [SnapshotTestingでURLで取得するリモートの画像の代替品としてAsset Catalogの画像を使用する](https://qiita.com/y-okudera/items/13d935fad4ee248aed54) でした。

# 目的
Dangerの[公式ガイド](https://danger.systems/guides/getting_started.html)では、GitHubのユーザーのPersonal Access Tokenを用いることを推奨していますが、個別にユーザーを作る必要があり、以下の観点でよくないです。

- Parsonal access token はその性質上権限が強すぎるので、流出した時のセキュリティ的なリスクが高い。
- BOTとはいえ、ユーザー扱いであることに変わりはないので、Organizationのシート（人数）を1枠使ってしまう。

そこでGitHub Appsを経由して用いれば、ユーザー作成なしかつ、Botとして扱えるとのことなので、やってみました。

# 概要
Personal Access Tokenでは対象ユーザーのAccess TokenをGitHub上で作成した後はCIなどの環境変数として取り回すだけですが、GitHub AppsはAppの作成&連携やAccess Token生成を自前でコネコネやる必要があります。
弊社ではCircleCIを用いてますので、CircleCIで行っていきます。

| Personal Access Token使用 | GitHub Apps使用 |
| ---- | ---- |
| ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-24_23.15.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/3f9d9efe-3312-08d2-0389-7aab194a681b.png) | ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-24_23.16.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/2050a29e-edd2-eeab-87c5-57e200e99102.png)

# GitHub Apps(Danger ボット)作成手順
では、Dangerで走るところまでやっていきましょう。

## GitHub上での設定
まずはGitHubからです。

### GitHubの設定より、「Settings」→「Developer settings」を開く。
![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.10.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/5769970d-00e9-6e5c-608b-e39fc536c49b.png)

### 「New GitHub Apps」より新規作成
![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.11.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/08b811eb-5cd7-dfc1-3239-9d56dcb90c3b.png)

### それぞれ項目を埋める

- 基本情報
    - 以下は必須ですが、適当につける
        - GitHub App name
        - 説明
    - Homepage URLはGitHub上にページが作られる
    - Expire user authorization tokens のチェックを入れることで「refresh_token」を使用できる

    <details><summary>参考画像</summary>
    
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.13.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/03d49192-0276-aae1-4063-c6e2d74942c5.png) 
    </details>

- Permissions
    - MetadataRead-only
    - Issues,PullRequests,Commit statusesをRead and write

    <details><summary>参考画像</summary>
    
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/bd0bac7c-69e3-1df6-feac-09fed131354f.png)
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.13.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/f629eb2b-5ce3-426d-6197-c148b9834a71.png)
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.14.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/c9ba5c27-62b0-507f-0547-250490c747fb.png)
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.14.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/98affcaa-2209-6f53-fbc8-ca1e537890c5.png) 
    </details>

- Subscribe Eventsは特にいじらず

    <details><summary>参考画像</summary>
    
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.16.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/a71039c2-1ba2-aa2f-9f2b-c5a92bacdf6c.png)
    </details>

- GitHubを他のorganizationに連携できるように
    
    <details><summary>参考画像</summary>
    
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.16.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/275ff454-61e1-bf05-a3c6-a6e8bdc86367.png)
    </details>

### Appsページで「AppID」「Installation ID」を確認、「Private Key」を作成

- AppID
    
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.19.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/adbfbd56-2a40-3191-8bc1-99a98a1a8438.png)
    
- Private Key
    - 以下のボタンPrivate Keyが生成され、`.pem`ファイルがダウンロードされます。
    
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.19.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/cf3bc2a0-9de7-8062-f986-baa4dc138e19.png)
    
- Installation ID
    - サイドメニューの「Install App」より、連携先のorganaizationを選択
        
        <details><summary>参考画像</summary>

        ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-21_13.24.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/760b56fc-9700-7e82-d10f-d08d1e5053f1.png)
        </details>

        
    - 連携リポジトリを選択後、「Install & Request」
        
        <details><summary>参考画像</summary>
            
        ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-21_13.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/0a26d7dd-ac48-82d0-5585-657a8125b540.png)
        </details>
        
    - 出てくるURLの末尾が「Installation ID」です
        - [APIから取る方法](https://docs.github.com/en/rest/apps/installations)が一般的ですが、めんどくさかったので
        
        <details><summary>参考画像</summary>
            
        ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.20.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/8ebb5357-f66d-771b-2e7d-211bc9752b3e.png)
        </details>
        

## Circle CIの設定

### 「Project Settings」→「Environment Variables」より設定

- 先程控えた値を投入
    - PEMのみBase64でエンコード/デコードしている　※スクリプト側で改行がうまく取れないため
        
        ```bash
        $ base64 test-danger2.2022-11-13.private-key.pem | cat
        ```
        
        ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.25.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/c5382251-2989-c70b-3d64-6a4b43625f5d.png)

### 「Project Settings」→「Advanced」で「Only build pull requests」をOnにする

- OnにしないとPR作成した後に流れなくなるため

![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-14_13.22.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/bf72220f-b338-273f-9819-01726c133060.png)

## ソースコード側の修正

### スクリプトを作成

- 要はGitHub Appsのaccess tokenを取得している
    - JWT作成→access token取得→環境変数に入れる

```bash
#!/usr/bin/env bash

echo -e "\n>>> generate a JWT\n"
JWT=$(ruby << EOF
    require 'openssl'
    require 'jwt'  # https://rubygems.org/gems/jwt
    require 'base64'

    # Private key contents
    pem = Base64.decode64(ENV['GITHUB_APP_PEM_BASE64'])
    private_key = OpenSSL::PKey::RSA.new(pem)

    # Generate the JWT
    payload = {
      # issued at time, 60 seconds in the past to allow for clock drift
      iat: Time.now.to_i - 60,
      # JWT expiration time (10 minute maximum)
      exp: Time.now.to_i + (10 * 60),
      # GitHub App's identifier
      iss: ENV['GITHUB_APP_ID']
    }

    jwt = JWT.encode(payload, private_key, "RS256")
    puts jwt
EOF
)
echo -e "\n>>> create an installation access token\n"
echo $JWT
TOKEN=$(curl -s -D /dev/stderr -X POST \
    -H "Authorization: Bearer ${JWT}" \
    -H "Accept: application/vnd.github.v3+json" \
    https://api.github.com/app/installations/${GITHUB_APP_INSTALLATION_ID}/access_tokens \
| jq -r .token)
echo export DANGER_GITHUB_API_TOKEN="${TOKEN}" >> $BASH_ENV
```

### `.circleci/config.yml`にdangerのコマンド用意

- 方法は色々あるけど、今回はfastlaneを利用します
- 先程のスクリプトでAccessToken生成後、それを用いてDanger走らせる

```ruby
    lane :run_danger do
        danger(
            danger_id: "test",
            dangerfile: "./DangerFile",
            github_api_token: ENV["DANGER_GITHUB_API_TOKEN"],
            verbose: true
        )
    end
```

```yaml
# circle ci
danger:
    description: "Run danger"
    steps:
      - run: 
          name: Refresh token
          command: sh scripts/refresh_github_apps_token.sh # 先程のアクセストークン取得
      - run: 
          name: Echo check token
          command: echo $DANGER_GITHUB_API_TOKEN # テスト
      - run: 
          name: Run danger
          command: bundle exec fastlane run_danger
```

## 実行してみる！

- できた！
    - ちゃんとBotだ！
    
    ![%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2022-11-21_13.43.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/8a6b3247-f71b-84ef-16b9-f20a7b25d48c.png)
    

# まとめ

- GitHub Apps1つでOrgnization連携を複数行い、スクリプト共有すれば簡単に導入できそう！
- 今回はDanger BotをGitHub Appsで作成してみるでしたが、GitHub Appsは他にも色々できる！
    - ブランチ操作、issue作成&ブランチ作成などなど、便利！
        - [おすすめ GitHub Apps & Actions 10選｜Masanao Suzuki / GAOGAO｜note](https://note.com/masanaosuzuki/n/n2d0316fc9e51) 

# 参考
https://note.com/nttrtech/n/nef467f66506e