プルリクを最新のbaseブランチに追随させる GitHub Actions

表題のワークフローを作ってみたのでその紹介です。

## 利用イメージ

プルリクに `@github-actions merge base branch` とコメントすると、

<img width="600" alt="スクリーンショット_2020-04-29_15_33_43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35733/c80c579d-0297-0a66-79f5-3424801afa08.png">

プルリクのbaseブランチをheadブランチにマージされます。

ユースケースとしては、baseブランチの変更（不具合修正や機能修正）の取り込みです。
（個人的には rebase の方が Git のログが綺麗なので好きなのですが、手元で rebase すると force push による反映となってしまうため今回のようにmergeの方が安全かも？）

## 導入方法

次のような yaml ファイルをリポジトリの `.github/workflows` 配下に置きます。

```yaml:.github/workflows/merge-base-branch.yml
name: Merge base branch

on:
  issue_comment:
    types: [created, edited]

env:
  LAUNCH_COMMAND: '@github-actions merge base branch' # 大文字小文字は問わない

jobs:
  merge:
    name: merge
    runs-on: ubuntu-18.04
    steps:
      - name: Run GitHub API
        # jobの実行条件ではenvが使えないのでここで判定
        if: github.event.issue.state == 'open' && github.event.issue.pull_request != null && contains(github.event.comment.body, env.LAUNCH_COMMAND) == true && (github.event.action != 'edited' || (github.event.action == 'edited' && contains(github.event.changes.body.from, env.LAUNCH_COMMAND) == false))
        run: |
          # プルリクエストの詳細を取得
          res=$(curl -X GET \
                     -H "Content-Type: application/json" \
                     -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
                     https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})
          head_sha=$(echo $res | tr -d '[:cntrl:]' | jq -r '.head.sha')

          # 参考: https://developer.github.com/v3/pulls/#update-a-pull-request-branch
          res=$(curl -X PUT \
               -H "Content-Type: application/json" \
               -H "Authorization: token ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \
               -H "Accept: application/vnd.github.lydian-preview+json" \
               -d "{ \"expected_head_sha\": \"$head_sha\" }" \
               https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}/update-branch)
          message=$(echo $res | tr -d '[:cntrl:]' | jq -r '.message')

          # 結果をプルリクにコメント
          if [[ $message != 'null' ]]; then
            curl -X POST \
                 -H "Content-Type: application/json" \
                 -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
                 -d "{ \"body\": \"$message\" }" \
                 https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments
          fi
```

#### コードの補足

- `github.event.issue.pull_request != null` の判定
    - `issue_comment` イベントは GitHub の Issue と Pull request 両方のコメント書き込みを契機とします。今回は Pull request に絞りたかったので、この判定をしています。
- `github.event.action == 'edited' && contains(github.event.changes.body.from, env.LAUNCH_COMMAND) == false` の判定
    - コメントの編集後に指定のコマンドが「新しく」含まれることになったかを判定しています。（コメント前は含まれていなかった事の確認。）
- `tr -d '[:cntrl:]'`
    - 何故かレスポンスに制御文字が含まれることがある？ので取り除いています。

#### 注意点

- `issue_comment` イベントを契機とするので、この yaml ファイルはデフォルトブランチに置いてください。
- `PUT` の方で利用するトークンは、もし本マージを契機に別の GitHub Actions を動かしたい場合は `GITHUB_TOKEN` 以外を指定してください。
    - 個人やボットの personal access token など。
        - トークンは yaml ファイルに直接書くのではなく、リポジトリの Secrets に登録して yaml ファイルからは `${{ secrets.HOGE }}` で参照するのが安全です。 
    - `GITHUB_TOKEN`では GitHub Actions を起動できない為です。

## おわりに

不具合等あればコメントください！