Skip to main content

インテグレーターのためのベストプラクティス

信頼性を持ってGitHub Enterprise Server APIとやりとりをするアプリケーションを構築し、ユーザに最善の体験を提供してください。

GitHubプラットフォームとの統合に興味はありますか。 あなたには良い仲間がいます。 このガイドは、ユーザーに最適なエクスペリエンスを提供するアプリを構築 し、 API と確実に対話できるようにするのに役立ちます。

GitHubから配信されるペイロードの機密を確保する

GitHub から送信されるペイロードの機密を確保することは非常に重要です。 ペイロード内で個人情報 (パスワードなど) が送信されることはありませんが 、情報の 漏洩は適切ではありません。 コミッターのメールアドレスやプライベートリポジトリの名前などは、機密性が求められる場合があります。

いくつかのステップを踏むことで、GitHubから配信されるペイロードを安全に受信できます。

  1. 受信サーバーは必ずHTTPS接続にしてください。 既定では、GitHub はペイロードを配信する際に SSL 証明書を検証します。
  2. ペイロードが GitHub から配信されていることを確実に保証するため、シークレット トークンを提供します。 シークレットトークンを強制することにより、サーバーが受信するあらゆるデータが確実にGitHubから来ていることを保証できます。 理想的には、サービスの ユーザーごとに 異なるシークレット トークンを指定する必要があります。 そうすれば、1つのトークンが侵害されても、他のユーザは影響を受けません。

同期作業より非同期作業を優先する

GitHub は、Webhook ペイロードを受信後 30 秒以内に統合が応答することを求めています。 サービスの応答時間がそれ以上になると、GitHubは接続を中止し、ペイロードは失われます。

サービスの完了時間を予測することは不可能なので、「実際の作業」のすべてはバックグラウンドジョブで実行すべきです。 バックグラウンド ジョブのキューや処理を扱えるライブラリの例としては、Resque (Ruby 用)、RQ (Python 用)、RabbitMQ (Java 用) などがあります。

バックグラウンド ジョブが実行中でも、GitHub はサーバーが 30 秒以内に応答することを求めていることに注意してください。 サーバは何らかの応答を送信することにより、ペイロードの受信を確認する必要があります。 サービスがペイロードについての確認を可能な限り速やかに行うことは非常に重要です。そうすることにより、サーバがリクエストを継続するかどうか正確に報告できます。

GitHubへの応答時に適切なHTTPステータスコードを使用する

各webhookには、デプロイメントが成功したかどうかを列挙する独自の「最近のデリバリ」セクションがあります。

[Recent Deliveries] ビュー

ユーザへの通知には、適切なHTTPステータスコードを使用するべきです。 (既定でないブランチから配信されたペイロードなど) 処理されないペイロードの受信確認のため、201202 などのコードを使えます。 500 エラー コードは、致命的な障害用に確保してください。

ユーザにできるだけ多くの情報を提供する

ユーザはGitHubに返信したサーバーの応答を調べることができます。 メッセージは明確で参考になるものとしてください。

ペイロードレスポンスの表示

APIが送信するあらゆるAPIに従う

GitHubは、リダイレクトのステータスコードを提供することにより、リソースがいつ移動したかを明示します。 このリダイレクトに従ってください。 すべてのリダイレクト応答では、Location ヘッダーに、移動する新しい URI を設定します。 リダイレクトを受け取ったら、削除する可能性がある非推奨のパスをリクエストしている場合、新しいURIにしたがってコードを更新するようお勧めします。

リダイレクトに従うようにアプリを設計するときに注意する必要がある HTTP 状態コードスの一覧を用意してあります。

手動でURLをパースしない

APIレスポンスには、URLの形でデータが含まれていることがよくあります。 たとえば、リポジトリを要求するときは、リポジトリをクローンするために使用できる URL を含む clone_url というキーを送信します。

アプリケーションの安定性を保つため、このデータをパースしようとしたり、先のURLの形式を予想して作成しようとしたりしないでください。 URLを変更した場合、アプリケーションが壊れるおそれがあります。

