Issue/PRに起こったイベントを取得できるGithub Timeline API

概要

最近GithubのIssue Trackerを自作してみようかなーと思っており、Issue/PullRequestで発生したイベントを取得できる Timeline API について調べたのでまとめておきます。

Timeline APIとは

GithubのIssue/PullRequestでは、誰かがアサインされたり、コメントがついたり、レビューされたりといったイベントが発生します。このイベントの一覧を取得できるのが Timeline API GET /repos/{owner}/{repo}/issues/{issue_number}/timeline です。Issue/PRの状況を取得してなんらかの通知をするようなシステムを独自で作るときに便利だと思います。完全に正確ではありませんが、GithubのUIで見られる以下の一連の表示がそのままAPIで取得できると思っておけばOKです。

f:id:muijp:20201208195549p:plain

ちなみに、ほぼ同じような機能を持っている Events API というAPIもあって使い分けが謎ですが、調べた限り以下の差分がありました。

  • Events API/Timeline API それぞれどちらかでしか取得できないイベントの種類/情報がある
  • Events APIはIssue/PRに関するイベントに加えてRepositoryに関するイベントを別途取得できるが、Timeline APIはIssue/PRについてしか取得できない

この記事ではEvents APIのことは気にせずTimeline APIについてのみ書きます。

APIの叩き方

Timeline APIを単純に叩くと以下のレスポンスが返ってきます。

{
    "message": "If you would like to help us test the Timeline API during its preview period, you must specify a custom media type in the 'Accept' header. Please see the docs for full details.",
    "documentation_url": "https://docs.github.com/rest/reference/issues#list-timeline-events-for-an-issue"
}

このAPIはまだpreview stateなので、専用のヘッダをつける必要があるとのこと。 ドキュメント にしたがって以下のヘッダをつけて送ることでレスポンスを得ることができます。

Accept: application/vnd.github.mockingbird-preview+json

また、プライベートレポジトリのIssueのTimelineを取得するにはAccess Tokenが必要です。 GithubのSettings からrepoの権限のついたTokenを発行して、以下のヘッダを付与することで、自分が権限を持ったプライベートレポジトリのTimelineもAPIから取得できるようになります。

Authorization: token <発行したToken>

レスポンス

Timeline APIを叩くとだいたいどういうレスポンスが返ってくるのか把握するために、 適当なrails/railsのPullRequest のTimelineを取得してみます。PR番号が40114なので、

GET https://api.github.com/repos/rails/rails/issues/40114/timeline

を叩くと、以下のようなレスポンスが返ってきます。あくまでだいたいどういうレスポンスが返ってくるのかを見るためにここに示しているだけなので、大したことがなさそうなフィールドは全部省略しています。実際のフルのレスポンスをみたい場合は実際にAPIを叩いてみてください。rails/railsはパブリックレポジトリなのでTokenは不要です。

[
    {
        "sha": "e0637a5a503fd624b5346c5c51adb447a23d8035",
        "node_id": "MDY6Q29tbWl0ODUxNDplMDYzN2E1YTUwM2ZkNjI0YjUzNDZjNWM1MWFkYjQ0N2EyM2Q4MDM1",
        "url": "https://api.github.com/repos/rails/rails/git/commits/e0637a5a503fd624b5346c5c51adb447a23d8035",
        "html_url": "https://github.com/rails/rails/commit/e0637a5a503fd624b5346c5c51adb447a23d8035",
        "author": {
            "name": "Adrianna Chang",
            "email": "adrianna.chang@shopify.com",
            "date": "2020-08-26T17:30:29Z"
        },
        "message": "Add attr_writer for credentials to Rails::Application",
        "event": "committed",
        ...
    },
    {
        "id": 475768545,
        "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3NDc1NzY4NTQ1",
        "user": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "body": "",
        "state": "commented",
        "html_url": "https://github.com/rails/rails/pull/40114#pullrequestreview-475768545",
        "event": "reviewed"
        ...
    },
    {
        "sha": "55a668db2937a5fe7b1c8b2f5e1913e54a16e427",
        "node_id": "MDY6Q29tbWl0ODUxNDo1NWE2NjhkYjI5MzdhNWZlN2IxYzhiMmY1ZTE5MTNlNTRhMTZlNDI3",
        "url": "https://api.github.com/repos/rails/rails/git/commits/55a668db2937a5fe7b1c8b2f5e1913e54a16e427",
        "html_url": "https://github.com/rails/rails/commit/55a668db2937a5fe7b1c8b2f5e1913e54a16e427",
        "author": {
            "name": "Adrianna Chang",
            "email": "adrianna.chang@shopify.com",
            "date": "2020-08-26T17:30:40Z"
        },
        "message": "Remove references to secrets.yml from documentation",
        "event": "committed",
        ...
    },
    {
        "id": 3696199379,
        "node_id": "MDIzOkhlYWRSZWZGb3JjZVB1c2hlZEV2ZW50MzY5NjE5OTM3OQ==",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696199379",
        "actor": {
            "login": "adrianna-chang-shopify",
            "id": 22918438,
            ...
        },
        "event": "head_ref_force_pushed",
        "created_at": "2020-08-26T18:59:38Z",
        ...
    },
    {
        "id": 3696281850,
        "node_id": "MDE1OlJlZmVyZW5jZWRFdmVudDM2OTYyODE4NTA=",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696281850",
        "actor": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "event": "referenced",
        "created_at": "2020-08-26T19:22:34Z",
        ...
    },
    {
        "id": 3696281853,
        "node_id": "MDExOk1lcmdlZEV2ZW50MzY5NjI4MTg1Mw==",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696281853",
        "actor": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "event": "merged",
        "created_at": "2020-08-26T19:22:34Z",
        ...
    },
    {
        "id": 3696281858,
        "node_id": "MDExOkNsb3NlZEV2ZW50MzY5NjI4MTg1OA==",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696281858",
        "actor": {
            "login": "rafaelfranca",
            "id": 47848,
            ...
        },
        "event": "closed",
        "created_at": "2020-08-26T19:22:34Z",
        ...
    },
    {
        "id": 3696425655,
        "node_id": "MDE5OkhlYWRSZWZEZWxldGVkRXZlbnQzNjk2NDI1NjU1",
        "url": "https://api.github.com/repos/rails/rails/issues/events/3696425655",
        "actor": {
            "login": "adrianna-chang-shopify",
            "id": 22918438,
            "node_id": "MDQ6VXNlcjIyOTE4NDM4",
            ...
        },
        "event": "head_ref_deleted",
        "created_at": "2020-08-26T20:07:17Z",
        ...
    }
]

