$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

GraphQL APIを使って、特定のGitHub Teamが持つレポジトリ一覧を一発で取得する

最近 merged-pr-statを使ってマージされたPR情報を集計したり、レビューの偏り具合を可視化するスクリプトを書いたりしていた。この時自分のチームが使っているレポジトリをどう一覧化するかに困っていた。もともとはひとまずスクリプト上でハードコードしていたが、これだとメンテが面倒。

よくよく考えてみると、GitHubのTeamでレポジトリのアクセス権限を管理してるんだから、それを使えばよいのでは?と思って調べてみたら、GraphQL APIを使ったら一発だったのでメモ。

GraphQL APIを使ってGithub Teamが持つレポジトリを取得する

こんなクエリを書いたらいい。org-name1やteam-name1のところは適宜置き換える。

query {
  organization(login: "org-name1") {
    team(slug: "team-name1") {
      repositories(first: 100) {
        nodes {
          nameWithOwner
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
}

https://developer.github.com/v4/explorer/ とかで実行すると様子がわかる。以下のような構造のレスポンスが返ってくる。

{
  "data": {
    "organization": {
      "team": {
        "repositories": {
          "nodes": [
            {
              "nameWithOwner": "shibayu36/repo1"
            },
            {
              "nameWithOwner": "shibayu36/repo2"
            },
            {
              "nameWithOwner": "shibayu36/repo3"
            },
          ],
          "pageInfo": {
            "hasNextPage": false,
            "endCursor": "Y3Vyc29yOnYyOpHOEkFhXQ=="
          }
        }
      }
    }
  }
}

TypeScriptを使ってorg名team名から全レポジトリ一覧を取得する関数を書いてみる

ページネーションもちゃんとして全リストを取ってくるようにしたTypeScriptの関数の例。

import { gql, GraphQLClient } from "graphql-request";

type _TeamRepositoriesResponse = Readonly<{
  organization?: {
    team?: {
      repositories?: {
        nodes: {
          nameWithOwner: string;
        }[];
        pageInfo: {
          hasNextPage: boolean;
          endCursor: string;
        };
      };
    };
  };
}>;
/**
 * チームのレポジトリ名一覧を取得する
 */
export async function fetchTeamRepositories(
  client: GraphQLClient,
  orgName: string,
  teamName: string
): Promise<string[]> {
  const gqlQuery = gql`
    query($orgName: String!, $teamName: String!, $after: String) {
      organization(login: $orgName) {
        team(slug: $teamName) {
          repositories(first: 100, after: $after) {
            nodes {
              nameWithOwner
            }
            pageInfo {
              hasNextPage
              endCursor
            }
          }
        }
      }
    }
  `;

  let after: string | undefined;
  let repos: string[] = [];
  while (true) {
    const data = await client.request<_TeamRepositoriesResponse>(gqlQuery, {
      orgName,
      teamName,
      after,
    });
    const repositoriesNode = data.organization?.team?.repositories;
    if (!repositoriesNode) break;

    repos = repos.concat(repositoriesNode.nodes.map(n => n.nameWithOwner));

    if (!repositoriesNode.pageInfo.hasNextPage) break;

    after = repositoriesNode.pageInfo.endCursor;
  }

  return repos;
}

こんな感じで使える。

const GITHUB_GRAPHQL_ENDPOINT = "https://api.github.com/graphql";
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const graphQLClient = new GraphQLClient(GITHUB_GRAPHQL_ENDPOINT, {
  headers: {
    authorization: `Bearer ${GITHUB_TOKEN}`,
  },
});
const teamRepos = await fetchTeamRepositories(graphQLClient, "shibayu36", "team1");

まとめ

GraphQL APIを使って、特定のGitHub Teamが持つレポジトリ一覧を一発で取得するのを試してみた。最近GitHubAPIを色々使ってみてるけど、とにかくGraphQL APIは一発で取りやすいし、API Limitにも引っかかりにくいしで超便利。

VSCodeにAwesome Emacs Keymap入れた

もともとVSCodeEmacsキーバインドを再現するために、Emacs Friendly Keymap - Visual Studio Marketplaceを使っていたのだが、挙動に違和感があったり、微妙に使いづらいところがあったり、あまりメンテされてなかったりして、乗り換えたいなと思っていた。ちょっと調べてみると、今はAwesome Emacs Keymapというのが良いというのを見たので、こっちに移行してみた。

marketplace.visualstudio.com

かなりEmacsの挙動が再現されていて、使い勝手が良かって非常にありがたい。

良かったところ

移行に伴う設定変更の様子

VSCodeのExplorerでフォーカスしているファイルを、ActiveなEditor Groupの隣に開く拡張を作った

https://marketplace.visualstudio.com/items?itemName=shibayu36.open-to-other-editor-group

こういう拡張を作ったので紹介。

困っていたこと

VSCodeで開発している時に、今書いているコードの参考にするため別のファイルを開こうと思ったり、今の実装のテストファイルを開こうと思ったりすることがある。こういう時には毎回以下のどちらかを行っていた。

  • 別のEditor Groupにフォーカスを移して「Go to File...」を実行
  • Explorer Viewにフォーカスを移してファイルを開いた後、別のEditor Groupでもう一度今見ていたファイルを開き直す

これがめちゃくちゃ面倒だった。

Explorer Viewには「Open to the Side」というメニューコマンドもあるのだけど、これは一番端のEditor Groupにいた時にどんどん新しいEditor Groupが作られてしまって、コレジャナイ感がすごかった。

Image from Gyazo

解決策

以上困っていたことを解決してみようと思い、「Open to Other Editor Group」を作った。やってることは単純でExplorer Viewにいる時に「Open to Other Editor Group」コマンドを発行すると、フォーカスしているファイルが

  • 1 Editor Groupしかなければ、新しくEditor Groupを作って開く
  • 複数Editor Groupあれば、現在ActiveなEditor Groupの隣に開く
  • 一番端のEditor GroupがActiveなら、1番目のEditor Groupに開く

ということをやっているだけ。

Image from Gyazo

これだけでも超便利で愛用しています。

便利設定

ショートカットを登録しておくと便利です。

// Open the focused file in Explorer View to next to the active editor group.
{
  "key": "ctrl+o",
  "command": "open-to-other-editor-group.openToOtherEditorGroup",
  "when": "explorerViewletVisible && filesExplorerFocus && !explorerResourceIsFolder && !inputFocus"
}

まとめ

今回は「VSCodeExplorerでフォーカスしているファイルをActiveなEditor Groupの隣に開く拡張」を作ったので、その紹介をした。地味に便利なので、どうぞご利用ください。