虎の穴ラボ技術ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

GitHubのレビューコメント一覧を取得するWebツールを作ってみた

はじめに

こんにちは!虎の穴ラボの鷺山です。

あるとき「自分が今までに書いたGitHubのコメントを振り返ってみよう!」と思い、
GitHubのプルリクエストで自分が過去に投稿したレビューコメントの一覧を取得しようとしたのですが、
どうやらGitHubのサイト上にはこの機能がないようです(執筆時点)。

ただし、GitHubが提供しているREST APIを利用すれば、それが実現できることが分かりました。

そこで、今回はこのAPIを使って、自分自身が過去に投稿したGitHubのレビューコメント一覧を取得できるWebツールを作ってみたのでご紹介します!

作ったツール

こちらがそのツールになります(GitHub Pagesでホストしています):

必要な情報を入力して「コメント取得」を押すと、GitHubのレビューコメントを一覧で見ることができます。

解説

ソースコード全体はこちらのGitHubリポジトリで公開しています。

次のセクションから技術的なポイントについて解説します。

使用するGitHub REST APIエンドポイント

次の2つのGitHub REST APIエンドポイントを使います:

  1. レビューコメント一覧取得 (List review comments in a repository)
  2. プルリクエスト情報取得 (Get a pull request)

1. レビューコメント一覧取得

コメントの一覧を取得するためにレビューコメント一覧取得 (List review comments in a repository) APIを使います。これは指定したリポジトリのプルリクエストに対するレビューコメントの一覧を取得できます。

GET /repos/{owner}/{repo}/pulls/comments

このエンドポイントの {owner} には対象のリポジトリのオーナー名を、{repo} にはリポジトリ名を指定します。
クエリパラメータとして、開始期間 since やページング用の page, per_page も指定できます。

また、対象がプライベートリポジトリの際は Authorization ヘッダに適切なGitHubアクセストークンを指定する必要があります。このトークンの作成手順については別のセクションで詳しく解説します。

たとえば、cURLでの実行例は次のようになります:

curl -L \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer XXXXX" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/octocat/spoon-knife/pulls/comments?since=2024-10-15&page=1&per_page=10

2. プルリクエスト情報取得

コメント先のプルリクエストの詳細な情報を取得するためにプルリクエスト情報取得 (Get a pull request) APIも使います。これで各プルリクエストのタイトルや作成者の情報などを取得できます。

GET /repos/{owner}/{repo}/pulls/{pull_number}

このエンドポイントの pull_number には対象のプルリクエストの番号を指定する必要があります。
この番号を含むプルリクエストのURLは前述の「レビューコメント一覧取得」APIのレスポンスに含まれています。

cURLでの実行例は次のようになります:

curl -L \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer XXXXX" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/octocat/spoon-knife/pulls/3

注意点

APIの実行にはレート制限があり、それを超過するとエラーレスポンスが返るようになります。

GitHubアクセストークンの作成

GitHub REST APIの実行に必要なアクセストークンの作成手順を紹介します(執筆時点の情報です)。

現在、個人用アクセストークンには "Fine-grained" (きめ細かい) 版と "Classic" (従来) 版の2種類があります。 詳細は公式ドキュメントをご確認ください。

共通の手順

まずGitHubにログインし、パーソナルアクセストークンの設定画面を開きます。
次に、使用するトークンの種類に応じて、以下のいずれかの手順に進みます。

Fine-grained 版の場合

  1. "Fine-grained tokens" をクリックし、"Generate new token" をクリックします。
  2. 作成画面に移るので、"Token name" に分かりやすい名前を入力します(例: Review Comments Viewer)。
  3. プライベートリポジトリが対象の場合は、
    • "Repository access""Only select repositories" を選択し、"Select repositories" プルダウンから対象のリポジトリを選択します。
      • ※ Organizationのリポジトリの場合は、Organization側の設定が必要な場合があります。
    • また "Permissions""Repository permissions" で、"Contents""Pull requests" のアクセスを "Read-only" に設定します。
  4. 最後に "Generate token" をクリックすると、アクセストークンが生成されます。

Classic 版の場合

  1. "Tokens (classic)" をクリックし、 "Generate new token" プルダウンから "Generate new token (classic)" を選択します。
  2. 作成画面に移るので、"Note" に分かりやすい名前を入力します(例: Review Comments Viewer)。
  3. "Select scopes""repo" にチェックを入れます。
  4. 最後に "Generate token" をクリックすると、アクセストークンが生成されます。

注意点

アクセストークンは一度しか表示されないため、必ずコピーして安全な場所に保管してください。

ブラウザでGitHub REST APIを実行する

前述のAPIをブラウザ上で実行できるようにWebツールとして実装します。

各種情報(リポジトリ名やオーナー名、アクセストークンなど)をフォームから入力し、APIを実行してコメントを取得した後、それをHTMLでレンダリングして表示します。

入力フォーム

まず、APIの実行時に指定するパラメータをHTMLの入力フォームとして設定します:

  <input id="owner" type="text" ... />
  ...
  <input id="repo" type="text" ... />
  ...
  <input id="since" type="date" ... />
  ...
  <input id="username" type="text" ... />
    ...
    <input id="exclude-self" type="checkbox" />
    ...
  <input id="token" type="password" ... />
  ...
  <button id="get-comments" ... type="button">コメント取得</button>

