Open3

GitHub上のcommitとかtreeをrawgitみたいにキャッシュして提供したい

okuokuokuoku

GitHub上のリポジトリを直接実行できるゲームエンジンが欲しいので構成を検討する。

  • 1分間1回程度のリクエストは匿名でもできるので、リポジトリの HEAD をSHA1に変換する操作は単純にproxyする (たぶんCORS的に直接はできないんじゃないか)
  • リポジトリ上のファイルはgithack等のGitHub CDN https://qiita.com/okuoku/items/9c72a88662831d774742 系のサービスを使う
  • treeオブジェクトやcommitオブジェクトは内容が変化することがないので、CloudFlare Workersか何かでキャッシュに突っ込みつつ処理することにすれば、1日1000回とかプレイされるようになっても余裕があると考えられる

3つ目を考える。

  • TODO: Gistのサポート、sizeをtreeのレスポンスに含めて良いか?、GitHub以外のリポジトリプロバイダ?commitにはユーザのアイコンとかユーザ名とかリポジトリのリンクとかを含めた方が良いか?
okuokuokuoku

GitHub Rest APIで手動で取ってくる

GitHubのURLから直接treeの値を知ることはできないので、あるファイルを取得するには branch名 → commit → tree → blob と辿る必要がある。

(注意: ブラウザでもクリックで実行できるが、APIにかなり保守的なrate limitがあるので注意。また本来は Accept: application/vnd.github.v3+json が必要。)

refの取得

リポジトリのURL https://github.com/okuoku/yuni が与えられたとして、 デフォルトブランチの最新のコミット を取得することを考える。このためには、デフォルトブランチ名と、それが指すrefを取得する必要がある。

  1. ユーザ名(owner)、リポジトリ名を取得する (okuokuyuni)
  2. デフォルトブランチを取得する: https://api.github.com/repos/okuoku/yunidefault_branch => master
  3. refを参照する: https://api.github.com/repos/okuoku/yuni/git/ref/heads/masterobject.ref => 22a55b9c403fc3f929cc39727a06e44a7db19715

デフォルトブランチとそれが指すcommitは日々刻々と変化するのでブラウザからfetchするべきだろう。GitHubのAPIは1時間に60回までは匿名でリクエストできる。

commitの取得

commitからコミットメッセージとtreeを取得する。

これはキャッシュすべき内容だろう。一度生成されたら絶対に変更されない。

treeの取得

treeの実際の内容を取得する。これにはファイル名やディレクトリ(他のtree)が含まれる。

重大な注意事項として、treeは可変長であるという点がある。treeが大きすぎて返却できない場合は truncated が真になることで通知される。

本来のtree objectにはサイズの情報は入っていないが、GitHubは返却するようだ。

また、Gitの仕様として、treeが何というディレクトリ名だったかの情報は含まれていない。このため、GitHub CDNにリクエストを行うには クライアントサイドで参照元のcommitとパスを覚えておいてURLを組み立てる 必要がある。また、逆に、ファイルのフルパスが事前に判明している場合はcommitやtreeを取得する必要がない。

ファイルの取得

まだ生きているジェネリックなGitHub CDNはgithackくらいのようだ。

ここでのURLにはtreeではなくcommitとフルパスが使用される。

okuokuokuoku

レスポンスの仕様を考える

基本的には名前付きのjsonオブジェクトを返却する。どこかのタイミングでPublic GitHub以外にも対応したいので、他のシステムの仕様に合わせた変更が必要になる可能性はそれなりにある。

基本構造

レスポンスは以下のようなオブジェクトにwrapする。

{"time": unix_time,
  "paged": false,
  "result": {OBJECT}}

今のところ、ページングはサポートする必要が無いと思っている。というわけで paged は false 固定。

repoinfo : /:plt/:owner/:repo/repoinfo

デフォルトブランチや、最近更新されたrefの情報は専用のrepoinfoに格納させる。これは完全に独自仕様。

{"private": false,
  "provider": "github_public",
  "web_url": "https://...",
  "default_branch": "master"}

例えば clone url とかを後で追加すると良さそう。

plt はプラットフォームで、public GitHubには gh をアサインする。

refs

{"refs": [...]}

refs はオブジェクトで、リポジトリに存在するブランチを列挙するために使う。GitHubで言うと https://api.github.com/repos/okuoku/yuni/git/refs の内容となる。... これはマジで全てのrefを含むため何らかの方法でフィルタリングが必要になる。例えば https://api.github.com/repos/facebook/react/git/refs は508ページもある。

これは後回しでも良いかな。。列挙はそれぞれのプロバイダのWebUIでやってもらうって事で。。

ref : /:plt/:owner/:repo/ref/:refname

refの指すオブジェクトを表わす。

{"commit": git_hash}

commit : /:plt/:owner/:repo/commit/:sha

{"web_url": "https://...", "tree": git_hash, parents: [git_hash, ...]}

★ コミットメッセージはJSONに返却しない。独立したテキストファイルとしてコミットメッセージの中身だけを提供する。

FIXME: コミッタは複数になる可能性があるけど、authorはどうなのか?

tree : /:plt/:owner/:repo/tree/:sha

{"entries": [...]}
{"path": "name", "mode": "10666", "type": "blob", "size": 0, "sha": "...."}

size はちょっと悩みどころだが、いわゆる stat コマンドに相当する操作で必要なデータは埋めておいた方が都合が良いので含めておくことにした。

GitHub等のWebUIでは最終更新となるcommitの情報も入っているが、それは演算負荷無しでは取れないので省略。