イベントのリストが返ってきていますね。リスト要素のeventフィールドが、そのオブジェクトが表すイベントの種別です。今回の例だと、イベント種別は、上からcommittedreviewedcommittedhead_ref_force_pushedreferencedmergedclosedhead_ref_deletedですね。それぞれのイベント種別についてはこの記事の次項でまとめますが、 公式のドキュメント によくまとまってます。

event以外のフィールドを見ると、該当のイベントの詳細がわかります。例えば、committedイベントだと、shaからコミットハッシュが、messageからコミットメッセージがわかりますね。

イベント一覧

どのようなイベント種別が存在するか、それぞれのイベント種別にどのようなフィールドが存在するかを簡単にまとめておきます。自分から見て重要そうなイベントの重要そうなフィールドについてのみ書くので、この記事ではだいたいこんな情報が取れるんだなということを把握するのにとどめて、すべてのイベント種別についてちゃんと知りたい場合は 公式ドキュメント を参照してください。ただし、自分が見つけた範囲でもforce pushを表すhead_ref_force_pushedというイベント種別がドキュメントになかったり、イベント種別によって実際のAPIレスポンスとフィールドのキーが異なったりしたのでもしかしたら公式ドキュメントが実際の仕様に追従できていない部分があるかもしれません。

共通

ほとんどのイベント種別に共通のフィールド。

  • event: イベント種別
  • node_id: global node ididというフィールドも存在するが、イベント種別によってはidがないこともあるのでイベントのユニークなIDがほしいときはnode_idを使うのが良さそう(たぶん)
  • created_at: イベントの発生日時

assigned

Issue/PRがアサインされたこと。

  • actor: アサインした人
    • login: Githubのユーザネーム
    • id: GithubのユーザID
    • avatar_url: アイコンのURL
  • assignee: アサインされた人
    • login
    • id
    • node_id
    • avatar_url

committed

コミットのこと。

commented

Issue/PRにつくコメントのこと。

  • url
  • html_url
  • actor: コメントした人
    • login
    • id
    • avatar_url
  • body: コメントの本文

mentioned

メンションのこと。

  • actor: メンションされた人
    • login
    • id
    • avatar_url

メンションされた人の情報しかなく、メンションした人やメンションがあったコメントに関してはこのイベントでは取得できないみたいです。

review_requested

レビューリクエストのこと。

  • review_requester: レビューをリクエストした人
    • login
    • id
    • avatar_url
  • requested_reviewer: レビューをリクエストされた人
    • login
    • id
    • avatar_url

reviewed

レビューのこと。

  • user: レビュアー
    • login
    • id
    • avatar_url
  • body: レビュー時につけたコメント
  • state: レビューのstate。commented changes_requested approved のいずれか
  • html_url
  • author_association: レビュアーレポジトリ権限。 OWNER COLLABORATOR など

merged

PRのマージのこと。

  • actor: マージした人
    • login
    • id
    • avatar_url
  • commit_id: マージコミットのコミットハッシュ

closed

Issue/PRのクローズのこと。

  • actor: クローズした人
    • login
    • id
    • avatar_url

結論

Timeline APIはIssue/PRの流れがわかってうれしい。Issue/PRを定期的に監視してなにかあったらいい感じで通知するみたいなツールを自分で作りたいときに使えそうです。