CI 失敗→複製錯誤→手動修→再等 10 分鐘——這個循環可以自動化嗎?

🌏 Read this article in English


CI 又紅了

你剛 push 一個 commit。

切回瀏覽器,打開 GitHub Actions,看到那個熟悉的紅色叉叉。

點進去,等日誌載入,捲到最下面——

error: 'response' is defined but never used  no-unused-vars

一個沒用到的 import。

你嘆了口氣,切回 IDE,刪掉那行,commit,push,再等 5 分鐘。

這種事每週發生幾次?每次 10-15 分鐘。

如果一天遇到 3 次,一個月就是 15-20 小時——花在任何人都能修的問題上。

這篇不是要教你寫更好的 code 來避免 CI 失敗(那是另一個話題)。

這篇要講的是:對於那些有確定性解法的機械性失敗,為什麼不讓 AI 自動修?


⚡ 3 秒重點

  • 痛點:CI 失敗的修復循環(等待→複製→修→等待)每次 10-15 分鐘,多數是機械性問題
  • 解法:用 Claude Code GitHub Action 自動偵測並修復 lint、type error、格式問題
  • 安全關鍵:允許清單(可自動修)vs 拒絕清單(必須人工審查)是核心設計
  • 適用:有 CI pipeline 的團隊,lint/format/type check 經常失敗
  • 不適用:CI 失敗主要是邏輯錯誤或安全相關的場景

你的時間花在哪裡:兩種 CI 失敗

不是所有 CI 失敗都一樣。

區分這兩種類型,是決定「該不該自動化」的前提:

機械性失敗 邏輯性失敗
範例 unused import、格式不一致、typo 商業邏輯錯誤、race condition
解法確定性 高——幾乎只有一種正確做法 低——需要理解上下文才能判斷
修復時間 幾秒(但等 CI 要 5-10 分鐘) 幾分鐘到幾小時
風險 極低——刪掉沒用的 import 不會壞 高——改錯可能引入新 bug
誰能修 任何人 需要熟悉 codebase 的人

Key Insight: 機械性失敗有確定性的解法極低的風險——這正是自動化的甜蜜點。不是所有 CI 失敗都該自動修,但機械性失敗是明確值得的。

你可能會問:那為什麼不一開始就用 pre-commit hook 擋住?

好問題。Pre-commit hook 確實能擋一些,但實務上:

  • 有些檢查太慢不適合放在 pre-commit(type check、integration test)
  • 團隊成員可能用 --no-verify 跳過
  • CI 環境和本機環境不同,有些錯誤只在 CI 出現

所以 CI 端的自動修復不是取代 pre-commit,而是補上最後一道防線


YOLO Push:一個看似魯莽的名字

這個概念來自 2026 年 1 月 Claude Code Meetup Taipei,由 Anthropic 的 Oliver Wang 分享。

他們在 Anthropic 內部有一個實踐:CI 失敗時,自動觸發 Claude Code 修復

名字叫 YOLO Push——「You Only Live Once」。

聽起來很隨便。

但底層架構其實相當嚴謹。

YOLO 的精神不是「隨便修修就好」,而是:

對於已知的機械性問題,不需要人類浪費時間在「等待→複製→修→等待」的循環上。

核心安全機制是允許清單和拒絕清單——嚴格界定哪些可以自動修、哪些不行。

下一段細講。


安全邊界:允許清單 vs 拒絕清單

這是整個方案最關鍵的設計決策。

不是「要不要自動修」的問題,而是「哪些可以自動修、哪些絕對不行」。

允許清單 ✅(可自動修復)

類型 範例
Lint 錯誤 unused import、missing semicolon
格式問題 indentation、trailing whitespace
Type error(簡單) missing type annotation、wrong return type
變數命名 typo in variable name

拒絕清單 ❌(必須人工審查)

類型 為什麼不能自動修
安全漏洞 修復方式可能影響安全模型
身分驗證/授權 改錯一行可能開放未授權存取
資料存取邏輯 可能洩漏或損壞使用者資料
商業邏輯 需要 domain knowledge 才能判斷
合規相關 法規要求需要人工確認

