GitHubフローを助けてくれるボットを作った

今回はGitHubフローを助けてくれる簡単なボットを作ったので紹介します。

ボット環境

弊社ではHubotを使ってSlack用のボットを作っています。

github.com

HubotはGitHub製のボットライブラリで、coffeescriptで簡単にかけて、そのままHerokuに簡単に上げることができます。

最初はSlackに移行したときにせっかくならボットを作ろうと思って始めて、ネタ機能が多かったり、新人研修の題材に使っていましたが、現在はユーザー数をログから計算して教えてくれたり、サービスにエラーが発生したら教えてくれたりなど実用的な機能を追加しています。

弊社でのGitHubフロー

開発のフローは基本的に以下のようになっています

  • 実装方針などをissueに書く
  • 実装する
  • プルリクエストを出す
  • レビューする
  • マージする

普通のフローですが、弊社ではブランチ名にもルールを設けています。

(feature|refactor|fix)-[issue No.]-description

例をあげるとfeature-100-implement_user_registrationという感じです。

基本的にプルリクエストはひとつのissueに紐付いているため、プルリクエストがマージされるとそのブランチとissueは不要となるため消すことになっていたのですが、人間はサボる生き物なので徹底されずに、ゴミのブランチやissueが多く残ってしまっていました。

ボットを作った

そこで、プルリクエストをマージしたら関連するブランチとissueを自動的に削除するボットを作りました!

apiUrl = "https://api.github.com/repos/xxxx"
querystring = require('querystring')

module.exports = (robot) ->
  robot.router.post("/github/pr/cleaner", (req, response) ->
    data = req.body
    action = data.action
    pullRequest = data.pull_request
    if action == 'closed' && pullRequest.merged
      repo = pullRequest.head.repo
      name = repo.name
      branchName = pullRequest.head.ref

      # delete branch
      deleteBranchRequest = robot.http("#{apiUrl}/#{name}/git/refs/heads/#{branchName}")
        .header('Authorization', "token #{process.env.GITHUB_TOKEN}")
        .del()
      deleteBranchRequest((err, res, body) ->
        if err
          robot.messageRoom(channel_name, "Deleting #{branchName} was failed")
      )

      # close issue
      issueNumber = branchName.match(/-(\d+)-/)
      if issueNumber[1] != undefined
        number = issueNumber[1]
        console.log("request #{apiUrl}/#{name}/issues/#{number}")
        closingIssueRequest = robot.http("#{apiUrl}/#{name}/issues/#{number}")
          .header('Authorization', "token #{process.env.GITHUB_TOKEN}")
          .patch(JSON.stringify(state: 'closed'))
        closingIssueRequest((err, res, body) ->
          if err
            robot.messageRoom(channel_name, "Closing \##{number} was failed")
        )

      url = require('url')
      query = querystring.parse(url.parse(req.url).query)
      channel_name = query.channel
      user = pullRequest.user.login
      robot.messageRoom(channel_name, "Good job, #{user}!\nLeave the rest to me!")
    response.end('')
  )

これをHerokuに上げた状態でGitHubのWebhookにhttps://xxx/github/pr/cleaner?channel=yyyというのを設定してプルリクエストのフックをとれるようにすればもう動きます。

これにより100%ブランチとissueが整理されるようになりました。

まとめ

このような感じで弊社ではボットを使って開発や運用の効率を上げる取り組みを行なっています。ボットがフローの一部となって人間の負担を減らしてくれるととても助かりますし、ボットにもっとすごいことをやらせてサービスを加速させることもできます。