🌏 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 會:
- 透過 GitHub MCP 檢視 CI 失敗的 log
- 分析錯誤原因
- 修改程式碼
- 在本機跑 lint/build 確認
- Commit + push
- 持續監控 CI 是否通過
- 如果還是失敗,繼續嘗試修復
優點:零設定、立即可用。
缺點:需要你手動觸發、會佔用本機 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 後:
- 開一個 PR,故意加入一個 lint error(例如
const unused = 123;) - 等 CI 失敗
- 觀察 GitHub Actions 頁面,應該會看到
Auto Fix CI Failuresworkflow 被觸發 - Claude 修完後會建立一個 PR,標題帶有
claude-auto-fix-ci-前綴 - 確認修正內容合理後 merge
如果沒有觸發,檢查:
workflows:的名稱是否和 CI workflow 的name:欄位完全一致- Claude GitHub App 是否已授權給這個 repo
ANTHROPIC_API_KEYsecret 是否已設定
關鍵設計解說
防無限迴圈:!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 分鐘):
- 在本機用 Claude Code CLI,下次 CI 失敗時試一次
claude "CI is failing..." - 觀察 Claude 的修復流程和品質
這週可以做的(30 分鐘):
- 在一個低風險 repo 部署方式 B 的 workflow
- 只允許 lint 和 format 修復(Phase 1)
- 跑兩週,追蹤成功率
不確定要不要做? 回答這 3 個問題:
- 你的 CI 失敗中,有多少 % 是 lint/format/typo?(如果 < 20%,效益有限)
- 你的團隊對 AI 自動 commit 的接受度?(需要溝通)
- 你的 CI pipeline 跑一次要多久?(越久,自動修復省的時間越多)
⸻
說到底,YOLO Push 的精神不是「讓 AI 取代你」。
是把你從「等待→複製→修→等待」的循環裡解放出來,去做真正需要你判斷的事。
延伸閱讀
想深入了解 Claude Code 在工程實踐上的應用?這些文章可能有幫助:
-
Intent-driven Development:我用 Claude Code 後的體悟
從「寫程式」轉變為「描述意圖」的開發模式,是理解 Claude Code 的基礎。
-
AI 工具的效益不只取決於工具本身,還取決於團隊的工程成熟度。
-
Agent 很慢、LLM 不夠聰明——什麼時候該用多步驟處理?
CI 自動修復本質上是一種 Agent 行為——理解 Agent 的適用場景有助於判斷何時該自動化。
Sources
Claude Code GitHub Action
- anthropics/claude-code-action (2026) | Archive
Anthropic 官方 GitHub Action,v1.0 GA 版本。含 CI 自動修復的完整範例 workflow。
CI 自動修復實踐
- Ask Claude Code to fix CI – Chris Dzombak (2025) | Archive
個人開發者使用 Claude Code + GitHub MCP 自動修復 CI 的實戰分享。方式 A 的靈感來源。
官方文件
- Claude Code GitHub Actions – Official Docs (2026)
官方設定指南,含認證方式(Direct API / AWS Bedrock / Google Vertex AI)和最佳實踐。
Meetup 來源
- Claude Code Meetup Taipei(2026-01-14)- Oliver Wang 分享
YOLO Push 概念的原始來源。Anthropic 內部每天處理數百筆 commit 的機械性 CI 修復。