Key Insight: 拒絕清單的項目一律需要人工審查,無論 AI 的修正看起來多正確。這不是技術問題,而是風險管理:機械性問題修錯了最多 CI 再跑一次,安全問題修錯了可能是資安事件。

品味 vs 正確性

還有一個灰色地帶值得注意:

客觀(適合自動修) 主觀(需要人類判斷)
語法錯誤 函式該不該抽取
Type 不符 註解是否有價值
格式不一致 抽象層是否過度設計
unused import 命名是否夠好

Claude 的訓練資料來自各種不同風格的 repo。新創團隊的好風格不等於金融系統的好風格。

可以自動化的是「正確性」,不是「品味」。


實作:兩種方式

方式 A:本機 CLI + GitHub MCP(最簡單)

🟢 難度:簡單 ⏱️ 預估時間:5 分鐘 📋 前置需求:已安裝 Claude Code CLI、GitHub MCP server

GitHub MCP 設定:如果還沒設定,在 Claude Code 中執行 /add-mcp-server 選擇 GitHub,或手動在 ~/.claude/mcp.json 加入:

{
  "mcpServers": {
    "github": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"]
    }
  }
}

需要先設定 GITHUB_TOKEN 環境變數(GitHub Personal Access Token)。

如果你只是想快速修一下 CI,不需要完整的自動化 pipeline,最簡單的方式:

claude "CI is failing on main. Figure out why, fix it, \
       commit & push, and monitor to be sure your fix worked."

Claude Code 會:

  1. 透過 GitHub MCP 檢視 CI 失敗的 log
  2. 分析錯誤原因
  3. 修改程式碼
  4. 在本機跑 lint/build 確認
  5. Commit + push
  6. 持續監控 CI 是否通過
  7. 如果還是失敗,繼續嘗試修復

優點:零設定、立即可用。

缺點:需要你手動觸發、會佔用本機 Claude Code session。

Tip:用 git worktree 可以避免 Claude 修 CI 時佔用你的工作目錄。


方式 B:GitHub Action 自動觸發(完整自動化)

🟡 難度:中等 ⏱️ 預估時間:30 分鐘 📋 前置需求:GitHub repo admin 權限、Anthropic API key

這是 Anthropic 官方提供的 ci-failure-auto-fix.yml 範例,當 CI 失敗時自動觸發 Claude 修復。

Step 1:安裝 Claude GitHub App

Claude GitHub App 提供 repo 讀取權限和 PR 互動能力,是 claude-code-action 運作的基礎。

/install-github-app

或手動安裝:github.com/apps/claude

安裝後,確認已授權給目標 repo。

Step 2:設定 API Key

在 GitHub repo → Settings → Secrets → Actions 中新增:

  • ANTHROPIC_API_KEY:你的 Claude API key

Step 3:建立 Workflow 檔案

.github/workflows/auto-fix-ci.yml 中:

怎麼找你的 CI workflow 名稱? 打開你的 .github/workflows/ 目錄,找到 CI 的 YAML 檔,看最上面的 name: 欄位。例如 name: CI 就填 "CI"name: Build and Test 就填 "Build and Test"

name: Auto Fix CI Failures

on:
  workflow_run:
    workflows: ["CI"]  # 改成你的 CI workflow 的 name 欄位值
    types:
      - completed

permissions:
  contents: write
  pull-requests: write
  actions: read
  issues: write
  id-token: write

