GitHub Actionsの月間無料枠2,000分を、3日で使い切った。ひと月ぶんの予算が、ひと月の十分の一ほどの日数で消えたわけだ。
最近、CodexやClaude CodeといったAIコーディングエージェントで個人開発を進めている。以前なら数日かかった機能が短時間で書き上がるので、思いついたものはとりあえず作ってみるようになった。最近では、電車の中でも寝る前の布団の中でも、暇さえあればスマホからエージェントに指示を飛ばしている。僕が手を動かしていない間も、pushだけは粛々と積み上がっていくわけだ。その結果がこれである。
個人開発なので、費用はできるだけ抑えたい。かといって、テストを省いて品質を差し出すのも御免蒙りたい。そこで、GitHub Actionsで回していたチェックの一部を、Huskyでpush前にローカルで走らせることにした。以下、その顛末と、まだ手をつけていない宿題について書く。
関所が混みはじめた
CIは、コードが本流に入る前に通る関所のようなものだ。通るたびに検めを受け、そのぶんの通行料(実行時間)を払う。AIのおかげで旅人の数が増えた結果、この関所に行列ができはじめているらしい。
しかも、これは個人開発に限った話ではない。2026年9月、Anthropicが「Agentic coding is straining CI. Here's how we scaled test impact analysis at Anthropic」という記事を公開している。Anthropicでは、エンジニアが四半期あたりに出荷するコード量が2021〜2025年と比べて平均8倍になり、CIジョブ数は6か月で25倍に増えたという。書くことがボトルネックでなくなった途端、今度はそれを検める側に負荷が集中したのだ。
同じ月には、Linearも「AI coding has made CI a bottleneck, so we reworked ours to keep up」を公開している。こちらもAIで開発が速くなった結果、CIの実行時間とコストが問題になり、テストの並列化や実行基盤の改善に取り組んだという内容だ。
日本では、株式会社DeploAIのCTOが「AI時代におけるローカルCIの重要性とその運用について」を書いている。AIエージェントを活用する現場でCIが開発速度を縛るようになり、self-hosted runnerを運用するうちに、同時実行数、CPU、ディスクI/Oが次々とボトルネックになった経緯が紹介されている。
こうした事例を眺めていると、ソフトウェア開発の詰まりどころが移りつつあるのを感じる。コードを生成するコストは下がった。しかし、生成されたコードが正しいことを確かめるコストまで下がったわけではない。そしてこれは大きな開発組織だけの悩みではなく、僕のような個人の手元でも起こる話なのだ。
3日で尽きた2,000分
GitHub Actionsには、プランに応じて月ごとの無料実行時間がある。GitHub Freeなら、privateリポジトリ向けに月2,000分だ。なお、publicリポジトリで標準のGitHub-hosted runnerを使うぶんには基本的に無料なので、ここで扱うのはもっぱらprivateリポジトリの話になる。
僕の場合、Next.jsとTypeScriptで作っている個人開発で、Oxlintによる静的解析、oxfmtによるフォーマットチェック、TypeScriptの型チェック、Vitestによる単体テスト、PlaywrightによるE2Eテスト、そしてアプリケーションのビルドを回している。殊に重いのがPlaywrightのE2Eテストで、ワークフローによっては1回で20分を超える。
以前はさして気にしていなかった。ところが、AIで開発するようになってから、直してpushするまでの回転が明らかに速くなった。CIでエラーが出れば直して再びpushし、そのたびにGitHub Actionsが律儀に走る。気づいたときには、2,000分を使い切っていた。
たった3日である。
むろん、AIだけが原因とは限らない。E2Eテストの実行時間にも、ワークフローの構成にも改善の余地はあるはずだ。ただ、開発の回転数が上がると、これまで問題にならなかったCIのコストが急に無視できなくなる。そのことを身をもって知った次第だ。
関所の手前で持ち物を検める
実行時間を減らすために、まず考えたのはローカルでの事前検証だった。Oxlintのエラー、oxfmtが指摘するフォーマットの崩れ、TypeScriptの型エラー、単体テストの失敗。このあたりは、わざわざGitHub Actionsに頼らなくても手元で検出できる。
こうしたエラーを抱えたままpushすると、関所で突き返され、直してもう一度並ぶことになる。CIがコードの品質を担保するために必要なのは言うまでもない。だが、家を出る前に気づける忘れ物を、関所の役人に指摘してもらう必要はないのだ。
そこで、Gitのpre-push フックを使うことにした。Gitには、commitやpushといった操作に合わせて任意のスクリプトを実行できるGit Hooksという仕組みがある。今回は、その管理にHuskyを使った。
Huskyでpush前に検める
HuskyはGit Hooksを手軽に管理するためのツールだ。Node.jsのプロジェクトなら、普段のパッケージマネージャーから導入できる。ここではpnpmを使う。
まず、Huskyをインストールして初期化する。
pnpm add -D husky
pnpm exec husky init
これで .husky ディレクトリが作られ、package.json に prepare スクリプトが追加される。初期状態では pre-commit フックが生えているが、今回はpushの前に検めたいので pre-push を使う。
次に、push前に走らせるチェックを package.json の1つのスクリプトにまとめておく。記述例は次の通りになる。
{
"scripts": {
"lint": "oxlint",
"format:check": "oxfmt --check",
"typecheck": "tsc --noEmit",
"test:unit": "vitest run",
"check:push": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm test:unit",
"prepare": "husky"
}
}
上記の例では、Lint、フォーマットチェック、型チェック、単体テストを順に実行している。&& でつないでいるので、途中でどれかがコケればその時点で止まる。スクリプト名やコマンドは、プロジェクトの構成に合わせて変えればいい。
最後に、.husky/pre-push というファイルを作り、次の1行を書く。
pnpm check:push
これだけで、git push のたびに先ほどのチェックが走るようになる。たとえば型エラーが残っていれば pnpm typecheck が失敗し、pushは中断される。直してからpushし直せばいい。ローカルで見つかるはずの単純なミスでGitHub Actionsを空回りさせる機会は、これでだいぶ減らせる。
なお、Huskyのフックはスキップもできる。つまり、手前の検めは関所の代わりにはならない。GitHub Actions側のチェックは、これまでどおり残しておく。
全部を手前に持ってくればいいわけではない
では、GitHub Actionsで回しているテストを、いっそ全部pre-push に移せばいいのだろうか。個人的には、それも違うと思っている。
たとえばPlaywrightのE2Eテストは実行時間が長い。push前に毎回走らせれば、今度はローカルでの待ち時間がかさむ。通行料を浮かせるために、家の玄関で20分立ち尽くすのでは本末転倒である。
そういうわけで、役割はおおよそ次のように分けるのが穏当そうだ。
| チェック内容 | pre-push | GitHub Actions |
|---|---|---|
| Oxlint | ○ | ○ |
| oxfmt | ○ | ○ |
| TypeScript型チェック | ○ | ○ |
| 単体テスト | ○ | ○ |
| E2Eテスト | 必要に応じて | ○ |
| ビルド | 必要に応じて | ○ |
もっとも、単体テストが数分以上かかるようなら、変更箇所に関係するテストだけを走らせる仕組みも検討したい。要は、すべてをローカルに移すことではない。GitHub Actionsで検める必要があるものと、手元で済ませられるものを仕分けることなのだ。
ついでに言えば、手元のチェックそのものを速くする手もある。別のプロジェクトでESLint・PrettierをOxlint・Oxfmtに替えたところ、Lintは最大で約37倍、フォーマットは約91倍速くなった。その顛末は「ESLintとPrettierをOxlint・Oxfmtに移行したら、Lintが37倍、フォーマットが91倍速くなった」に書いた。pushのたびに走らせるなら、待ち時間は短いに越したことはない。
そもそも関所に並ばせない
もう1つ、見落としていたことがある。スマホから指示を飛ばしていると、エージェントは修正のたびに律儀にpushし、CIの結果を見て次の手を考えがちだ。僕は画面の小さな結果を眺めるだけなので、それで構わないと思っていた。しかし、これはいわば関所を試験会場代わりに使っているようなもので、通行料がかさむのも当然である。
そこで、スマホから頼むときは、基本的にpushさせないことにした。手元のチェックを通したらコミットまでで止めてもらい、ひと区切りついたところでまとめて1回だけpushさせる。小さな修正を5回pushすれば関所を5回くぐるが、まとめれば1回で済む。
毎回これを書き添えるのは面倒なので、エージェントが読むAGENTS.mdのようなファイルに「pushは指示があるまでしない」と書いておくのが手っ取り早い。人間がpushを我慢するのは難しいが、エージェントは言われたとおりに我慢してくれる。正直なところ、いちばん効いているのはHuskyよりこちらかもしれない。
Huskyで通行料は減るのか
ここは少し注意が要る。Huskyを入れたところで、GitHub Actionsの実行時間そのものが短くなるわけではない。ローカルのチェックを通ってpushすれば、関所ではこれまでどおり検めが行われる。Huskyで減らせるのは、手元で防げたはずのエラーのせいで発生する無駄なCI実行だけである。
ざっくり試算してみる。GitHub Actionsの実行が1回20分で、月に100回pushするとしよう。そのうち10回が、ローカルでも検出できたエラーで失敗し、直して再pushしているとする。この10回を事前に防げて、しかも失敗したCIが毎回20分まるまる走っていたのなら、月200分が浮く。2,000分の無料枠に対して10%ぶんだ。
ただし、これはあくまで机上の計算に過ぎない。実際にはLintや型チェックが先にコケて、20分を待たずにCIが終わることもある。複数のジョブを並列で走らせていれば、課金対象の実行時間の数え方も変わってくる。実際にどれだけ減ったのかは、今後の実行履歴を見比べて確かめるつもりだ。
関所そのものの見直し
3日で枠を使い切ったことを思えば、Huskyだけで根本的に解決するとは考えにくい。CIがこれほど頻繁に走るのなら、GitHub Actions側の構成も見直さざるをえない。いくつか手は考えられる。
まず、古いワークフローをキャンセルする手がある。同じブランチに立て続けにpushした場合、古いコミットに対するCIを最後まで走らせる必要はないことが多い。GitHub Actionsでは、concurrency を設定すれば同じグループの古い実行をキャンセルできる。
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
ただし、デプロイのように途中で止めると困るワークフローには、この設定を雑に当てないよう留意されたし。
次に、変更内容に応じてテストを走らせる手がある。READMEを1行直しただけで、E2Eテストまで回す必要はない。paths などを使えば、特定のファイルが変わったときだけワークフローを起動できる。もっとも、必須チェックに指定しているワークフローをスキップすると、PRのマージ判定に響くことがあるので、設定は慎重にしたい。
キャッシュも効くかもしれない。依存パッケージのインストールやビルドに時間を食っているなら、キャッシュで改善できる余地がある。とりわけpnpmを使っているなら、GitHub Actions側でもキャッシュをきちんと設定しておきたい。
そして最後の手段が、self-hosted runnerだ。自宅のPCなどでGitHub Actionsを動かせば、GitHub-hosted runnerの無料枠を消費せずにCIを回せる。いわば自前の関所を建てるわけだが、マシンの購入費、電気代、セキュリティ、メンテナンスといった維持費がついて回る。
しかも、AIエージェントが大量のCIを呼び出す環境では、self-hosted runnerに移したところで、CPUやメモリ、ディスクI/Oが詰まる可能性がある。DeploAIの記事が描いていたのも、まさにこの話だった。関所の場所を移すだけでは足りず、検めそのものを効率化する必要があるのだ。
書くより検めるほうが高くつく時代
AIコーディングエージェントのおかげで、個人でも以前とは比べものにならない速度でソフトウェアを作れるようになった。一方で、書く速度が上がったぶん、それを検めるCIのコストが新たな悩みの種になっている。現に僕は、2,000分を3日で使い切った。これまで気にも留めなかった開発基盤のコストが、AIを使いはじめた途端に顔を出したのは、なかなかに興味深い。
さしあたりの第一歩として、Huskyを入れ、push前にLint、フォーマットチェック、型チェック、単体テストをローカルで走らせるようにした。あわせて、スマホから頼むときはエージェントにむやみにpushさせないようにした。むろん、これで片がつくわけではない。今後はGitHub Actions側の実行条件やテストの最適化、self-hosted runnerの導入も検討していくつもりだ。
以上、関所の通行料に泣かされた話である。コードを書くことがどんどん安くなっている今、個人開発でも、どう書くかと同じくらい、書かれたコードをどう効率よく検めるかを考えなければならないのだ。