iPhoneで Grafanaの グラフを 参照できる アプリ Grafanizer 作ってます。 詳しくは こちらへ

この記事はGitLab Advent Calendar 2016の17日目の記事です。

はじめに

仕事でGitLabのIssuesへ担当者を自動アサインする仕組みを作成したところに、GitLabのアドベントカレンダーなるものが目に飛び込んできましたのでちょっと祭りに混ぜてもらおうと思います。

さて、「Webhookを使ってGitLabのIssues担当者を自動アサイン」と題名になっていますが、実際はGitLabにかぎらずAPIがあればどのサービスでも使えますしIssues担当者以外でも同じくAPIがあればなんでも応用可能なものになってます。

簡単にまとめると、「Webhookの仕組みを利用して任意の処理をさせようぜー」「ChatOps? Hubot? そんなに複雑で大げさな仕組みは要らないんだよー」という内容です。なのでそういう仕組がすでにある方はそちらで組んだほうが良いと思います。

webhook_trigger

概要

メインになるのはwebhookなるGolang製のツールなのですが、Webhookを扱うwebhookというツールで名前が同じでややこしい(笑)。なので、ここでは Webhook な大文字始まりのときは仕組みを表し、 webhook と小文字始まりの時はツールを表すことにします。

流れ的には、

  1. GitLabのIssuesが変更
  2. webhookにWebhookが通知
  3. webhookが任意のプログラムを起動
  4. 任意のプログラムがGitLabのAPIへアクセス

という感じで、図にすると以下の感じになりますが、任意のプログラムは既存のプログラムでもいいですし CurlWget でも代用が効くという内容です。

        Webhook            Shell                    Curl
GitLab ---------> webhook -------> 任意のプログラム -------> GitLabAPI

GitLabの準備

設定

GitLabの設定としては、任意のプロジェクトのWebhook設定ページを開き、URLを指定して Issues events にチェックをいれるだけです。

URLについては http://example.com:9000/hooks/randomAssign な感じで、ホスト名は任意(もしGitLabと同じサーバーでしたらlocalhostでも可)にして randomAssign の部分は分かりやすくアクション名にしておきましょう。

テスト

WebhookのテストにはRequestBinJson Parser Onlineが便利です。

GitLabのWebhookの設定にRequestBinのURLも追加しておくとWebhookでやり取りしてるJSONが拾え、拾ったJSONはJson Parser Onlineで整形すると分かりやすいです。

webhookの準備

インストール

webhookこちらからダウンロードできます。バイナリも準備されており、各プラットフォーム用をダウンロードして展開するだけで使えます。Golang製なのでここらへんは楽でいいですね(これがRubyとかNodeJSだったら素直にHubot入れてたと思う)。

設定

設定についてはこちらにいくつかサンプルがあります。

簡単な内容でしたらBitbucket用のサンプルを見るだけで十分だと思います。実行させるコマンドとその引数が直感的に分かりやすいです。

ここでは以下のようなファイルを作成しました。IDの部分は先程GitLabで指定したアクションにします。

[
  {
    "id": "randomAssign",
    "execute-command": "/gogo/bar/randomAssign.sh",
    "command-working-directory": "/gogo/bar",
    "pass-arguments-to-command": [
      {
        "source": "payload",
        "name": "object_attributes.project_id"
      },
      {
        "source": "payload",
        "name": "object_attributes.id"
      }
    ]
  }
]

今回はオンプレなので AccessToken は指定していませんが、クラウドで利用する場合はGitLabの設定と合わせてしっかりと設定しておきましょう。

担当者アサインの仕組み

今回は下記を引数にして randomAssign.sh を起動するようになっており、そのスクリプト内で実際の処理を行っています。

  1. Issues変更内容
  2. プロジェクトID
  3. IssuesID

スクリプトについてはなんでも良かったのですが、依存関係を少なくお手軽にということでBashを使いました。 このくらいのものであれば jq コマンドを使えばBashでも簡単に書くことができます。