jobs:
  auto-fix:
    # 三個條件:CI 失敗 + 有對應 PR + 不是自動修復的 branch(防無限迴圈)
    if: |
      github.event.workflow_run.conclusion == 'failure' &&
      github.event.workflow_run.pull_requests[0] &&
      !startsWith(github.event.workflow_run.head_branch, 'claude-auto-fix-ci-')
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          ref: ${{ github.event.workflow_run.head_branch }}
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Setup git identity
        run: |
          git config --global user.email "claude[bot]@users.noreply.github.com"
          git config --global user.name "claude[bot]"

      - name: Create fix branch
        id: branch
        run: |
          BRANCH_NAME="claude-auto-fix-ci-${{ github.event.workflow_run.head_branch }}-${{ github.run_id }}"
          git checkout -b "$BRANCH_NAME"
          echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT

      - name: Get CI failure details
        id: failure_details
        uses: actions/github-script@v7
        with:
          script: |
            const run = await github.rest.actions.getWorkflowRun({
              owner: context.repo.owner,
              repo: context.repo.repo,
              run_id: ${{ github.event.workflow_run.id }}
            });

            const jobs = await github.rest.actions.listJobsForWorkflowRun({
              owner: context.repo.owner,
              repo: context.repo.repo,
              run_id: ${{ github.event.workflow_run.id }}
            });

            const failedJobs = jobs.data.jobs
              .filter(job => job.conclusion === 'failure');

            let errorLogs = [];
            for (const job of failedJobs) {
              const logs = await github.rest.actions
                .downloadJobLogsForWorkflowRun({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  job_id: job.id
                });
              errorLogs.push({
                jobName: job.name,
                logs: logs.data
              });
            }

            return {
              runUrl: run.data.html_url,
              failedJobs: failedJobs.map(j => j.name),
              errorLogs: errorLogs
            };

      - name: Fix CI failures with Claude
        uses: anthropics/claude-code-action@v1
        with:
          prompt: |
            /fix-ci
            CI workflow failed.
            Failed Run: ${{ fromJSON(steps.failure_details.outputs.result).runUrl }}
            Failed Jobs: ${{ join(fromJSON(steps.failure_details.outputs.result).failedJobs, ', ') }}
            PR Number: ${{ github.event.workflow_run.pull_requests[0].number }}
            Branch Name: ${{ steps.branch.outputs.branch_name }}
            Base Branch: ${{ github.event.workflow_run.head_branch }}
            Repository: ${{ github.repository }}

            Error logs:
            ${{ toJSON(fromJSON(steps.failure_details.outputs.result).errorLogs) }}

            ONLY fix these types of issues:
            - Linting errors (unused imports, missing semicolons)
            - Type errors (missing annotations, wrong types)
            - Formatting issues (indentation, whitespace)
            - Simple typos in variable names

            DO NOT fix anything related to:
            - Security vulnerabilities
            - Authentication or authorization logic
            - Business logic or data access
            - Test assertions (the test might be correct)

            If the issue is NOT in the allowed list,
            just comment "Requires human review" and stop.
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: >-
            --max-turns 6
            --allowedTools
            "Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash(git:*),Bash(npm:*),Bash(npx:*),Bash(gh:*),Bash(bun:*)"

Step 4:驗證 Workflow 運作

