Edited at

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


2019/08/17 更新

Githubが公式にサポートしたようです。

Managing the automatic deletion of branches

リリースして1年も経っていませんが、本体に必要とされる機能を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を用いた開発ができそうです。

アイコンは近々できる予定!