jqについて

jq 、以前から見かけてはいたのですがちょっとしたスクリプトの中でJSONを使うというケースが無かったので、実は使うのは今回が初めてです。

ですが、調べてみるとこれがなんとも高機能。今回はこのページを参考にさせてもらいました。とても分かりやすい! 一気に jq のファンになっちゃいました(笑)

高機能すぎて覚えきれない感じですが、Shellから使うときは

  • 値にアクセス: http://example.com/gogo/bar.json | jq '.id'
  • 要素数を計算: http://example.com/gogo/bar.json | jq '. | length'
  • オブジェクトの中身を取得: http://example.com/gogo/bar.json | jq '. | map(select(.id == "1")) | .[0].id'

あたりを抑えておけばなんとかなりそうですね。SQLっぽいこともできそうなので、気になる方は先程のページを参考にどうぞ。

コード

最終的には以下のコードになりました。

途中にある配列へはアサインしたいメンバーのIDを列挙してください。jq を使えば

curl -H "PRIVATE-TOKEN: XXXX" http://example.com/api/v3/users | jq '.[].id'

で取得できますね。

#!/bin/bash

API="http://example.com/api"
TOKEN="XXXXXXXXXXX"

ASSIGNEELENGTH=`curl -s --header "PRIVATE-TOKEN: $TOKEN" $API/v3/projects/$1/issues/$2 | jq '.assignee | length'`
if [ $ASSIGNEELENGTH -ne 0 ]
then
  exit 0
fi

declare -a array=("2" "4" "5" "6")
ASSIGNEE=${array[$(($RANDOM % 4))]}
curl --request PUT --header "PRIVATE-TOKEN: $TOKEN" $API/v3/projects/$1/issues/$2?assignee_id=$ASSIGNEE

exit 0

簡単すぎて説明はいらないと思いますが、処理内容としては

  1. 指定したIssuesのAssignee情報を取得
  2. Assignee情報があればExit
  3. 配列に担当者をリストアップ
  4. 担当者配列からランダムに取得
  5. 取得した担当者をアサイン

という流れです。

応用

他に応用としては

  • ラベルの自動設定
    • 例えばマージリクエスト(GitHub的にはプルリク)が発生したタイミングで、レビュー中のラベルを貼ることができますね
  • サービス間でのWebhookのやりとり
    • IncomingなWebhookを受け付けるサービスとOutgoingするWebhookがあるサービス、どちらもWebhookが用意されているのにお互いに想定している内容が違うというケースが結構あります。ですが、この仕組みを使えば内容を調整して転送させて連携させるということも容易にできますね

とかも考えられます。

さいごに

ちなみに、以前のポストでも同じようなネタを書いているのですが、そのときはzapierを利用して対応しました。そして、ポストはしてないのですがその後GASを利用して書き換えてます。

ですが、このwebhookを利用すればもっと簡単にできそうですね。オンプレな環境ではこのまま使えるし、クラウドな環境でもGolang製ということでGCPと相性が良さそうです(GCPの場合は jq が使えないと思うので実行させるコマンドもGolangで実装する必要があると思いますが)。


Buzzword的な ChatOps を支える Hubot 、実際に使って見たこともありなかなかいい仕組みだなとは理解してる。

けど、仕組みが複雑になればなるほど一つが動かなくなると全部が機能しなくなってくるし、チャットの仕組みも必要になってくるのでメンバーの温度差がバラバラだと運用に支障が出てきたりもする。
流行りに乗ってなんでも ChatOps として導入を進めるのもいいが、これだけは譲れないという部分はやはりシンプルで分かりやすいものを構築したい。

また、Webhookであればチャット側のAPIと常時接続させておく必要がないため、今時のサーバーレスアーキテクチャとの相性も非常に良さそう。

そういったところにはこの仕組みがうまくマッチすると思う。