把 YAML 檔 commit 到 repo 的 main branch 後:

  1. 開一個 PR,故意加入一個 lint error(例如 const unused = 123;
  2. 等 CI 失敗
  3. 觀察 GitHub Actions 頁面,應該會看到 Auto Fix CI Failures workflow 被觸發
  4. Claude 修完後會建立一個 PR,標題帶有 claude-auto-fix-ci- 前綴
  5. 確認修正內容合理後 merge

如果沒有觸發,檢查:

  • workflows: 的名稱是否和 CI workflow 的 name: 欄位完全一致
  • Claude GitHub App 是否已授權給這個 repo
  • ANTHROPIC_API_KEY secret 是否已設定

關鍵設計解說

防無限迴圈!startsWith(github.event.workflow_run.head_branch, 'claude-auto-fix-ci-') 確保自動修復的 branch 不會再觸發自動修復。

/fix-ci 技能:Claude Code 內建的 CI 修復技能,搭配 PR Number、Branch Name 等 context 變數,讓 Claude 能精確定位問題位置。

工具限制--allowedTools 嚴格限制 Claude 只能使用檔案編輯、git 操作和 gh CLI,不能執行任意指令。Bash(gh:*) 讓 Claude 能讀取 PR 資訊和 CI log。

拒絕清單寫在 prompt 裡:明確告訴 Claude 什麼不能修,遇到拒絕清單的問題就停下來。

獨立 branch + 自動 PR:Workflow 先建立 claude-auto-fix-ci-* branch,Claude 修完後會透過 Bash(gh:*) 使用 gh pr create 建立 PR 回原 branch。你只需要 review 和 merge,不需要手動操作。


常見錯誤

錯誤 症狀 解法
無限迴圈 自動修復觸發新的 CI → 又失敗 → 又觸發 加 branch prefix 檢查(範例已含)
API 費用爆增 每次 CI 失敗都呼叫 API --max-turns 限制、設 workflow timeout
修錯東西 Claude 改了不該改的 用獨立 branch + PR review,不直接 push
權限不足 Action 無法 push 或建立 PR 確認 permissions 設定正確

導入建議:從保守到積極

不需要一步到位。建議分三個階段導入:

Phase 1:只修 lint 和 format(🟢 低風險)

# prompt 中只允許
ONLY fix: Linting errors, Formatting issues
DO NOT fix: Everything else

效果:最安全的起點。lint 和 format 的修復幾乎不會引入問題。

追蹤指標

  • CI 失敗中有多少 % 被自動解決?
  • 自動修復有沒有引入新問題?

Phase 2:加入 type error(🟡 中等風險)

# 擴大允許範圍
ONLY fix: Linting errors, Formatting issues, Simple type errors

注意:type error 的修復有時不只一種做法。建議這階段改成建立 PR 而非直接 push,讓人類 review。

Phase 3:PR 自動修復(🔴 需審查機制)

進階玩法——當 linter bot 或安全掃描工具在 PR 留下 comment 時,自動修復。

GitHub Marketplace 上有 PR Autofix with Claude Code action 可以做到這件事。

建議:即使到了 Phase 3,拒絕清單的項目仍然永遠需要人工審查

Key Insight: 導入自動化最大的風險不是技術,而是信任邊界的管理。從允許清單最小的範圍開始,用數據證明可靠後再逐步擴大——這跟管理 AI Agent 的原則一樣。


成本考量

用之前要知道的事:

項目 說明
GitHub Actions 分鐘數 每次觸發消耗 runner 時間,視修復複雜度而定
Anthropic API 費用 每次呼叫消耗 token,Claude Sonnet 較便宜
設定時間 方式 A 幾乎為零,方式 B 約 30 分鐘

省錢技巧

  • 調整 --max-turns(範例設 6),避免 Claude 無限嘗試
  • 設定 workflow timeout-minutes: 10
  • claude_args: "--model claude-sonnet-4-6" 選擇較便宜的模型
  • 考慮用 GitHub concurrency 控制,限制同時跑的修復數量

下一步

如果你想試試看:

今天就能做的(5 分鐘):

  1. 在本機用 Claude Code CLI,下次 CI 失敗時試一次 claude "CI is failing..."
  2. 觀察 Claude 的修復流程和品質

這週可以做的(30 分鐘):

  1. 在一個低風險 repo 部署方式 B 的 workflow
  2. 只允許 lint 和 format 修復(Phase 1)
  3. 跑兩週,追蹤成功率

不確定要不要做? 回答這 3 個問題:

  1. 你的 CI 失敗中,有多少 % 是 lint/format/typo?(如果 < 20%,效益有限)
  2. 你的團隊對 AI 自動 commit 的接受度?(需要溝通)
  3. 你的 CI pipeline 跑一次要多久?(越久,自動修復省的時間越多)

說到底,YOLO Push 的精神不是「讓 AI 取代你」。

是把你從「等待→複製→修→等待」的循環裡解放出來,去做真正需要你判斷的事。


延伸閱讀

想深入了解 Claude Code 在工程實踐上的應用?這些文章可能有幫助:


Sources

Claude Code GitHub Action

CI 自動修復實踐

官方文件

Meetup 來源

  • Claude Code Meetup Taipei(2026-01-14)- Oliver Wang 分享

    YOLO Push 概念的原始來源。Anthropic 內部每天處理數百筆 commit 的機械性 CI 修復。

Leave a Comment