PR プレビュー (uzu preview)
内部ドキュメント
このドキュメントは内部実装の詳細です。ゲーム開発者向けの情報は docs.code.uzu-app.com を参照してください。
シナリオ repo の PR を、差分ではなく実物で確認するための仕組み。 uzu preview はビルドしたゲームを R2 に 1 本置き、emulator の起動 URL を返す。 revision は作らないので、配信中のシナリオには一切影響しない。
使い方
uzu preview # emulator の URL を出力する
uzu preview --json # { "id", "url", "expiresAt" } を stdout へ
uzu preview --label "PR #453" # backend の監査ログに残す表示名--json のときは進捗もビルドツールの出力も stderr へ寄せるので、stdout をそのまま JSON として読める。CI では UZU_PUBLISH_TOKEN で認証する (uzu publish と同じ)。
--label は 200 バイト以内・改行なし。ビルドの前に検証するので、長すぎる PR タイトルを そのまま渡してもビルドが無駄にならない。
仕組み
uzu preview
→ ビルド → upload session を preview モードで取得 (id = "pv-" + ULID)
→ data.zip / files/* / logic.js を R2 へ
→ registerRevision は呼ばない
→ 応答の署名済み endpoint から emulator の URL を組んで出力
→ 14 日後に R2 のライフサイクルが実体を消す → リンクは 404 になるpublish との違いは「revision を登録するか」だけで、ビルドからアップロードまでは 同じ経路 (bundle-upload.ts) を通る。
| publish | preview | |
|---|---|---|
| revisionId | ULID | pv- + ULID |
scenario_revisions | 作る | 作らない (backend が 403) |
| 配信 endpoint の署名 | 24 時間 | 15 日 |
| 実体の寿命 | 無期限 | R2 のライフサイクルで 14 日 |
| キャラクター画像 (icon / playCard) | Cloudflare Images へアップロード | アップロードしない |
署名を実体より長い 15 日にしているのは、常に「実体が先に消える」向きへ倒すため。 逆にすると、ファイルはあるのにリンクだけ死んでいる窓ができる。
pv- プレフィックスが担っていること
pv- は 3 か所で同じ意味を持つ。片方だけ変えると壊れる。
- R2 のライフサイクル規則が、この prefix を見て 14 日で削除する
POST /uzu_code/scenario_revisionsが、この prefix の upload token を 403 で弾く (実体が消える前提のものを配信レコードにしない)- orphan prefix の掃除を実装するときは、この prefix を対象外にする (プレビューは対応する revision を持たないので、そのままだと発行直後に消える)
運用要件
R2 バケット uzu-code-revisions に、prefix pv- を 14 日で削除するライフサイクル規則が必要。 規則が無いとプレビューの実体が無期限に残る (リンクは 15 日で開けなくなるが、オブジェクトは残る)。
認可
プレビューの URL を知っていれば誰でも部屋に入れる。play-server の WebSocket は seatId しか見ておらず、revisionId の推測不可能性がそのままアクセス制御になっている。 pv-{scenario}-pr{N} のような決め打ちの ID にすると PR 番号を当てるだけで未公開シナリオの state を読めてしまうため、ULID のまま扱う。