GitHub上の作業を自動化して幸せになろう 〜ProbotでGitHub Appsを作る〜

[DMM.com Advent Calendar 2018](https://qiita.com/advent-calendar/2018/dmm) 1日目の記事です。

こんにちは。DMM.comのサービス全体で利用される認証基盤の開発・運用を担当している[@_kentaro_m](https://twitter.com/_kentaro_m)です。

みなさん、GitHubは使っていますか。DMMではGitHub Enterpriseが導入されており、システム開発する上で無くてはならない存在となっています。

[DMM.comのGitHub EnterpriseとCircleCI Enterpriseの活用例 | GitHub Resources](https://resources.github.com/videos/Case-study-of-GitHub-Enterprise-and-CircleCI-Enterprise-integration-by-DMM/)

本日はGitHub上で発生する作業を自動化するために、GitHubと連携するアプリケーション「GitHub Apps」とそれを作るためのフレームワークである「Probot」を試してみたので紹介したいと思います。

## その作業、自動化できますよ
GitHubを使っていると、あの作業自動化できるのではという場面に遭遇することがあります。

* Pull Requestをマージしたタイミングで自動でブランチを削除して欲しい
* Pull Requestが作成されたタイミングで特定のレビュアーを追加して欲しい
* Pull RequestのタイトルにWIPが含まれているときにマージできないようにして欲しい
* 一定期間動きのないIssueをクローズしたい

このような作業を自動化するために、GitHub Appsという仕組みがGitHubより提供されています。リポジトリにアプリケーションをインストールすることで、例に挙げた作業を自動で行ってくれます。

GitHub Appsは誰でも公開することが可能で、以下のように既に多くのアプリケーションが公開されています。「Add to GitHub」ボタンを押すと、すぐに利用可能です。

* [Delete merged branch](https://probot.github.io/apps/delete-merged-branch/)
    * Pull Requestをマージしたタイミングで自動でブランチを削除する
* [Auto Assign](https://probot.github.io/apps/auto-assign/)
    * Pull Requestを作成したタイミングで特定のレビュアーを追加する
* [Work In Progress](https://probot.github.io/apps/wip/)
    * Pull RequestのタイトルにWIPが含まれているときにマージできないようにする
* [Stale](https://probot.github.io/apps/stale/)
    * 一定時間動きのないIssueを自動でクローズする

他にも様々な作業を自動化するGitHub Appsが公開されています。

[Featured Apps | Probot](https://probot.github.io/apps/)

## 作業の自動化を支える仕組み
GitHub上の作業自動化を実現する仕組み「GitHub Apps」とそれを作るためのフレームワーク「Probot」について詳しく見ていきます。

### GitHub Apps
GitHub AppsはGitHubと連携するアプリケーションの形式です。OrganizationアカウントまたはUserアカウントに直接インストールして使用します。

基本的にはリポジトリのイベント受信 (Webhook受信) をトリガーに動作し、リポジトリを操作 (API実行) する動作のアプリケーションが多いです。

<img width="1920" alt="github_apps.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/f8cd7b96-a3de-9ad7-7586-431d0d500051.png">


例えば、Pull Requestのマージでブランチを自動削除する[Delete merged branch](https://probot.github.io/apps/delete-merged-branch/)の場合は、Pull Requestをマージしたイベントをトリガーに動作し、ブランチを削除するAPIを呼び出して、作業自動化を実現しています。

GitHub Appsの特徴としては、インストールしたアカウント配下のリポジトリごとにアクセス制御が可能な点です。リポジトリのイベント受信とリポジトリ操作の権限を与えるかをそれぞれのリポジトリで設定し、アプリケーション動作の有効・無効を制御しています。

### Probot
[Probot](https://probot.github.io/)はGitHub Appsを作るためのフレームワークです。TypeScriptまたはJavaScriptで開発することが可能で、リポジトリのWebhook受信やGitHub API実行を簡単に利用できる仕組みを提供しています。

## GitHub Appsを作る
ProbotでGitHub Appsを実際に作ってみたいと思います。今回はサンプルとして、Pull Requestがオープンしたタイミングで「レビュー可能」というラベルを追加するアプリケーションを作りたいと思います。

<img width="786" alt="Screen_Shot_2018-11-29_at_1_52_27.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/dee511e5-d763-82b2-4301-47cbb44c0fcd.png">

以下の手順で作っていきます。[Developing an app | Probot](https://probot.github.io/docs/development/)に記載されているとおりです。

1. create-probot-appでプロジェクトテンプレートを作る
2. app.ymlを変更し、アクセス権限を設定する
3. GitHubにアプリケーションを登録する
4. Pull Requestへのラベル追加処理を実装する
5. アプリケーションの動作確認を行う

### 1. create-probot-appでプロジェクトテンプレートを作る
yarnコマンド (or npxコマンド)でプロジェクトテンプレートを作成することができます。

2018年11月27日時点でプロジェクトテンプレートは4種類、Issueのコメント追加(basic-js/basic-ts)、アプリケーションのデプロイ(deploy-js)、リポジトリのGit操作(git-data-js)、Checks APIとの連携(checks-js)が用意されています。

Issueのコメント追加のみTypeScriptに対応していました。

[probot/create-probot-app: 🤖📦 Create a new probot app](https://github.com/probot/create-probot-app)

```
❯ yarn create probot-app pr-add-label
yarn create v1.9.4
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Installed "create-probot-app@3.0.2" with binaries:
      - create-probot-app

Let's create a Probot app!
Hit enter to accept the suggestion.

? App name: pr-add-label
? Description of app: A Probot app
? Author's full name: Kentaro Matsushita
? Author's email address: 
? Homepage:
? GitHub user or org name: kentaro-m
? Repository name: pr-add-label
? Which template would you like to use? basic-ts
```

`yarn build`コマンドでTypeScriptのビルド、`yarn dev`コマンドでローカル実行が可能です。

```
❯ cd pr-add-label
❯ yarn build && yarn dev
 Welcome to Probot! Go to http://localhost:3000 to get started.
```

ローカルホストにアクセスすると、アプリケーション起動が確認できます。

<img width="1279" alt="Screen Shot 2018-11-27 at 1.54.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/67807ba0-d762-2da0-3a75-edd1e3176514.png">

`Ctrl` + `C`でアプリケーションを停止します。

### 2. app.ymlを変更し、アクセス権限を設定する
アプリケーションの動作に必要な権限を付与します (`app.yml`に定義せずに[GitHub Appsの管理画面](https://github.com/settings/apps/new)から設定することも可能です)。

付与できるアクセス権限は以下より詳細を確認ください。

* Events
    * [Webhooks | GitHub Developer Guide](https://developer.github.com/webhooks/)
* Permission
    * [GitHub App Permissions | GitHub Developer Guide](https://developer.github.com/v3/apps/permissions/) 

```yml:app.yml
default_events:
    # プルリクエストイベントを受信許可
    # (プルリクエストが作成されたイベントを拾うため)
    - pull_request

default_permissions:
     # メタ情報のread権限付与
     metadata: read
     # プルリクエストのwite権限付与
     # (プルリクエストにラベルを追加するため)
     pull_requests: write
```

### 3. GitHubにアプリケーションを登録する
GitHubでアプリケーションを動作させるために登録作業が必要です。登録方法は2種類あり、その違いはGitHub Apps設定画面の必要項目の入力を自動入力するか、手入力するかの違いがあります。

今回は自動登録で説明します。手動で登録したい方は下記リンクを参照ください。

[Manually Configuring a GitHub App | Probot](https://probot.github.io/docs/development/#manually-configuring-a-github-app)

再度アプリケーションを起動し、ローカルホストにアクセスします。

```
❯ yarn build && yarn dev
 Welcome to Probot! Go to http://localhost:3000 to get started.
```

Register GitHub Appをクリックします。
<img width="1277" alt="Screen_Shot_2018-11-29_at_0_21_16.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/16e76346-9cb9-ce2c-4437-8bec25b98ac3.png">

アプリケーションの名前を入力します。変更がなければ、Create GitHub Appをクリックします。
<img width="1278" alt="Screen Shot 2018-11-29 at 0.25.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/4e28b97b-eeb2-7068-59bf-11e721f7ba85.png">

アプリケーションをインストールするアカウントを選択します。
<img width="1279" alt="Screen_Shot_2018-11-29_at_0_26_50.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/56a103e7-92b3-bfd1-731f-14dc66501777.png">

アプリケーションの動作を許可するリポジトリの選択と付与したアクセス権限の確認を行います。ここで表示される権限は`app.yml`で定義したものです。問題なければ、Installをクリックします。
<img width="1279" alt="Screen_Shot_2018-11-29_at_0_30_43.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/363ef968-1d3e-3636-c605-f58d9aedeac1.png">

アプリケーションのインストール後、ローカル開発環境ではアプリケーションの動作に必要な情報が記載された`.env`ファイルが生成されます。

```
WEBHOOK_PROXY_URL=<Webhookを受信するエンドポイント>
APP_ID=<アプリのID>
PRIVATE_KEY=<秘密鍵>
WEBHOOK_SECRET=<Webhookをセキュアに扱うための認証トークン>
```

### 4. Pull Requestへのラベル追加処理を実装する
ラベル追加処理は`app.on`でPull Request作成のWebhookイベント受信をトリガーに、`context.github`([@octokit/rest](https://github.com/octokit/rest.js): Node.js製GitHub APIクライアントのインスタンス)でラベル追加APIを実行して実現しています。

Webhook受信及びGitHub APIの呼び出しに関しての詳細は以下公式ドキュメントをご覧ください。

* [Receiving webhooks | Probot](https://probot.github.io/docs/webhooks/)
* [Interacting with GitHub | Probot](https://probot.github.io/docs/github-api/)

```ts:src/index.ts
import { Application } from 'probot' // eslint-disable-line no-unused-vars

export = (app: Application) => {
  // Pull Requestが作成されたタイミングで呼び出されます
  app.on('pull_request.opened', async (context) => {

    // ラベル追加APIを呼び出す際のリクエストパラメータ生成
    // Returns: {owner: 'username', repo: 'reponame', number: 123, labels: ['レビュー可能']}
    const params = context.issue({
      labels: ['レビュー可能']
    })

    // 作成されたPull Requestにラベル追加
    await context.github.issues.addLabels(params)
  })
}
```

### 5. ローカルでアプリケーションの動作確認を行う
[smee.io](https://smee.io/)というWebサービスを利用し、インターネット上に公開したエンドポイントでWebhookイベントの受信を行い、ポートフォワーディングを利用してローカルホストで受け取ることが可能です。この仕組みを利用することで、効率よく開発できます。

アプリケーションのビルドと起動を行います。

```
❯ yarn build && yarn start
```

さきほどアプリケーションをインストールしたリポジトリでプルリクエストを作成します。

<img width="1278" alt="Screen Shot 2018-11-29 at 1.51.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/e85e0d1b-0592-4157-92a4-0186ef666651.png">

ラベル追加に成功しました。

<img width="786" alt="Screen_Shot_2018-11-29_at_1_52_27.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/dee511e5-d763-82b2-4301-47cbb44c0fcd.png">

うまく動作しない場合はコンソールに出力されているログを確認しましょう。

<img width="700" alt="Screen_Shot_2018-11-29_at_1_57_22.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/4820a696-7e96-2582-bc9b-9043713b858c.png">

また、直近のWebhookイベントは記録されているので、GitHub AppsのAdvanced設定画面から再送することができます。この機能を使用して、効率よくデバッグしましょう。

<img width="1277" alt="Screen_Shot_2018-11-29_at_2_13_06.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/b45c5914-3762-49cc-f27f-68669e9adf76.png">


## GitHub Appsを公開する
ローカルでアプリケーションの動作確認が済んだら、アプリケーションを公開するために無料で利用できるクラウドプロバイダーにデプロイを行います。

### 無料で利用できるクラウドプロバイダーにデプロイしよう
公式ではHeroku、Now、Glitchにデプロイする方法が紹介されています。

[Deployment | Probot](https://probot.github.io/docs/deployment/)

今回はブラウザ操作のみでデプロイできるGlicthを使って、デプロイを行います。事前に作成したアプリケーションコードをGitHubリポジトリにPushしておいてください。

さきほど作成したアプリケーションのGitHubリポジトリ
[kentaro-m/pr-add-label](https://github.com/kentaro-m/pr-add-label)

以下のリンクをクリックすると、Glitchで新しいプロジェクトが作成されます。

[Glitchで新しいプロジェクトを作成する](https://glitch.com/edit/#!/new-project)

次にGitHubからコードをインポートします。左上のプロジェクト名をクリックし、Advanced Options > Import from GitHubを選択し、インポートしたいGitHubリポジトリを入力して、OKをクリックします。

<img width="1278" alt="Screen_Shot_2018-11-30_at_0_32_18.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/60eec88e-30ff-11b4-5d6e-70dff362c4f5.png">

少し待つと、GitHubからコードが読み込まれます。

<img width="1277" alt="Screen Shot 2018-11-30 at 0.38.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/149a3085-9ba7-2e5c-6553-0665f10e3a6d.png">

次にアプリケーション動作に必要な環境情報を`.env`ファイルに追加します。

```
APP_ID=<ローカル開発環境の.envファイルの値をコピー>
WEBHOOK_SECRET=<ローカル開発環境の.envファイルの値をコピー>
PRIVATE_KEY=<ローカル開発環境の.envファイルの値をコピー>
NODE_ENV=production
```

設定が完了したら、動作確認を行います。上部のShowボタンを押すと、別タブでアプリケーションの詳細ページが表示されます。

<img width="1278" alt="Screen_Shot_2018-11-30_at_1_29_17.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/f800079c-59c1-5ecf-7a57-f9e35b5b8307.png">

アドレスバーに表示されたURLの`/probot`を除いた値がWebhookイベントを受信するエンドポイントになります。

<img width="1277" alt="Screen_Shot_2018-11-30_at_1_34_36.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/a85bb4ff-8a89-baec-43b4-656131d77b82.png">

GitHubのDeveloper Settings > GitHub Apps > 作成したGitHub Appの設定画面と進み、Webhook URLの値を先ほど取得したURLで置き換えます。また、Webhook secretの値もローカル開発環境の.envファイルの値をコピーして貼り付けます。Save changesをクリックして、設定完了です。

<img width="1278" alt="Screen_Shot_2018-11-30_at_1_45_43.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/de2c1c70-2ed6-bae3-42d6-97b6010a9693.png">

最後にアプリケーションをインストールしたリポジトリでPull Requestを作成して動作確認を行います。ラベルが追加されていることが確認できました。

<img width="1278" alt="Screen Shot 2018-11-30 at 2.36.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/6e7a6150-3b8a-6db0-cdfc-c55fcecff206.png">

GlitchではStatusボタンを押すと、ログの確認を行うことができます。先ほど紹介したWebhookイベントの再送機能と組み合わせると、デバッグが容易になると思います。

<img width="1276" alt="Screen_Shot_2018-11-30_at_2_37_12.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/17291164-12bd-f067-724b-b9748b31e668.png">

### 開発したアプリケーションを他のユーザーにも使ってもらおう
アプリケーションを他のユーザーに使ってもらうためには公開する必要があります。

GitHubのDeveloper Settings > GitHub Apps > 作成したGitHub Appの設定画面 > Advanced SettingsのMake publicボタンを押すと、公開されます。

<img width="1018" alt="Screen_Shot_2018-11-30_at_2_53_49.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/caf44d06-411b-16ed-0577-667847f62162.png">

アプリケーションごとに発行されるURL(`https://github.com/apps/<アプリケーション名>`)を他のユーザに共有してください。

<img width="1278" alt="Screen Shot 2018-11-30 at 2.51.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/9e158a60-5dd4-b4ea-c94c-8f603154abf9.png">


### 公式サイトに掲載してもらおう
公式サイトにはFeatured Appsというページがあり、数多くのGitHub Appsが紹介されています。

以下のリポジトリにPull Requestを出すことで、自分が開発したGitHub Appsを掲載してもらうことが可能です。多くの開発者が利用できる便利なGitHub Appsができたら、Pull Requestを送ってみてください。

[probot/probot.github.io: This is the home of probot documentation, apps, how-to guides and more.](https://github.com/probot/probot.github.io)

<img width="1278" alt="Screen Shot 2018-11-30 at 2.57.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/89734717-5715-9cb5-520a-21b278c462e8.png">

実は私自身もGitHub Appsを作って公開しています。Pull Requestが作成されたタイミングで任意のユーザーをレビュアーとして追加する「[Auto Assign](https://probot.github.io/apps/auto-assign/)」というアプリケーションを公開しています。もし、興味があればお試しください。

[kentaro-m/auto-assign: 🤖 A Probot app that adds reviewers to pull requests when pull requests are opened.](https://github.com/kentaro-m/auto-assign)

<img width="1279" alt="Screen Shot 2018-12-01 at 11.18.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/5d8eca97-a45a-ad42-031a-b09dab7df273.png">

## GitHub Appsを運用する
GitHub Appsをしばらく運用してみての知見を共有したいと思います。

### アクセス権限の設定変更には注意しよう
アプリケーションの公開後のアクセス権限の変更はユーザーの許可が必要です。インストールしているユーザーにメールが送信されて、専用ページから権限変更を許可してもらう流れとなります。

<img width="943" alt="Screen Shot 2018-12-01 at 11.27.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/d02a0413-a225-e789-8e3f-509b978061c0.png">

各ユーザーが権限変更許可をしたかについては知る方法がないので、権限の変更前後で異なる権限を持ったアプリケーションが存在することになり、動作不良の原因調査が難しくなります。

パブリックリポジトリとプライベートリポジトリで十分な動作確認を行った上でアプリケーションを公開することをおすすめします。

### Sentryでエラーをトラッキングしよう
アプリケーションを運用すると、様々なエラーが発生します。エラーを早期発見して、原因調査及び修正を行うために[Sentry](https://github.com/marketplace/sentry)というエラートラッキングツールを使用しています。

1ヶ月あたり1万件までエラーを無料で収集することができます。Probot公式のドキュメントでも紹介されているので、本格的に運用する際は導入するのが良いと思います。

[Error tracking | Probot](https://probot.github.io/docs/deployment/#error-tracking)

<img width="1279" alt="Screen Shot 2018-12-01 at 11.49.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/ac5073b5-a3d3-4940-23ce-a0ca6d5c3202.png">


### 外形監視サービスでサーバーのスリープを防止しよう
アプリケーションをホスティングしているクラウドプロバイダーは一定期間のアクセスがないと、サーバーがスリーブします。これを防ぐために外形監視サービスを利用して、定期的にアクセスしています。

[Website Monitoring & Downtime Updates | StatusCake](https://www.statuscake.com/)

<img width="1279" alt="Screen_Shot_2018-12-01_at_11_39_45.png" src="https://qiita-image-store.s3.amazonaws.com/0/38478/8cba8d39-183c-a56b-d078-79dda3f52543.png">


## 今後試したいこと

### GitHub Actionsとの連携を試したい
先日のGitHub UniverseでGitHub Actionsというサービスが発表されました。これはアプリケーションのビルドやデプロイ、テストなど開発者のワークフロー実行をGitHub上で自動化できるものです。

現在はベータ版提供となっており、一部ユーザのみに公開されています（私もベータ版テスターに申し込んで、使える日を首を長くして待ってます）。GitHub Actionsで定義するワークフロー上でProbotの機能を利用できるそうなので試したいです。

[GitHub Actions： みなさんが開発し、GitHubで実行 | The GitHub Blog](https://blog.github.com/jp/2018-10-24-action-demos/)

### GitHub Enterpriseで動作を確認したい
`.env`ファイルに環境変数を追加することで、GitHub Enterprise上でも動作できます。今回は時間の関係でここまでできませんでしたが、社内でもGitHub Appsを活用するために検証していきたいです。

```:.env
GHE_HOST=fake.github-enterprise.com
```

## さいごに
Probotで実際にGitHub Appsを作って公開するところまで試しました。ドキュメントや開発環境が充実しているため、非常に簡単にアプリケーションを作ることができました。

冒頭でも紹介した通り、他の開発者が作ったGitHub Appsを利用することもできるので、どんどん自動化して幸せになりましょう。

明日は @i35_267 さんの記事になります。引き続き[DMM.com Advent Calendar 2018](https://qiita.com/advent-calendar/2018/dmm)をお楽しみください。