GeekFactory

int128.hatenablog.com

HubotでWebHookのHTTPリクエストを受け取る

Hubotはチャットの投稿を受け取って処理を実行するのに便利ですが、外部からHTTPリクエストを受け取って処理を行うこともできます。

HubotにはExpressが組み込まれており、デフォルトでは8080ポートをlistenします。HubotでHTTPリクエストを処理するには、以下のように robot.router でルーティング定義を書きます。

module.exports = (robot) ->

  # /webhookにPOSTされたらログを表示する
  robot.router.post '/webhook', (req, res) ->
    console.info req.body
    res.end()

詳しくはHubotのドキュメントを参照してください。

hubot/scripting.md at master · github/hubot · GitHub

この仕組みを利用すると、外部からWebhookを受け取ってSlackにメッセージを流すといったことが簡単にできます。

ここではGitLabのWebhookを受け取ってみましょう。もちろんGitHubでも同じことができます。

Webhookの設定

GitLabでMerge Requestが作成された場合にWebhookを投げるには、リポジトリの設定画面でWebhookを有効にします。

https://gitlab.com/gitlab-org/gitlab-ce/raw/master/doc/web_hooks/ssl.png

doc/web_hooks/web_hooks.md | master | GitLab.org / GitLab Community Edition | GitLab

Merge Requestが作成されたり更新されたりすると、指定したURLに以下のJSONがPOSTされます*1

{
  "object_kind": "merge_request",
  "user": {
    "name": "Administrator",
    "username": "root",
    "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
  },
  "object_attributes": {
    "id": 99,
    "target_branch": "master",
    "source_branch": "ms-viewport",
    "source_project_id": 14,
    "author_id": 51,
    "assignee_id": 6,
    "title": "MS-Viewport",
    "created_at": "2013-12-03T17:23:34Z",
    "updated_at": "2013-12-03T17:23:34Z",
    "st_commits": null,
    "st_diffs": null,
    "milestone_id": null,
    "state": "opened",
    "merge_status": "unchecked",
    "target_project_id": 14,
    "iid": 1,
    "description": "",
    "source": {
      "name": "awesome_project",
      "ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
      "http_url": "http://example.com/awesome_space/awesome_project.git",
      "web_url": "http://example.com/awesome_space/awesome_project",
      "visibility_level": 20,
      "namespace": "awesome_space"
    },
    "target": {
      "name": "awesome_project",
      "ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
      "http_url": "http://example.com/awesome_space/awesome_project.git",
      "web_url": "http://example.com/awesome_space/awesome_project",
      "visibility_level": 20,
      "namespace": "awesome_space"
    },
    "last_commit": {
      "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
      "message": "fixed readme",
      "timestamp": "2012-01-03T23:36:29+02:00",
      "url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
      "author": {
        "name": "GitLab dev user",
        "email": "gitlabdev@dv6700.(none)"
      }
    },
    "work_in_progress": false,
    "url": "http://example.com/diaspora/merge_requests/1",
    "action": "open"
  }
}

Hubotスクリプトの作成

ここではMerge Requestの作成時に通知を絞りたいので、ステータスをチェックします。また、JSONからいい感じに情報を抜き出してメッセージを作ります。

module.exports = (robot) ->

  robot.router.post '/gitlab-webhook', (req, res) ->
    if req.body.object_kind == 'merge_request'
      merge_request = req.body.object_attributes
      if merge_request.action == 'open'
        message = """#{merge_request.title}のコードレビューよろしく頼む。
#{merge_request.url}"""
        robot.send {room: 'bot'}, message
    res.end()

Hubotを実行して、GitLabでMerge Requestを作成してみましょう。「コードレビューよろしく頼む」というメッセージが通知されれば成功です。

応用編

コードレビューの担当者はチームでランダムに選出してもよいでしょう。

Merge RequestでAssigneeに指定されているた人にメンションを飛ばしたい場合は、JSONassignee_id からユーザ名を逆引きする必要があります。ユーザ情報はGitLab APIで簡単に取得できます。詳しくは以下を参照してください。GitHubならユーザ情報も一緒にもらえるんだけどなぁ。

http://doc.gitlab.com/ee/api/users.html

まとめ

Hubotはチャットをlistenするだけでなく、Webhookをlistenするのにも便利です。ここではGitLabのMerge Requestをチャットに通知する方法を紹介しました。さくっとスクリプトを書いて開発環境を快適にしましょう。

*1:ここで示したJSONはGitLab 8.xの場合で、古いバージョンでは項目がもっと少ないです。リポジトリのURLすら取得できないので、メッセージにURLを含めたい場合はWebhookにパラメータで渡すなりハードコードするなりなどの対応が必要です。