Githubで今年作ったリポジトリの一覧を出力する

毎年この方法を調べている気がするが、今年はGithub CLIGraphQL APIの呼び出しをサポートしたようなので、コレを使って出力してみることにする1。GraphQL自体を試したい場合には、 https://docs.github.com/en/free-pro-team@latest/graphql/overview/explorer のあたりを参考にして試行錯誤してみれば良い。

利用例は以下のような形。 --paginate をつけると、いい感じに100件以上になったときもページネーションして取ってきてくれる。必要なら引数を渡せる。

$ gh api graphql --paginate -f query='
  query($endCursor: String) {
    viewer {
      repositories(first: 100, after: $endCursor) {
        nodes { nameWithOwner }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
'

このサンプルの例をベースに作ってみる。

createdAtが2020以降のもの

個人的には以下の様なものが欲しい

以下の様なMakefileを作る。フィルタリングは自作のコマンドでやっているが、慣れている人はjqなどでやっても良い。

# query
00:
  gh api graphql --paginate -f query='$(shell cat list-repository.query)' | jq . | tee repositories.json

# filtered
01:
  jqfpy --squash -c '[h.pick("nameWithOwner", "createdAt", d=row) for row in get("data/viewer/repositories/nodes") if not row["isFork"] and not row["isPrivate"] and row["createdAt"].startswith("2020-")]' repositories.json | tee 2020-repositories.json

# setup
setup:
  port install gh # or https://github.com/cli/cli
  pip install jqfpy

list-repositories.queryは以下のようなもの。

query($endCursor: String) {
  viewer {
    repositories(first: 100, after: $endCursor) {
      nodes { nameWithOwner createdAt updatedAt isPrivate isFork }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
}

ちなみに、これはMakefile故の問題ではあるのだけれど、オプションに渡すqueryを'ではなく"でクォートしようとしてしまうと$endCursorなどが展開されてしまいParseErrorになってしまう。コレでしばらくハマってしまっていた2

今年作られたリポジトリ

そんなわけで今年作ったリポジトリは以下の様なものらしい。それぞれの雑感については別の記事で触れるかもしれない。この記事では触れない。

{"nameWithOwner": "podhmo/cliauth", "createdAt": "2020-01-08T14:08:56Z"}
{"nameWithOwner": "podhmo/minitask", "createdAt": "2020-01-26T08:09:01Z"}
{"nameWithOwner": "podhmo/snatch", "createdAt": "2020-02-01T06:28:14Z"}
{"nameWithOwner": "podhmo/scoop-buckets", "createdAt": "2020-03-13T14:50:54Z"}
{"nameWithOwner": "podhmo/baku", "createdAt": "2020-03-23T16:19:22Z"}
{"nameWithOwner": "podhmo/egoist", "createdAt": "2020-04-21T20:34:34Z"}
{"nameWithOwner": "podhmo/maperr", "createdAt": "2020-05-03T06:47:54Z"}
{"nameWithOwner": "podhmo/typoless", "createdAt": "2020-05-29T21:48:10Z"}
{"nameWithOwner": "podhmo/sheetconf", "createdAt": "2020-07-04T08:22:49Z"}
{"nameWithOwner": "podhmo/startevent", "createdAt": "2020-09-22T14:11:50Z"}
{"nameWithOwner": "podhmo/tenuki", "createdAt": "2020-09-26T23:12:42Z"}
{"nameWithOwner": "podhmo/reflect-openapi", "createdAt": "2020-10-03T05:50:01Z"}
{"nameWithOwner": "podhmo/starlette-dataframe-response", "createdAt": "2020-10-30T16:17:07Z"}
{"nameWithOwner": "podhmo/inflexible", "createdAt": "2020-11-28T21:09:31Z"}

createdAtの他にupdatedAtもあるので、更新日でみたい人はそちらを使えば良い。更新日だとさすがに長くなる。

{"nameWithOwner": "podhmo/emacs-sandbox", "updatedAt": "2020-12-09T07:26:20Z"}
{"nameWithOwner": "podhmo/alchemyjsonschema", "updatedAt": "2020-08-17T13:42:55Z"}
{"nameWithOwner": "podhmo/python-semver", "updatedAt": "2020-11-05T17:19:18Z"}
{"nameWithOwner": "podhmo/miniconfig", "updatedAt": "2020-07-09T19:24:53Z"}
{"nameWithOwner": "podhmo/prestring", "updatedAt": "2020-11-29T20:08:08Z"}
{"nameWithOwner": "podhmo/handofcats", "updatedAt": "2020-07-09T19:18:58Z"}
{"nameWithOwner": "podhmo/individual-sandbox", "updatedAt": "2020-12-26T16:55:55Z"}
{"nameWithOwner": "podhmo/selfish", "updatedAt": "2020-05-30T10:46:06Z"}
{"nameWithOwner": "podhmo/magicalimport", "updatedAt": "2020-02-06T00:48:25Z"}
{"nameWithOwner": "podhmo/dictknife", "updatedAt": "2020-10-05T01:57:29Z"}
{"nameWithOwner": "podhmo/swagger-marshmallow-codegen", "updatedAt": "2020-12-20T21:46:40Z"}
{"nameWithOwner": "podhmo/kamidana", "updatedAt": "2020-03-17T12:41:41Z"}
{"nameWithOwner": "podhmo/moduleknife", "updatedAt": "2020-05-23T15:22:48Z"}
{"nameWithOwner": "podhmo/jqfpy", "updatedAt": "2020-12-29T22:36:40Z"}
{"nameWithOwner": "podhmo/commithistory", "updatedAt": "2020-04-29T09:35:17Z"}
{"nameWithOwner": "podhmo/go-twitter-notififer", "updatedAt": "2020-04-29T08:59:35Z"}
{"nameWithOwner": "podhmo/go-traceable", "updatedAt": "2020-08-27T03:23:49Z"}
{"nameWithOwner": "podhmo/pyinspect", "updatedAt": "2020-02-06T06:29:52Z"}
{"nameWithOwner": "podhmo/shosai", "updatedAt": "2020-01-17T13:15:27Z"}
{"nameWithOwner": "podhmo/pycomment", "updatedAt": "2020-07-04T16:54:44Z"}
{"nameWithOwner": "podhmo/go-webtest", "updatedAt": "2020-12-12T04:06:44Z"}
{"nameWithOwner": "podhmo/lsp-spec-to-openapi-doc", "updatedAt": "2020-08-12T05:03:04Z"}
{"nameWithOwner": "podhmo/pypackage", "updatedAt": "2020-10-31T14:20:45Z"}
{"nameWithOwner": "podhmo/metashape", "updatedAt": "2020-12-26T12:21:52Z"}
{"nameWithOwner": "podhmo/monogusa", "updatedAt": "2020-02-29T15:17:08Z"}
{"nameWithOwner": "podhmo/cliauth", "updatedAt": "2020-01-08T14:09:11Z"}
{"nameWithOwner": "podhmo/minitask", "updatedAt": "2020-02-26T15:26:11Z"}
{"nameWithOwner": "podhmo/snatch", "updatedAt": "2020-02-01T07:07:36Z"}
{"nameWithOwner": "podhmo/scoop-buckets", "updatedAt": "2020-03-13T15:07:18Z"}
{"nameWithOwner": "podhmo/baku", "updatedAt": "2020-03-23T17:07:24Z"}
{"nameWithOwner": "podhmo/egoist", "updatedAt": "2020-10-31T14:44:25Z"}
{"nameWithOwner": "podhmo/maperr", "updatedAt": "2020-05-09T10:11:39Z"}
{"nameWithOwner": "podhmo/typoless", "updatedAt": "2020-05-30T08:34:52Z"}
{"nameWithOwner": "podhmo/sheetconf", "updatedAt": "2020-07-05T20:49:17Z"}
{"nameWithOwner": "podhmo/startevent", "updatedAt": "2020-09-22T15:57:50Z"}
{"nameWithOwner": "podhmo/tenuki", "updatedAt": "2020-09-29T19:24:23Z"}
{"nameWithOwner": "podhmo/reflect-openapi", "updatedAt": "2020-12-26T14:57:03Z"}
{"nameWithOwner": "podhmo/starlette-dataframe-response", "updatedAt": "2020-10-31T14:19:26Z"}
{"nameWithOwner": "podhmo/inflexible", "updatedAt": "2020-12-12T15:03:52Z"}

雑感

こういう取得・整形(フィルタリング)の処理を1ファイルでまとめてしまいたいと言う気持ちもあるし、収集と整形は処理を分けたいと言う気持ちもある。Makefile1つでやってやれないことはないのだけれど、ちょっと複雑になるので結局分けた。

gist


  1. それ以外にもいつの間にかいろいろ機能が増えて便利になっていた

  2. gh: Parse error on ":" (COLON) at [1, 7] というようなエラー。