<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitHub Actions</title><link>https://blog.ast.moe/tags/github-actions/</link><description>Recent posts from 五里霧中</description><generator>Hugo</generator><item><title>AIで開発が進むほどCIの30分待ちがつらくなったので、Omoikaneの検証を見直した</title><link>https://blog.ast.moe/blog/2026-09-29/</link><pubDate>Tue, 29 Sep 2026 10:20:00 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-09-29/</guid><description>&lt;p&gt;Claude CodeやCodexなどのAIコーディングエージェントを使って開発していると、実装や調査を並行して進められる。以前なら一つずつ片付けていたタスクに、同じ日にいくつも手を付けられるようになった。一方で、PRを出した後のCIは、こちらの作業速度に合わせて速くなるわけではない。&lt;/p&gt;
&lt;p&gt;自作ブラウザの&lt;a href="https://github.com/ieee0824/omoikane"&gt;Omoikane&lt;/a&gt;はRustのコードが大きく、内部で使うBoaも別のCargo workspaceにある。テストはLinuxとmacOS、異なるアーキテクチャ、WPTなどに広がっている。Rustは依存関係やfeature、targetに応じてコンパイルするので、この構成では特にビルドの待ち時間が目立つ。エージェントが次の変更を作れても、前の変更の検証が終わらないまま積み上がっていく。&lt;/p&gt;
&lt;p&gt;そこで&lt;a href="https://github.com/ieee0824/omoikane/issues/1104"&gt;Issue #1104&lt;/a&gt;で、どこに時間がかかっているかを測り、CIの実行条件を見直した。目標はテストを雑に減らすことではない。変更に必要な検証は維持したまま、不要な実行と再準備を減らすことだ。&lt;/p&gt;
&lt;h2 id="最初に待ち時間を測った"&gt;最初に待ち時間を測った&lt;/h2&gt;
&lt;p&gt;調査時点で成功していた通常CIは14分13秒と16分40秒、Release / Gate 5は27分16秒と27分03秒だった。これらのworkflowは並行して走るので、数字を足してPRの待ち時間にしてはいけない。最後に終わるcheckが、次の判断を待たせる。&lt;/p&gt;
&lt;p&gt;特に分かりやすかったのが、&lt;code&gt;AGENTS.md&lt;/code&gt;だけを変更した&lt;a href="https://github.com/ieee0824/omoikane/pull/1103"&gt;PR #1103&lt;/a&gt;だった。コードの動作は変わらないのに、通常CI、ブラウザの挙動確認、JIT、Release、CodeQLが起動し、全checkの完了まで30分43秒かかった。大きなコード変更と同じ規模のゲート待ちが、文書修正にも付いていた。&lt;/p&gt;
&lt;p&gt;もう一つはキャッシュだった。Gate 5のmacOS ARM64では、&lt;code&gt;rust-cache&lt;/code&gt;がfull matchを報告した成功runでも242クレートを再コンパイルし、Cargoビルドに約12分かかっていた。「キャッシュに命中した」という表示だけでは、コンパイル済み成果物まで使えているとは言えない。&lt;/p&gt;
&lt;p&gt;この時点では、CIを丸ごと一つ速くする方法を探すより、待ち時間を生んでいる理由を分けて調べることにした。文書の変更判定、Rustの成果物、WPTの準備、古いrunの重複は、それぞれ別の問題である。&lt;/p&gt;
&lt;h2 id="文書だけなら重い検証を起動しない"&gt;文書だけなら、重い検証を起動しない&lt;/h2&gt;
&lt;p&gt;最も大きな差が出たのは、最初から不要なjobを実行しないことだった。&lt;/p&gt;
&lt;p&gt;変更ファイルを分類し、ルートの&lt;code&gt;AGENTS.md&lt;/code&gt;と&lt;code&gt;README.md&lt;/code&gt;、&lt;code&gt;docs/&lt;/code&gt;配下のMarkdownだけなら軽量経路を使う。コード、fixture、WPTの入力、workflow、スクリプトなどが一つでも混ざれば通常の検証へ進む。変更範囲を確定できなかったときも、検証を省かない。&lt;a href="https://github.com/ieee0824/omoikane/blob/main/scripts/ci-change-scope.py"&gt;分類スクリプト&lt;/a&gt;はPRの変更ファイルを取得し、許可した文書だけかを判定する。&lt;/p&gt;
&lt;p&gt;ここでworkflow自体を&lt;code&gt;paths&lt;/code&gt;で除外しなかった。branch protectionでrequired checkにしているjobが生成されず、PR側ではPendingのまま残る可能性があるからだ。&lt;a href="https://github.com/ieee0824/omoikane/blob/main/.github/workflows/change-scope.yml"&gt;変更判定用のcheck&lt;/a&gt;は常に動かし、重いjobをjob単位でskipする。分類に失敗したら全検証へ戻す。意図したskipと、必要な検証が失敗・未実行になった状態を混同しないための設計である。&lt;/p&gt;
&lt;p&gt;変更後の文書のみの&lt;a href="https://github.com/ieee0824/omoikane/pull/1114"&gt;PR #1114&lt;/a&gt;は、全checkが3分53秒で完了した。変更前のPR #1103の30分43秒から26分50秒、約87%短い。成功jobの実行時間を全部足した値も271分06秒から33秒になった。ただし、この「job実行時間合計」は並列jobの時間を足したもので、PRの待ち時間でも課金時間でもない。比較した二つのPRは別revisionで、runnerの待ち時間も異なる。厳密な同一条件のA/B試験とは言わない。&lt;/p&gt;
&lt;p&gt;それでも、文書変更に対して何を実行するかを変えた効果は明確だった。重い検証はskipされ、required checkは完了し、PRをマージできた。&lt;/p&gt;
&lt;h2 id="rustのキャッシュはヒットだけ見ても分からない"&gt;Rustのキャッシュは「ヒット」だけ見ても分からない&lt;/h2&gt;
&lt;p&gt;コード変更ではビルドを避けられない。そこで次に見たのが、Gate 5のキャッシュである。&lt;/p&gt;
&lt;p&gt;Omoikaneのルートworkspaceと&lt;code&gt;engine/boa/&lt;/code&gt;は別workspaceになっている。ルート側から見ればBoaはpath依存だが、&lt;code&gt;Swatinem/rust-cache&lt;/code&gt;の&lt;code&gt;cache-workspace-crates: true&lt;/code&gt;だけでは、そのBoaの成果物が期待どおり残らない構成だった。LinuxではOmoikaneとBoaのpath依存群を含む10クレートが再コンパイルされ、macOSではレジストリ依存を含む242クレートが再コンパイルされていた。macOSの大量再コンパイルを、Boaだけのせいと決めることもできなかった。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/ieee0824/omoikane/blob/main/.github/workflows/jit-release-gate.yml"&gt;Gate 5のworkflow&lt;/a&gt;では、Rust全般のキャッシュと、Boaのsuite用成果物を保持する別キャッシュを使うようにした。BoaのRustソースやmanifest、lockfileが変わればBoa側のキーを切り替える。逆にOmoikane側のソースだけが変わったなら、Boaの成果物は再利用する。実際にBoaだけを変えたrunではキャッシュミスと必要な再コンパイルを、ホスト側だけを変えたrunでは3ターゲットでのBoaキャッシュ復元を確認した。&lt;/p&gt;
&lt;p&gt;同じhead &lt;code&gt;a34b618f&lt;/code&gt; のGate 5を初回と再実行で比べた結果は次のとおりだった。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;suite target&lt;/th&gt;
&lt;th style="text-align: right"&gt;初回&lt;/th&gt;
&lt;th style="text-align: right"&gt;warm再実行&lt;/th&gt;
&lt;th style="text-align: right"&gt;&lt;code&gt;Compiling&lt;/code&gt;の件数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linux ARM64&lt;/td&gt;
&lt;td style="text-align: right"&gt;22分53秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;18分49秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;242 → 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux x86_64&lt;/td&gt;
&lt;td style="text-align: right"&gt;27分45秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;23分35秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;245 → 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS ARM64&lt;/td&gt;
&lt;td style="text-align: right"&gt;25分06秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;20分24秒&lt;/td&gt;
&lt;td style="text-align: right"&gt;242 → 10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Gate 5全体では31分04秒から25分09秒へ、5分55秒短くなった。同一revision・同一feature・同一targetでの比較だが、runnerの負荷による変動も含む。初回には新しいキャッシュの保存時間も入っている。先に挙げた変更前の「約27分」のrunとはrevisionが違うので、その差を改善効果と呼ぶことはできない。&lt;/p&gt;
&lt;p&gt;macOSで残った10件については、復元したCargo fingerprintより、checkoutされたソースの更新時刻が新しいことをログで確認した。キャッシュがあるのに再ビルドされる理由は、表示上のhitだけではなくfingerprintまで見ないと分からない。&lt;/p&gt;</description></item></channel></rss>