章 ▾ 第2版

6.5 GitHub - GitHubのスクリプティング

GitHubのスクリプティング

これまでGitHubの主要な機能とワークフローをすべて見てきましたが、大規模なグループやプロジェクトでは、独自のカスタマイズや外部サービスとの統合が必要になる場合があります。

幸いなことに、GitHubは多くの点で非常にハックしやすいです。このセクションでは、GitHubのフックシステムとAPIを使用して、GitHubを思い通りに機能させる方法について説明します。

サービスとフック

GitHubリポジトリ管理の「フックとサービス」セクションは、GitHubを外部システムと連携させる最も簡単な方法です。

サービス

まず、サービスを見ていきましょう。「フックとサービス」の統合は、以前に共同作業者の追加やプロジェクトのデフォルトブランチの変更について説明したリポジトリの「設定」セクションで見つけることができます。「Webhooks and Services」タブの下に、サービスとフックの設定セクションのようなものが表示されます。

Services and Hooks configuration section
図129. サービスとフックの設定セクション

選択できるサービスは何十もあり、そのほとんどが他の商用システムやオープンソースシステムとの統合です。それらのほとんどは、継続的インテグレーションサービス、バグ・課題トラッカー、チャットルームシステム、ドキュメントシステム向けです。ここでは、非常にシンプルな「メールフック」の設定について説明します。「Add Service」ドロップダウンから「email」を選択すると、メールサービス設定のような設定画面が表示されます。

Email service configuration
図130. メールサービス設定

この場合、「Add service」ボタンをクリックすると、指定したメールアドレスに、誰かがリポジトリにプッシュするたびにメールが送信されます。サービスは様々な種類のイベントをリッスンできますが、ほとんどはプッシュイベントのみをリッスンし、そのデータに対して何かを行います。

GitHubと統合したいシステムを使用している場合は、既存のサービス統合が利用可能かどうかをここで確認してください。たとえば、Jenkinsを使用してコードベースのテストを実行している場合、Jenkinsの組み込みサービス統合を有効にして、誰かがリポジトリにプッシュするたびにテスト実行を開始できます。

フック

より具体的なものが必要な場合や、このリストに含まれていないサービスやサイトと統合したい場合は、より汎用的なフックシステムを使用できます。GitHubのリポジトリフックは非常にシンプルです。URLを指定すると、GitHubはあなたが望むあらゆるイベントでそのURLにHTTPペイロードを投稿します。

一般的に、これは、小さなWebサービスを設定してGitHubフックのペイロードをリッスンし、データを受信したときにそのデータで何かを行うという仕組みです。

フックを有効にするには、サービスとフックの設定セクションにある「Add webhook」ボタンをクリックします。これにより、Webフック設定のようなページに移動します。

Web hook configuration
図131. Webフック設定

Webフックの設定は非常にシンプルです。ほとんどの場合、URLと秘密鍵を入力して「Add webhook」をクリックするだけです。GitHubにペイロードを送信させたいイベントについてはいくつかのオプションがあります。デフォルトでは、誰かがリポジトリの任意のブランチに新しいコードをプッシュしたときに発生するpushイベントのペイロードのみを受け取る設定になっています。

Webフックを処理するために設定できるWebサービスの簡単な例を見てみましょう。ここではRubyのWebフレームワークであるSinatraを使用します。これは非常に簡潔で、何をしているのかを簡単に理解できるはずです。

特定の人がプロジェクトの特定のブランチに特定のファイルを変更してプッシュした場合にメールを受け取りたいとしましょう。これは、次のようなコードで比較的簡単に行うことができます。

require 'sinatra'
require 'json'
require 'mail'

post '/payload' do
  push = JSON.parse(request.body.read) # parse the JSON

  # gather the data we're looking for
  pusher = push["pusher"]["name"]
  branch = push["ref"]

  # get a list of all the files touched
  files = push["commits"].map do |commit|
    commit['added'] + commit['modified'] + commit['removed']
  end
  files = files.flatten.uniq

  # check for our criteria
  if pusher == 'schacon' &&
     branch == 'ref/heads/special-branch' &&
     files.include?('special-file.txt')

    Mail.deliver do
      from     'tchacon@example.com'
      to       'tchacon@example.com'
      subject  'Scott Changed the File'
      body     "ALARM"
    end
  end
end

ここでは、GitHubが配信するJSONペイロードを受け取り、誰がプッシュしたか、どのブランチにプッシュしたか、そしてプッシュされたすべてのコミットでどのファイルが変更されたかを検索しています。その後、その情報を基準と照合し、一致した場合にメールを送信します。

このようなものを開発およびテストするために、フックを設定するのと同じ画面に優れた開発者コンソールがあります。GitHubがそのウェブフックに対して試行した最後の数回の配信を確認できます。各フックについて、いつ配信されたか、成功したかどうか、そしてリクエストとレスポンスの両方のボディとヘッダーを詳細に調べることができます。これにより、フックのテストとデバッグが非常に簡単になります。

Web hook debugging information
図132. Webフックのデバッグ情報

このもう一つの優れた機能は、任意のペイロードを再配信してサービスを簡単にテストできることです。

Webフックの作成方法と、リッスンできるさまざまなイベントタイプに関する詳細については、GitHub開発者ドキュメント(https://docs.github.com/en/webhooks-and-events/webhooks/about-webhooks)を参照してください。

GitHub API

サービスとフックは、リポジトリで発生するイベントに関するプッシュ通知を受け取る方法を提供しますが、これらのイベントに関する詳細情報が必要な場合はどうでしょうか?共同作業者の追加や課題へのラベル付けのようなことを自動化する必要がある場合はどうでしょうか?

ここでGitHub APIが役立ちます。GitHubには、Webサイトでできることのほとんどを自動化するためのAPIエンドポイントが多数用意されています。このセクションでは、APIへの認証と接続方法、課題へのコメント方法、APIを通じてプルリクエストのステータスを変更する方法について学びます。

基本的な使い方

最も基本的なことは、認証を必要としないエンドポイントに対するシンプルなGETリクエストです。これは、ユーザーやオープンソースプロジェクトの読み取り専用情報である可能性があります。たとえば、「schacon」という名前のユーザーについて詳しく知りたい場合は、次のように実行できます。

$ curl https://api.github.com/users/schacon
{
  "login": "schacon",
  "id": 70,
  "avatar_url": "https://avatars.githubusercontent.com/u/70",
# …
  "name": "Scott Chacon",
  "company": "GitHub",
  "following": 19,
  "created_at": "2008-01-27T17:19:28Z",
  "updated_at": "2014-06-10T02:37:23Z"
}

組織、プロジェクト、イシュー、コミットなど、GitHubで公開されているほぼすべての情報について、このようなエンドポイントが大量に存在します。APIを使って任意のMarkdownをレンダリングしたり、.gitignoreテンプレートを見つけたりすることもできます。

$ curl https://api.github.com/gitignore/templates/Java
{
  "name": "Java",
  "source": "*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear

# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
"
}

課題へのコメント

ただし、イシューやプルリクエストにコメントするなどのウェブサイト上での操作を行いたい場合や、プライベートなコンテンツを表示したり操作したりしたい場合は、認証が必要です。

認証方法はいくつかあります。ユーザー名とパスワードのみで基本認証を使用することもできますが、一般的には個人アクセストークンを使用する方が良いでしょう。これは、設定ページの「Applications」タブから生成できます。

Generate your access token from the “Applications” tab of your settings page
図133. 設定ページの「Applications」タブからアクセストークンを生成する

このトークンに設定したいスコープと説明が尋ねられます。スクリプトやアプリケーションが不要になったときにトークンを安心して削除できるよう、適切な説明を付けてください。

GitHubはトークンを一度しか表示しないので、必ずコピーしてください。これで、ユーザー名とパスワードを使用する代わりに、スクリプト内で認証にこれを使用できます。これは、実行したい操作の範囲を制限でき、トークンが取り消し可能であるため便利です。

これには、レート制限が引き上げられるという利点もあります。認証しない場合、1時間あたり60リクエストに制限されます。認証すると、1時間あたり最大5,000リクエストを行うことができます。

それでは、これを使って私たちの課題の一つにコメントをしてみましょう。特定の課題、課題 #6 にコメントを残したいとします。そのためには、先ほど生成したトークンをAuthorizationヘッダーとして含め、repos/<user>/<repo>/issues/<num>/comments へHTTP POSTリクエストを行う必要があります。

$ curl -H "Content-Type: application/json" \
       -H "Authorization: token TOKEN" \
       --data '{"body":"A new comment, :+1:"}' \
       https://api.github.com/repos/schacon/blink/issues/6/comments
{
  "id": 58322100,
  "html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100",
  ...
  "user": {
    "login": "tonychacon",
    "id": 7874698,
    "avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2",
    "type": "User",
  },
  "created_at": "2014-10-08T07:48:19Z",
  "updated_at": "2014-10-08T07:48:19Z",
  "body": "A new comment, :+1:"
}

そのイシューにアクセスすると、GitHub APIから投稿されたコメントのように、正常に投稿されたコメントを確認できます。

A comment posted from the GitHub API
図134. GitHub APIから投稿されたコメント

APIを使えば、Webサイトでできることのほとんどすべてを実行できます。例えば、マイルストーンの作成と設定、イシューやプルリクエストへの担当者の割り当て、ラベルの作成と変更、コミットデータへのアクセス、新しいコミットやブランチの作成、プルリクエストのオープン、クローズ、マージ、チームの作成と編集、プルリクエスト内のコード行へのコメント、サイト内検索など、多岐にわたります。

プルリクエストのステータスの変更

プルリクエストを扱う際に非常に役立つ最後の例を1つ見ていきましょう。各コミットには1つ以上のステータスを関連付けることができ、そのステータスを追加および照会するためのAPIがあります。

ほとんどの継続的インテグレーションおよびテストサービスは、このAPIを利用して、プッシュされたコードをテストすることでプッシュに反応し、そのコミットがすべてのテストに合格したかどうかを報告します。これを使用して、コミットメッセージが適切にフォーマットされているか、提出者がすべての貢献ガイドラインに従っているか、コミットが有効に署名されているかなど、あらゆることを確認することもできます。

コミットメッセージにSigned-off-byという文字列があるかをチェックする小さなWebサービスに接続するWebフックをリポジトリに設定したとしましょう。

require 'httparty'
require 'sinatra'
require 'json'

post '/payload' do
  push = JSON.parse(request.body.read) # parse the JSON
  repo_name = push['repository']['full_name']

  # look through each commit message
  push["commits"].each do |commit|

    # look for a Signed-off-by string
    if /Signed-off-by/.match commit['message']
      state = 'success'
      description = 'Successfully signed off!'
    else
      state = 'failure'
      description = 'No signoff found.'
    end

    # post status to GitHub
    sha = commit["id"]
    status_url = "https://api.github.com/repos/#{repo_name}/statuses/#{sha}"

    status = {
      "state"       => state,
      "description" => description,
      "target_url"  => "http://example.com/how-to-signoff",
      "context"     => "validate/signoff"
    }
    HTTParty.post(status_url,
      :body => status.to_json,
      :headers => {
        'Content-Type'  => 'application/json',
        'User-Agent'    => 'tonychacon/signoff',
        'Authorization' => "token #{ENV['TOKEN']}" }
    )
  end
end

これで理解しやすいはずです。このWebフックハンドラでは、プッシュされた各コミットを調べ、コミットメッセージ内の「Signed-off-by」文字列を探し、最後にそのステータスを/repos/<user>/<repo>/statuses/<commit_sha> APIエンドポイントへHTTP POSTで送信します。

この場合、ステート(「success」、「failure」、「error」)、何が起こったかの説明、ユーザーが詳細情報を確認できるターゲットURL、および単一のコミットに複数のステータスがある場合の「コンテキスト」を送信できます。たとえば、テストサービスがステータスを提供し、このような検証サービスもステータスを提供する可能性があります。その際、「コンテキスト」フィールドがそれらを区別する方法となります。

誰かがGitHubで新しいプルリクエストをオープンし、このフックが設定されている場合、APIを介したコミットステータスのような表示を見ることができます。

Commit status via the API
図135. APIを介したコミットステータス

メッセージに「Signed-off-by」文字列を含むコミットの隣に小さな緑色のチェックマークが表示され、作者が署名を忘れたコミットには赤いバツ印が表示されるようになりました。また、プルリクエストがブランチの最後のコミットのステータスを受け取り、失敗している場合に警告が表示されることも確認できます。これは、このAPIをテスト結果に使用している場合に非常に便利で、最後のコミットがテストに失敗しているものを誤ってマージしてしまうことを防げます。

Octokit

これらの例ではほとんどすべてcurlやシンプルなHTTPリクエストで行ってきましたが、このAPIをより慣用的な方法で利用できるようにするいくつかのオープンソースライブラリが存在します。この記事執筆時点では、Go、Objective-C、Ruby、.NETがサポートされている言語に含まれます。これらについては、HTTPの多くの部分を処理してくれるため、詳細についてはhttps://github.com/octokitを確認してください。

これらのツールが、GitHubを特定のワークフローに合わせてカスタマイズおよび変更するのに役立つことを願っています。API全体の完全なドキュメントと一般的なタスクのガイドについては、https://docs.github.com/を参照してください。

scroll-to-top