ポイント

  • exclude-self: 自分で作成したプルリクエストに対するコメント(以降「自己コメント」と呼びます)を除外するためのオプションです。
    • チェックが入っているとき「他者が作成したプルリクエストに向けたコメント」のみを抽出します。
  • token: アクセストークンは秘匿情報のため password 型にしています。
  • get-comments: フォーム入力後、「コメント取得」ボタンのクリックでAPIが実行されるようにします。
const getCommentsButton = document.getElementById("get-comments");

// 「コメント取得」ボタンのクリックイベント
getCommentsButton.addEventListener("click", (event) => {
  ...
  fetchComments();
});

// コメントを取得してレンダリングする関数
async function fetchComments() {
  // フォームの入力値を取得
  const owner = document.getElementById("owner").value;
  const repo = document.getElementById("repo").value;
  const since = document.getElementById("since").value;
  const username = document.getElementById("username").value;
  const excludeSelf = document.getElementById("exclude-self").checked;
  const token = document.getElementById("token").value;

  const url = `https://api.github.com/repos/${owner}/${repo}/pulls/comments` +
    `?since=${since}&page=${page}&per_page=${perPage}`;

  const headers = {
    Accept: "application/vnd.github+json",
    "X-GitHub-Api-Version": "2022-11-28",
    ...(token && { Authorization: `Bearer ${token}` })
  };

  try {
    // コメント一覧取得API実行
    const response = await fetch(url, { headers });
    ...

コメント先のプルリクエスト情報の取得

レビューコメント一覧取得のAPIレスポンスには、コメント先のプルリクエストのタイトルや作成者などの情報が含まれていません。それらを補完するため、別途プルリクエスト情報取得のAPIも実行します。

プルリクエストのURLは「レビューコメント一覧取得」レスポンスの pull_request_url に含まれています。

  const prUrl = comment.pull_request_url;

効率化のため、一度取得したプルリクエストの情報はキャッシュし、再利用することでAPIのリクエスト数を抑えます。

const prCache = new Map();
...

async function fetchPullRequest(prUrl, headers) {
  if (prCache.has(prUrl)) return; // 既に取得済みの場合はスキップ

  const response = await fetch(prUrl, { headers });
  const data = await response.json();

  // レスポンスが成功の場合、キャッシュに保存
  if (response.ok) {
    const result = {
      title: data.title, // タイトル
      author: data.user.login, // 作成者
      ...
    };
    prCache.set(prUrl, result);
  }

ユーザー名によるコメントの絞り込み

今回使うAPIには「ユーザー名でコメントを絞り込む機能」が無いので、スクリプト内でそれを実現します。

    // 指定されたユーザーのコメントのみをフィルタリング
    const comments = username
      ? data.filter(it => it.user.login === username)
      : data;

excludeSelf オプションを選択している場合は、自己コメントも除外します。

    // 自己コメントを除外している場合
    const filteredComments = username && excludeSelf
      ? comments.filter(it => prCache.get(it.pull_request_url).author !== username)
      : comments;

取得したコメント一覧の表示

最後に、取得した各コメントをHTMLにレンダリングします。

// 各コメントデータを表示用のDiv要素に変換する関数
function createCommentDiv(comment) {
  const result = document.createElement("div");
  result.className = "comment";
  ...

  result.innerHTML = `
    <div class="comment-header">
      <!-- ヘッダー部分 -->
      ...
    </div>

    <div class="comment-body">
      <!--コメント本体部分 -->
      ...
    </div>
  `;

  return result;
}
ヘッダー部分

ヘッダー部分にはコメント先のプルリクエストのタイトル、URL、番号、作成者の情報を含めます。

  const pr = prCache.get(comment.pull_request_url);
    <!-- ヘッダー部分 -->
    <div>${prIcon}</div>
    <div><b>${pr.title}</b></div>
    <div><a href="${pr.url}" ...>#${pr.number}</a></div>
    <div ...>
      <img src="${pr.avatarURL}" ... title="${pr.author}">
    </div>
コメント本体部分

コメント本体部分の上部には、コメント先のファイルのパスや、差分情報(最大5行)を含めます。

  const filepath = comment.path;
  const diff = comment.diff_hunk.split("\n").slice(1).slice(-5).join("\n");
    <!--コメント本体部分 -->
    <div ...>
      <div>${fileIcon}</div>
      <div>${filepath}</div>
    </div>
    <div ...>${sanitizeHTML(diff)}</div>

コメント本体部分の下部には、コメント者の情報やコメント本文を含めます。

  const avatarURL = comment.user.avatar_url;
  const url = comment.html_url;
  const date = toJPDate(comment.created_at);
  const text = sanitizeHTML(comment.body);
    <div class="commenter">
      <div ...>
        <img src="${avatarURL}" ...>
      </div>
      <div><b>${comment.user.login}</b></div>
      <div><a href="${url}" ...>on ${date}</a></div>
    </div>
    <div ...>${text}</div>

※ HTMLタグが埋め込まれうる文字列は sanitizeHTML() でサニタイズします。

まとめ

今回はGitHubのレビューコメント一覧を取得するWebツールを紹介し、それを実現するためのGitHub REST APIの活用方法を解説しました。

GitHubはこのほかにも多くのREST APIエンドポイントを提供しており、これらを組み合わせることで様々な分析をしたり、開発に役立つツールを作るなど、色々便利なことに応用できそうです。

採用情報

虎の穴ラボでは一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。
toranoana-lab.co.jp