Claude Media
Claude CodeでDaggerのCIを手元に再現し、失敗を直させる手順

Claude CodeでDaggerのCIを手元に再現し、失敗を直させる手順

DaggerのCheckをdagger checkで手元に再現し、失敗をClaude Codeに直させる手順です。CLAUDE.mdの検証ループ、権限設定、Stop hookでの自動再実行まで書きます。

Claude CodeにCIの失敗を直させるとき、いちばん時間を食うのは「pushして、待って、ログを貼る」往復です。Daggerを使うと、CIで走る検査がdagger checkというコマンドになり、手元で同じ失敗を再現できます。Claude Codeは編集のたびに自分で検査を回し、緑になるまで直せます。

Daggerでは検査が手元のコマンドになる

Daggerは、パイプラインを実コードとコマンドで定義し、pushの前後・手元・クラウドで同じように動かすCIの実行エンジンです。公式ドキュメントはこの仕組みを「スクリプトを、きれいなAPI・実コード・持ち運べるDAG実行エンジンに置き換える」ものと説明しています。

実務で触るのは次の3つです。

  • Check: 検査1件。リント、テスト、整形の確認などが当たる
  • Generator: 生成物を作る処理。結果は変更セットとして返り、適用前にレビューできる
  • Module: CheckやGeneratorを提供する部品。dagger module installで足す

dagger checkは、ワークスペースの全Checkを並列に走らせ、1つでも落ちると非ゼロで終了します。この「終了コードで合否が決まる」性質が、Claude Codeと相性のよい点です。Claudeはコマンドの終了コードと出力だけを見て、直したかどうかを判断できます。

やりたいことコマンド
Checkを一覧するコマンドdagger check -l
全Checkを実行するコマンドdagger check
生成物を作るコマンドdagger generate
関数を直接呼ぶコマンドdagger api call <module> ...

入れて、Checkを一覧にする

Daggerのドキュメントのインストール手順は、バージョンを指定するスクリプトです。手順が指す版はv1.0.0-beta.18で、macOSとLinuxは次のとおりです。

curl -fsSL https://dl.dagger.io/dagger/install.sh | \
  DAGGER_VERSION=1.0.0-beta.18 BIN_DIR=$HOME/.local/bin sh
dagger version

/usr/local/binに書けない環境では、BIN_DIRをホーム配下にしてPATHに通すか、sudo -E付きで実行します。

次に、プロジェクトのルートでワークスペースを初期化します。

dagger init
dagger module recommend   # プロジェクトに合うモジュールを探す
dagger check -l           # Checkの一覧

dagger initは空のdagger.tomlを作ります。このファイルがワークスペースの設定で、リポジトリを使う全員に共通です。非対話モードのinitは、次に流すコマンドとしてdagger module recommendとdagger cloud checks onを表示します。

推奨モジュールが出なければ、プロジェクトが使うツール名で探します。ESLintなら次のとおりです。

dagger module search eslint
dagger module install dagger.io/js/eslint

dagger check -lの*は「モジュールが見つけた全項目」を指します。--allを付けると、Goのパッケージやテストファイルごとに1行ずつ展開されます。--allで行が消えるモジュールは、検査対象を見つけられていません。多くは.golangci.ymlのようなツール側の設定ファイルが無いのが原因です。

失敗したCheckだけを絞って再実行する

Claudeに渡す出力は短いほど、直す精度が上がります。dagger checkは失敗を全部出すのが既定で、最初の失敗で止めたいときは--fail-fastを付けます。

dagger check --fail-fast               # 最初の失敗で残りを止める
dagger check --go --check=test         # goモジュールのtestだけ
dagger check --go-package=store        # 1パッケージ分
dagger check --go-package=store --go-test=TestFixture   # テスト1件

絞り込みの手がかりは、実行結果に出る行そのものです。ドキュメントのGoの例では、結果が次の形のリンクで報告され、これをdagger checkに渡すと同じ選択をもう一度実行できます。

✔ dag://?check=go/packages/tests/test&go-package=store&go-test=TestFixture 5.3s OK

どのフラグで絞れるかが分からないときは、dagger check -l --all --format=cliが行ごとにフラグ形式で出力します。Claudeにこれを実行させれば、失敗した行に対応するフラグを自分で組み立てられます。

4段

CI失敗の手元再現ループ

  1. 1

    全体を回す

    dagger check --fail-fastで、落ちるCheckを1つ特定します。

  2. 2

    失敗した行を絞る

    結果のリンクか--format=cliのフラグで、そのCheckだけを再実行します。

  3. 3

    直す

    Claudeがコードかテストを編集し、絞った形で再実行します。

  4. 4

    全体で確認する

    絞った実行が通ったら、絞り込み無しのdagger checkで他のCheckを壊していないか確かめます。

CLAUDE.mdに検証ループを書く

ループを毎回プロンプトで説明するより、CLAUDE.mdに置いておく方が安定します。次は書き方の一例です(コマンドは上の節のもの)。

## 検証
- コードを変更したら `dagger check --fail-fast` を実行し、緑になるまで直す
- 失敗したCheckは、結果のリンクか `dagger check -l --all --format=cli`
  のフラグで絞って再実行する
- 絞った実行が通ったら、絞り込み無しの `dagger check` で最終確認する
- `dagger.toml` の設定変更と `dagger cloud` 系コマンドは、人の指示があるときだけ行う

