Go
GitHub
AWS
lambda

PullRequestがマージされたら自動でブランチを削除するGitHubApp(delete-branch-bot)を作った

ちゃんとブランチ消してますか?

あなたはGitHubでPRをマージした後、きちんとブランチを削除していますか?
GitHubにはマージ後に「Delete branch」ボタンを表示してくれるんですけどね
スクリーンショット 2018-09-27 21.31.51.png

なーんか押すの忘れちゃうんですよね
ついにはよく共同で開発している@chaspyに「運用変える?」と言われてしまう始末…ごめんなさい:head_bandage:
スクリーンショット 2018-09-27 21.30.48.png

「自動で消す方法なんてすぐ見つかるでしょ」と検索してみるもなかなか見つからず。
20,30分探しても有効な情報が見つけられなかったので、「作ろう」ということになり
@chaspy と一緒にdelete-branch-botを作りました。(もし他に方法があれば教えて下さい。)

GitLabはMergeRequestを作成する際にオプションでマージ後ブランチを削除するチェックボックスがあるのですが…

What's delete-branch-bot?

GitHubのRepository単位でインストールが行えるGitHubAppです。
PullRequestがmergeもしくはcloseされると、自動で対象のブランチを削除します。(なんて分かりやすいAPP名なんだ)

How to install?

GitHubの検索窓で、delete-branch-botと検索するか、Appのページへどうぞ。
delete-branch-bot

環境

簡単にご紹介します。

構成

AWSのAPI GatewayLambdaを使っています。
GitHubAppからGitHubのPullRequestEventがPOSTされるので、API GatewayでLambdaにゲートウェイしてあげて、Lambda上でGitHubRepositoryに対してブランチを削除するREST APIを叩くだけです。
delete-branch.png

言GO

Golangです。
最初はGithubが公式に出しているライブラリを使おうということで、Rubyが候補に上がったのですがLambdaがRubyには対応してなかったため、最近利用している&ライブラリも豊富なGolangを選択しました。
Googleがサードパーティとして公開しているgo-githubを使えば安全&簡単にGithubAPIが使用できます。
ドキュメントはgo-github[doc]に書いてあります。(Goのドキュメントは他のDocに比べて見づらい…)

GitHubAppの認証

GitHubAppの認証にはGitHubAppに紐づくPrivateKeyをJSON Web Tokens(JWT)で署名して認証する必要があります。
ここはライブラリに力を借ります。
ghinstallationはGitHubAppの情報からJWT Tokenを生成してHTTPリクエストにTOKENを付加してくれる非常に素晴らしいライブラリで、go-githubのドキュメントにもGitHubAppの認証をしたい場合はこれ使えと書いてます。

ハマったところ

ghinstallationの使用例ですが、integration ID(例で1となっている引数)とinstallation ID(例で99となっている引数)に何を指定しているのか、ライブラリの中身を読んでもピンと来なかったところです。もちろん指定を間違っていると正しい署名が作成されないので、認証エラーとなりRepositoryに対してリクエストを送ることができません。
ちなみに、integration IDにはGitHubAppのAPPIDを指定します。installation IDはインストールされたRepositoryに紐づくインストールIDです。installation IDは動的に変動しますが、GitHubAppのWebhook先にPOSTされるJSONに記載されているので、Parseしてあげてると良いでしょう。

func main() {
    // Shared transport to reuse TCP connections.
    tr := http.DefaultTransport

    // Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99.
    itr, err := ghinstallation.NewKeyFromFile(tr, 1, 99, "2016-10-19.private-key.pem")
    if err != nil {
        log.Fatal(err)
    }

    // Use installation transport with github.com/google/go-github
    client := github.NewClient(&http.Client{Transport: itr})
}

GitHubAppの認証についての公式ページはAuthenticating with GitHub Appsを参照下さい。

最後に

これで心理的なプレッシャーから開放されて、よりよいGitHubを用いた開発ができそうです。
アイコンは近々できる予定!