たとえば、ページネーションされた結果を処理するときは、末尾に ?page=<number> の付いた URL を構築したいことがよくあります。 この誘惑に負けてはなりません。 ページネーションに関するガイドでは、ページネーションされた結果を安全に扱うための信頼できるヒントがいくつか提供されています。

イベントの処理前にイベントのタイプとアクションを確認する

Webhook のイベント タイプは複数あり、各イベントは複数のアクションを持つことができます。 GitHubの機能セットが増えるにつれて、新しいイベントタイプを追加したり、既存のイベントタイプに新しいアクションを追加したりすることがあります。 Webhookの処理を行う前に、イベントのタイプとアクションをアプリケーションが明示的に確認するようにしてください。 X-GitHub-Event 要求ヘッダーを使って、受信したイベントの種類を知り、適切に処理することができます。 同様に、ペイロードには最上位レベルの action キーがあり、関連オブジェクトに対して実行されたアクションを知るために利用できます。

たとえば、GitHub の Webhook を "すべてのもの を送信する" ように設定している場合、新しいイベントの種類やアクションが追加されると、アプリケーションはそれらを受信し始めます。 したがって、どのような種類の catch-all else 構文も使わないことをお勧めします。 たとえば、次のコード例をご覧ください。

# Not recommended: a catch-all else clause
def receive
  event_type = request.headers["X-GitHub-Event"]
  payload    = request.body

  case event_type
  when "repository"
    process_repository(payload)
  when "issues"
    process_issues(payload)
  else
    process_pull_requests
  end
end

このコード例では、repository または issues イベントを受信した場合、process_repositoryprocess_issues メソッドが正しく呼び出されます。 しかし、他のすべてのイベントの種類では、process_pull_requests が呼び出されます。 新しいイベントの種類が追加されると、誤った動作になり、新しいイベントの種類は pull_request イベントと同じ方法で処理されます。

代わりに、イベントのタイプを明示的に確認し、それに応じて処理するようお勧めします。 次のコード例では、pull_request イベントを明示的にチェックし、else 句では新しいイベントの種類を受信したことを単に記録します。

# Recommended: explicitly check each event type
def receive
  event_type = request.headers["X-GitHub-Event"]
  payload    = JSON.parse(request.body)

  case event_type
  when "repository"
    process_repository(payload)
  when "issues"
    process_issue(payload)
  when "pull_request"
    process_pull_requests(payload)
  else
    puts "Oooh, something new from GitHub: #{event_type}"
  end
end

各イベントも複数のアクションを持つことができるため、アクションも同様に確認することをお勧めします。 たとえば、IssuesEvent ではいくつかのアクションが考えられます。 たとえば、issue が作成されたときの opened、issue が閉じられたときの closed、issue が誰かに割り当てられたときの assigned などです。

イベントタイプを追加するのと同じように、既存のイベントに新しいアクションを追加できます。 したがって、イベントのアクションをチェックするときも、どのような種類の catch-all else 構文も使わないことをお勧めします。 代わりに、イベントタイプの例と同様、イベントのアクションも明示的に確認するようお勧めします。 この例は、上記のイベントタイプで示したものと非常に似通ったものです。

# Recommended: explicitly check each action
def process_issue(payload)
  case payload["action"]
  when "opened"
    process_issue_opened(payload)
  when "assigned"
    process_issue_assigned(payload)
  when "closed"
    process_issue_closed(payload)
  else
    puts "Oooh, something new from GitHub: #{payload["action"]}"
  end
end

この例では、始めに closed アクションを調べてから、process_closed メソッドを呼び出します。 未確認のアクションは、今後の参考のために記録されます。

APIエラーの扱い

あなたのコードが決してバグを発生させなかったとしても、APIにアクセスしようとするとき立て続けにエラーが発生することがるかもしれません。

繰り返し表示される 4xx5xx の状態コードを無視せず、API と正しくやり取りしていることを確認する必要があります。 たとえば、エンドポイントが文字列を要求しているのに数値を渡している場合は、5xx 検証エラーを受け取り、呼び出しは成功しません。 同様に、許可されていないエンドポイントまたは存在しないエンドポイントにアクセスしようとすると、4xx エラーが発生します。

繰り返し発生する検証エラーを意図的に無視すると、不正利用によりアプリケーションが停止されることがあります。