最後の1行に理由があります。dagger.tomlはリポジトリの全員に効く設定で、モジュールの設定値もここに保存されます。Claudeがテストを通すために設定を緩める方向へ直すと、CIの意味が変わってしまいます。この種の抜け道への対策は、テストだけ通すハードコードを防ぐプロンプトにまとめています。

権限とStop hookで再実行を自動にする

検査コマンドの許可を絞る

毎回の許可ダイアログを避けたい検査コマンドだけを、許可ルールに入れます。Claude Codeの権限ルールは、Bash(npm run *)のようにサブコマンドの後ろに*を置く書き方で、npm run単体にも一致します。dagger checkにも同じ形が使えます。

{
  "permissions": {
    "allow": ["Bash(dagger check *)"],
    "ask": ["Bash(dagger cloud *)", "Bash(dagger api call *)"]
  }
}

dagger api callを確認に回したのは、Prettierの修正の例がdagger api call -y prettier project --path=. formatのように、変更セットを作業ツリーへ適用する使い方をするからです。dagger cloud系は、アカウントとGitHub連携に関わります。どちらも読み取り専用の検査ではないので、許可リストの外に置く構成です。

終了前に必ず検査を通す

Claudeが「終わりました」と言う前にdagger checkを強制するには、Stopフックを使います。コマンドフックが終了コード2で終わると、Claudeは停止せず、フックのstderrを次の指示として作業を続けます。

.claude/settings.jsonには次のように登録します。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/dagger-check.sh",
            "timeout": 900
          }
        ]
      }
    ]
  }
}

スクリプトの一例です。

#!/bin/sh
# .claude/hooks/dagger-check.sh
# stdinのJSONで、フック起因の継続中かを判定する
if grep -q '"stop_hook_active": *true'; then
  exit 0
fi
out=$(dagger check --fail-fast 2>&1) && exit 0
printf '%s\n' "$out" | tail -n 40 >&2
exit 2

stop_hook_activeは、Claude Codeがすでにストップフックの結果として動き続けているときにtrueになる項目です。ここで0を返すと、フックによる差し戻しは1回で終わり、直した後の再検査はしません。直るまで繰り返させたいときはこの分岐を外し、連続継続の上限(8回)に任せる方法もあります。上限の仕組みはStop hookのブロック上限で扱っています。

出力はtailで40行に切っています。全文をClaudeに渡すと、ログの長さがコンテキストを圧迫します。timeoutの既定は600秒なので、テストが長いプロジェクトでは余裕を持たせた値にします。

直せるCheckと、人が決めるCheck

Checkは「落ちる」だけで、直す手段は別に用意されています。ここを取り違えると、Claudeが空回りします。

切り分け

失敗の種類と直し方

  • 整形の確認(Prettier)

    Prettierモジュールを入れると、リポジトリ内のProjectごとにformat-checkが付きます。直すときはdagger api call -y prettier project --path=. formatで整形を適用します。

  • リント(ESLint)

    ESLintモジュールは各Projectにfix関数を持ち、eslint --fixの結果を変更セットで返します。fixは通常の関数でありCheckではないので、dagger checkの中では走りません。

  • 生成物のずれ(stale)

    Generatorのstaleは、変更セットが空であれば通るCheckです。go generateが追跡中のファイルを変えるなら失敗するため、直すのはdagger generateの結果をレビューして取り込む作業になります。

  • テストの失敗

    Goモジュールはテスト1件ごとに結果を出すので、失敗した名前がそのままClaudeへの手がかりになります。

整形やリントの失敗は、機械的な修正コマンドで直る失敗です。Claudeには、まず修正の関数を呼ばせ、残った失敗だけをコードの修正として扱わせると、編集量が減ります。

push後は同じCheckが走る

CIの側は、ドキュメントによればGitHub Actionsの1行で足ります。

- run: dagger check

ワークフローを書かずに済ませたいときは、Dagger CloudのCloud Checksがあります。dagger cloud checks onでGitHubリポジトリを連携すると、pushやプルリクエストのたびにCheckが走ります。対象はGitHubのリポジトリで、gitのoriginがそのリポジトリを指している必要があります。実行はdagger activityで確かめられます。ドキュメントは、赤になったら手元でdagger checkを実行して直し、コミットしてpushする流れを案内しています。

この構造の利点は、手元とCIで検査の定義が1つしか無いことです。手元で緑にしたものがCIで落ちる原因は、定義のずれではなく、環境差や外部サービスの状態に絞られます。合否を終了コードでCIに渡す考え方は、plugin evalの結果をCIの合否に使う方法と共通です。

つまずきやすい点

  • GoのテストがSKIPになる: テストがサービスの不在でスキップしても、Checkは失敗にならず通ります。結果にskippedの件数が出ているなら、データベースなどをワークスペースに配線する段階がまだ残っています
  • フラグが-で始まる設定値: dagger module settings go goflags -- -tags=extendedのように、値の前に--が要ります。付けないとCLIが値を自分のフラグとして読みます
  • dagger.lock: ワークスペースの設定とは別に、イメージとGitの参照をピン留めするファイルです。dagger.toml、dagger.lock、.daggerはコミットして共有します。Daggerのコマンドが勝手に追記することがあるので、Claudeの変更に含まれていても異常ではありません
  • 設定の個人差: 個人の設定は~/.config/dagger/config.tomlに置けます。リポジトリのdagger.tomlを書き換えずに、-gを付けて保存できます

IaCの検証でも同じ発想を使えます。実際の運用に近い題材は、Terraformのplan差分まで検証させる手順にあります。

この記事を共有:XはてブLinkedIn