チャプター ▾ 第2版

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

GitHubのスクリプティング

これまでにGitHubの主要な機能とワークフローをすべてカバーしてきましたが、どんな大規模なグループやプロジェクトでも、カスタマイズしたい点や統合したい外部サービスがあるかもしれません。

幸いなことに、GitHubは多くの点で非常にハック可能です。このセクションでは、GitHubフックシステムとそのAPIを使用して、GitHubを望むように動作させる方法を説明します。

サービスとフック

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

サービス

まず、サービスを見てみましょう。フックとサービスの両方の統合は、リポジトリの「Settings」セクションにあります。ここでは以前、コラボレーターの追加やプロジェクトのデフォルトブランチの変更について説明しました。「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ペイロードを投稿します。

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

フックを有効にするには、サービスとフックの設定セクションの「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ペイロードを取得し、誰がプッシュしたか、どのブランチにプッシュしたか、プッシュされたすべてのコミットでどのファイルが変更されたかを検索しています。次に、それを基準と照合し、一致すればメールを送信します。

このようなものを開発してテストするために、フックを設定したのと同じ画面に便利な開発者コンソールがあります。そのWebフックに対して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には、ウェブサイトでできることのほとんどすべてを自動化された方法で行うための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リクエストを行うことができます。

では、これを使って課題の1つにコメントをしてみましょう。特定の課題、Issue #6にコメントを残したいとします。これを行うには、repos/<user>/<repo>/issues/<num>/commentsに、生成したトークンをAuthorizationヘッダーとして含む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を使用すると、ウェブサイトでできるほとんどすべてのことができます。マイルストーンの作成と設定、課題やプルリクエストへの人の割り当て、ラベルの作成と変更、コミットデータへのアクセス、新しいコミットとブランチの作成、プルリクエストのオープン、クローズ、マージ、チームの作成と編集、プルリクエストのコード行へのコメント、サイトの検索など、多岐にわたります。

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

プルリクエストを扱う場合に非常に役立つので、もう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」文字列を探し、最後にHTTP POSTを/repos/<user>/<repo>/statuses/<commit_sha> APIエンドポイントにステータスと共に送信します。

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

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

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

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

Octokit

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

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

scroll-to-top