GitHub Actions のジョブ実行時間を実測ベースで削る手順書
cron ジョブの Actions 課金を実測から削る手順。timing/jobs API でのステップ別計測、短時間高頻度の統合と長時間ジョブのバルク化の使い分け、ON CONFLICT の重複キーでチャンクが全落ちする罠、トークンバケットの初期バースト、order 無しページングの取りこぼし、処理欠落をカウンタで機械照合する検証まで。
約4.2万トークンの節約 (API料金換算で約64円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「GitHub Actions のジョブ実行時間を実測ベースで削る手順書」は、業務自動化カテゴリのAI指示書(MDファイル)です。cron ジョブの Actions 課金を実測から削る手順。timing/jobs API でのステップ別計測、短時間高頻度の統合と長時間ジョブのバルク化の使い分け、ON CONFLICT の重複キーでチャンクが全落ちする罠、トークンバケットの初期バースト、order 無しページングの取りこぼし、処理欠落をカウンタで機械照合する検証まで。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約4.2万トークン(API料金換算で約64円)・94%のトークンを節約できます。
- カテゴリ
- 業務自動化
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約4.5万トークン
- この巻物使用時
- 約2,600トークン
- 節約量
- 約4.2万トークン (約64円)
- 更新日
- 2026-08-27
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/github-actions-2/raw を読み込んで、この指示書どおりに実装して"
中身
GitHub Actions のジョブ実行時間を実測ベースで削る手順書
AI エージェントにそのまま読ませて実行させる前提の指示書。 対象は「定期実行(cron)のジョブが長くて Actions 課金が増えている」リポジトリ。
前提知識(ここを外すと的を外す)
GitHub Actions は ジョブ単位で分単位の切り上げ課金。したがってコストは次の2種類に分かれる。
| 型 | 例 | 効く対策 |
|---|---|---|
| 短時間・高頻度 | 8秒の処理を15分毎 | 既存ジョブへステップとして統合(起動オーバーヘッドを1回に集約) |
| 長時間・低頻度 | 40分の日次バッチ | 中身の往復回数を削る(統合しても意味がない) |
「8秒の処理を15分毎」は実計算3時間でも 月766分課金される。新しい cron を作る前にこの表で分類する。
手順1: 実測を取る(推測で着手しない)
# ワークフロー別の直近 run(必ず event=schedule に絞る。手動実行は条件が違う)
gh api "repos/<owner>/<repo>/actions/workflows/<file>.yml/runs?event=schedule&per_page=5" \
--jq '.workflow_runs[]|"\(.id) \(.created_at) \(.conclusion)"'
# 実行時間(一覧APIには無い。timing でしか取れない)
gh api "repos/<owner>/<repo>/actions/runs/<id>/timing" --jq '.run_duration_ms'
# ステップ別の所要時間(どこが遅いかはこれで確定する)
gh api "repos/<owner>/<repo>/actions/runs/<id>/jobs" \
--jq '.jobs[].steps[]|"\((( (.completed_at|fromdateiso8601) - (.started_at|fromdateiso8601) )))s \(.conclusion) \(.name)"'
⚠ 最重要の落とし穴: baseline は「変更直前の schedule 実行」から取る。 古い run を1本だけ見て「この処理は○秒」と決めると桁を間違える。実例では、 古い run で 696秒/526秒だったステップが、直前の run では 264秒/7秒だった。 7秒の処理を「526秒だから」と最適化しても効果はゼロで、実際にそういう変更がマージされた。 最低2〜3本の schedule run を並べ、ばらつきを見てから決める。
billable.UBUNTU.total_msは 0 を返すことがある。run_duration_msを使う。continue-on-error: trueのステップは API 上の conclusion が success になる。 失敗を見たいなら後続のif: steps.X.outcome == 'failure'ステップの結果とログを見る。- 10秒未満で終わっている run は「起動していない」可能性(支出上限・権限)。集計から除外する。
手順2: 効くレバーを選ぶ
レバーA: 短時間・高頻度ジョブを統合する
統合先は cron 式ではなく実発火時刻を実測して選ぶ。GitHub は schedule を大量に間引く。
gh api "repos/<owner>/<repo>/actions/workflows/<file>.yml/runs?event=schedule&created=<YYYY-MM-DD>&per_page=100" \
--jq '.workflow_runs[].created_at'
実例では */15 * * * * の実発火は **41回/日・間隔ランダム(最大90分空く)**で、分は事実上ランダムだった。
だから「15分毎のジョブ内で MIN=$(date -u '+%-M'); [ "$MIN" -ge 15 ] && exit 0 して hourly を維持」は成立しない
(:00-:14 に入るのは約23%だけ=発火回数が半分以下になる)。同じ cron 式を持つジョブ同士だけを統合する。
統合後の注意:
- 前ステップが失敗すると後続が silent skip される。全ステップに
if: ${{ !cancelled() }}を付ける - 旧ファイルは
workflow_dispatchだけ残し、scheduleを消す(二重発火の防止)
レバーB: per-row の往復をバルク化する(長時間ジョブの本命)
「1行ずつ await」を潰す。典型は次の3つ。
- 行ごとの INSERT/UPDATE → 500件チャンクのバルク
- 行ごとの SELECT(存在確認・突合) → 事前に一括取得してメモリ上の Map/Set で突合
- 同じ値に更新する行 → 1回の
IN (...)にまとめる(500件ずつ分割)
エラー分離を落とさないこと。 1行ずつ書いていた頃は「1行の不正データでも他は入る」性質があった。 バルク化で「1行の不正データで500行が丸ごと落ちる」のは劣化なので、 チャンクが失敗したらそのチャンクだけ従来の per-row 実行にフォールバックする形にする。 フォールバックした件数はログに出す(出さないと効いているか分からない)。
レバーC: 外部API律速ならレート制御を入れる
並列度を上げても、外部APIのクォータが天井なら意味がない。実例では並列度を 5→12 に上げても 2,830秒→2,973秒で改善ゼロだった(クォータ律速だったため)。 効いたのは「固定バッチ → ワーカープール」+「共有トークンバケットで平準化」+「呼び出し回数自体を半減」で、 49.6分 → 30.6分になった。
- 固定バッチ(
for i += N { await Promise.all(batch) })はバッチ内の最遅1件を待つ。 共有インデックスから次の仕事を取る N ワーカーに置き換える - クォータの単位を確認する(「1ユーザーあたり毎分」なのか「プロジェクトあたり」なのか)。 ユーザー単位なら、1ユーザーで代理アクセスしている限りそれが天井
- 同じリソースを2回叩いていないか確認する(例: 候補名を順に試して外れたらもう1回叩く実装は、 当たった名前を永続キャッシュすれば呼び出しが半減する)
手順3: 実装時の落とし穴(実測で踏んだもの)
-
ON CONFLICTは同一statement内に同じキーが2回あるとチャンク全体を落とす (cannot affect row a second time)。溜めてから投げる設計ではキーで dedupe しないと 全件フォールバックして効果ゼロになる。実例では 1,347行すべてがフォールバックし、 バルク化の効果が完全に消えていた。dedupe は「同一キーは最後の1件を残す」=逐次 upsert と同じ結果にする。 -
トークンバケットは初期トークンを 0 にする。
tokens = capacityで初期化すると 開始直後に capacity 件を一斉発射してレート上限を超える(実例では 429 が43件中33件、最初の2分に集中)。 -
orderの無い LIMIT/OFFSET ページングは行を取りこぼす。.range(from, to)系で全件取得するなら必ず.order("id")を付ける。 取りこぼした行は「存在しない」と判定され、重複レコードを毎回作るバグになる。 -
テストのために本番経路へエントリポイントガードを足さない。
if (import.meta.url === pathToFileURL(process.argv[1]).href) { main() }は、判定が環境差で false になると 成功扱いで何もせず終了する。ヘルパーを共有モジュールへ切り出してテストする。 -
DDL を伴う最適化(キャッシュ表の追加など)は、表が無い時に警告して続行する作りにする。 コードのマージと DDL 適用は別タイミングになるため、必須にすると定期実行が丸ごと落ちる。
手順4: 「処理が欠けていないこと」を検証する
速くなったかだけでなく、同じ仕事をしたかを機械照合する。
- スクリプトの最終行に
結果: {"processed":N,"inserted":N,"updated":N,"skipped":N,...}のような カウンタ JSON を出す。最適化の前後でキーと意味を変えない(比較できなくなる) - 変更前の run のカウンタをログから取り、変更後と突き合わせる
- 合計が対象件数と一致するか(例:
対象=799に対し成功269 + 変更なし528 + エラー2 = 799) - フォールバック件数が想定内か(
fallbackRows: ... = 0が理想)
手順5: 効果を「請求」で確認する
見積りで完了と言わない。月あたりの課金分は 「1回の実時間を分に切り上げ × 月の実発火回数」で再計算し、 実額は次の請求サイクルで確認する。
発注テンプレ(AI に渡す形)
対象: <owner>/<repo> の <workflow>.yml
目的: ジョブ実行時間の短縮(機能・出力形式は変えない)
実測: 直前の schedule run <id> のステップ別時間を timing/jobs API で取得し、
最も長いステップから着手する
制約:
- 挙動を変えない(判定条件・対象の絞り込み・カウンタの意味)
- エラー分離を落とさない(チャンク失敗時は per-row フォールバック)
- ページングには order を付ける
- 外部APIのクォータ単位を確認し、レート制御を入れる(初期トークン0)
- ON CONFLICT に投げる前にキーで dedupe する
完了条件:
1. 型検査・ユニットテスト・ビルドが通る
2. 実行後のカウンタが変更前と整合(処理欠落なし)
3. run_duration_ms の前後を数値で示す
よくある質問
+「GitHub Actions のジョブ実行時間を実測ベースで削る手順書」とは何ですか?
cron ジョブの Actions 課金を実測から削る手順。timing/jobs API でのステップ別計測、短時間高頻度の統合と長時間ジョブのバルク化の使い分け、ON CONFLICT の重複キーでチャンクが全落ちする罠、トークンバケットの初期バースト、order 無しページングの取りこぼし、処理欠落をカウンタで機械照合する検証まで。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約4.5万トークンかかりますが、この巻物を使えば約2,600トークンで済みます。差し引き約4.2万トークン(API料金換算で約64円)・94%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
Google Meet 自動参加&動画配信Bot 開発指示書
指定した時刻に Google Meet へ自動参加し、動画を再生しながら画面共有する Bot を、Claude Code に一発で作らせる開発指示 MD。朝会の定例動画配信・ウェビナーの自動放送に。
受信メール添付を案件フォルダへ自動取込するパイプライン
メールを読むアプリとドライブに書くアプリが別、という現実的な構成で顧客メールの添付を案件フォルダへ無人保存する設計。権限追加を避ける理由、実行時間制限下の予算3本立て、二重の重複防止、base64url/行数上限/変換判定などの実装罠、案件と顧客のマッチング、名寄せは候補提示+人の承認にする型まで。
Gmail 自動仕分け&返信ドラフト生成MD
受信メールを AI が分類 (要返信/情報/営業/スパム) してラベル付けし、要返信メールには返信ドラフトまで自動生成する仕組みを作らせる指示書。DWD (ドメイン全体委任) 設定手順込み。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア