Gemini CLI CI Skill:监控 GitHub Actions 失败并自动在本地复现的 Skill 实现剖析
本文围绕 gemini-cli 仓库中的 CI Skill 定义文件 展开,讲解这个专为 Gemini CLI 定制的 "CI Replicate & Status" 技能如何自动发现分支上的 GitHub Actions 运行、以 fail-fast 策略轮询状态,并在检测到失败时立即生成可本地执行的测试/lint 命令。读完本文,你能完整理解该 Skill 的工作流程、命令行参数、失败分类规则,以及底层脚本 ci.mjs 的运行发现、日志抓取与噪音过滤机制,从而掌握"远程 CI 失败 → 本地一键复现"这一缩短修复周期的实战方案。
1. Skill 定位:从远程 CI 到本地复现的自动化桥梁
SKILL.md 的 frontmatter 将该技能描述为:提供高性能、fail-fast 的 GitHub Actions 工作流监控,以及对 CI 失败的自动化本地验证,并且"运行发现是自动的——只需提供分支名"。原文档列出了三大核心能力:
- Automatic Replication(自动复现):自动监控 CI,一旦检测到失败,立即在本地执行脚本建议的测试或 lint 命令(输出中标记为 🚀)。
- Real-time Monitoring(实时监控):对当前分支上所有并发工作流提供聚合状态行。
- Fail-Fast Triage(快速分诊):在第一个 job 失败时立即停止轮询,输出结构化失败报告。
这个 Skill 遵循 Gemini CLI 的 Agent Skills 机制(可参见 docs/cli/skills.md 中对技能发现与管理的说明),由 SKILL.md 声明用途,由 scripts/ 目录下的脚本承担实际的 CLI 调用工作。它的核心价值在于:把"看 CI → 找失败 job → 翻日志 → 定位失败测试文件 → 本地重跑"这条人工链路压缩成一次命令调用。
2. 两种工作流:replicate(默认)与 status
原文档定义了两种用法,底层调用的是同一个脚本,区别在于交互深度:
2.1 CI Replicate(replicate)——默认路径
node .gemini/skills/ci/scripts/ci.mjs [branch]
- 行为:触发后 Agent 监控 CI,一旦检测到失败,立即自动执行所有建议的测试或 lint 命令(标记 🚀),无需人工干预。
- 运行发现:脚本自动找到该分支最新的活动或近期 run,不要手动用
gh run list搜索 run ID,直接给分支名即可。 - 目标:无人工介入地复现失败,然后进入代码分析与修复阶段。
2.2 CI Status(status)——手动三步走
node .gemini/skills/ci/scripts/ci.mjs [branch] [run_id]
- 第二个参数
[run_id]用于指定某个特定的历史 run;原文档明确说明,除非用户明确要查看某个历史 run,否则不应手动搜索 run_id,只需提供分支名。 - Step 1(Monitor):带分支名执行工具。
- Step 2(Extract):从输出中提取建议的
npm test或npm run lint命令(标记 🚀)。 - Step 3(Reproduce):在本地执行这些命令确认失败。
- 行为:脚本每 15 秒轮询一次;检测到失败后退出并打印结构化报告,附带需要在本地执行的确切命令。
3. 参数与仓库识别:脚本入口逻辑
从 ci.mjs 的入口代码可以看到参数解析与仓库识别策略:
const BRANCH =
process.argv[2] || execSync('git branch --show-current').toString().trim();
const RUN_ID_OVERRIDE = process.argv[3];
- 第一个位置参数是分支名,缺省时自动取当前 git 分支(
git branch --show-current); - 第二个位置参数是可选的
run_id覆盖值。
仓库标识(REPO)通过解析 git remote get-url origin 得到,剥离 github.com/ 或 github.com: 前缀和 .git 后缀;若没有 origin 远端则回退到 google-gemini/gemini-cli(ci.mjs)。所有 gh 调用被封装在 runGh() 中:stdout 被管道捕获,stderr 丢弃,任何异常都返回 null 而非抛出——这让脚本在 gh 未认证或网络抖动时能优雅降级,而不是中途崩溃。
4. 运行发现(Discovery):双通道定位目标 run
monitor() 函数实现了原文档承诺的"自动运行发现",实际上用了两条互补的通道(ci.mjs):
通道 1:分支 run 列表。 执行
gh run list --branch "<branch>" --limit 10 --json databaseId,status,workflowName,createdAt
优先选择 status !== 'completed' 的活动 run;若没有活动 run,则取最新一条的创建时间,挑出与其相差 60 秒以内 的 run(覆盖一次推送并行触发多个 workflow 的场景)。
通道 2:commit status 反查。 通过 git rev-parse <branch> 拿到 HEAD SHA,再调用
gh api repos/<REPO>/commits/<sha>/status
从 target_url 中包含 actions/runs/ 的状态记录里用正则 actions\/runs\/(\d+) 提取 run ID,并去重合并进目标列表。这条通道用于处理"间接/链式触发"的 run——即不直接挂在分支名下、但通过 commit 状态关联到的 workflow run。
最终脚本会打印一行 Monitoring workflows: <去重后的 workflow 名列表>,若两个通道都找不到 run,则输出 No runs found for branch <branch>. 并以退出码 0 正常结束(例如本地分支从未推送过 CI)。
5. 轮询循环:聚合状态行与 fail-fast 退出
主循环(ci.mjs)每轮对所有目标 run 做两件事:
gh run view <id> --json databaseId,status,conclusion,workflowName获取 run 级状态,status !== 'completed'即视为仍有活动 run;gh run view <id> --json jobs拉取全部 job,按 job 的status/conclusion聚合四个计数器:in_progress→ running、queued→ queued、success→ passed、failure→ failed。
无失败时,脚本用 \r 原地刷新一行聚合状态,这正是原文档所说 "Aggregated status line for all concurrent workflows" 的实现:
⏳ Monitoring 3 runs... 12/20 jobs (10 passed, 0 failed, 2 running, 0 queued)
当所有 run 完成且无失败时打印 ✅ All workflows passed! 并以退出码 0 退出;一旦某轮发现 conclusion === 'failure' 的 job,立即进入失败处理分支并 以退出码 1 退出——这就是 fail-fast 语义:不等其余 job 跑完,第一时间把结构化报告交给 Agent。轮询间隔为 15 秒(setTimeout(r, 15000)),与原文档 "poll every 15 seconds" 的描述一致。
6. 日志抓取与噪音过滤:从原始日志到结构化报告
对每个失败 job,脚本调用 fetchFailuresViaApi(jobId)(ci.mjs):
gh api repos/<REPO>/actions/jobs/<jobId>/logs | \
grep -iE " FAIL |❌|ERROR|Lint failed|Build failed|Exception|failed with exit code"
即只保留命中失败特征的行,maxBuffer 上限设为 10MB。随后 isNoise() 再过滤一轮噪音,命中以下任意特征的行被丢弃(ci.mjs):
* [new branch](git fetch 输出)npm warn(NPM 警告)fetching updatesnode:internal/errorsat(stack trace 行)checkexecsyncerrornode_modules
这正是 SKILL.md 中 "Noise Filtering" 一节所说的:底层脚本自动过滤 Git 日志、NPM 警告、堆栈冗余,Agent 应聚焦于工具提供的 "Structured Failure Report"。
7. 失败归类与本地命令生成
7.1 失败行的两级归类
对过滤后的每一行失败输出,脚本先尝试 extractTestFile():清除 #[]()|、<...> 等标记字符后,用正则 ([\w\/._-]+\.test\.[jt]sx?) 提取 vitest/jest 风格的测试文件路径。若未提取到文件,则按关键词兜底归类:行内含 lint → Lint Error,含 build → Build Error,否则 Unknown File(ci.mjs)。
若整个 job 日志一条特征行都没抓到(failures 为空),脚本转而查看 job 的 steps,找到第一个 conclusion === 'failure' 的 step 名,同样按 lint/build 关键词归类为 Lint Error / Build Error / Job Error,并记录 <job.name>: Failed at step "<step>"(ci.mjs)。
7.2 生成 npm test -w <workspace> -- <files> 命令
generateTestCommand()(ci.mjs)把"失败文件 → 本地测试命令"的转换规则与 monorepo 结构对齐:
| 失败文件前缀 | npm workspace | 命令形式 |
|---|---|---|
packages/core/... |
@google/gemini-cli-core |
npm test -w @google/gemini-cli-core -- <相对文件...> |
packages/cli/... |
@google/gemini-cli |
npm test -w @google/gemini-cli -- <相对文件...> |
Job Error / Unknown File / Build Error / Lint Error |
(跳过) | 不生成测试命令 |
工作区名与 packages/core/package.json、packages/cli/package.json 中的包名完全对应;同一 workspace 的多个失败文件合并进一条命令,多个 workspace 之间用 && 连接。若所有失败都是 lint 类,则输出兜底命令 npm run lint:all。最终报告形如:
❌ Failures detected across 2 job(s). Stopping monitor...
--- Structured Failure Report (Noise Filtered) ---
Category/File: packages/cli/src/ui/components/Editor.test.tsx
- Editor > renders placeholder text
...
🚀 Run this to verify fixes:
npm test -w @google/gemini-cli -- src/ui/components/Editor.test.tsx.x
---------------------------------
报告细节也有节制:每条失败描述超过 500 字符会被截断并标注 [TRUNCATED],每个文件最多展示 10 条、多余部分折叠为 ... and N more,保证输出紧凑、可被 Agent 直接消费。
7.3 与仓库脚本的对应关系
建议命令在根 package.json 中都有真实落点:
npm test即npm run test --workspaces --if-present && npm run test:sea-launch,所以npm test -w <pkg> -- <path>会按 npm workspaces 语义只跑指定包;npm run lint:all对应node scripts/lint.js,即 scripts/lint.js —— 它按平台自动安装并运行 actionlint(检查 GitHub Actions YAML)、shellcheck、yamllint,所以该命令能覆盖 CI 中 lint job 的主要检查面;- 另有
npm run lint是eslint . --cache --max-warnings 0(--max-warnings 0意味着警告也会让 lint 失败),对应纯 JS/TS 代码的 lint 失败。
8. 失败分类与处理动作(原文档规则)
SKILL.md 的 "Failure Categories & Actions" 一节给出 Agent 面对四类失败的标准动作,与脚本的归类一一对应:
| 类别 | Agent 应执行的动作 |
|---|---|
| Test Failures | 运行脚本建议的具体 npm test -w <pkg> -- <path> 命令 |
| Lint Errors | 运行 npm run lint:all 或对应包的 lint 命令 |
| Build Errors | 检查 tsc 输出或构建日志,解决编译问题(对应仓库根 npm run typecheck 与 npm run build) |
| Job Errors | 用 gh run view --job <job_id> --log 排查基础设施或环境准备失败 |
其中 "Job Errors" 是脚本在抓不到任何测试/lint/build 特征、只能定位到失败 step 时的兜底分类,此时自动化复现链路让位于人工(或 Agent 主动)看完整 job 日志。
9. 适用前提与实践要点
结合脚本实现,使用该 Skill 需要注意以下前提与限制:
- 依赖
ghCLI 且已认证:所有发现、轮询、日志抓取都经gh;runGh()对失败返回null,若gh完全不可用,脚本会因找不到 run 而直接以No runs found退出(退出码 0),不会报错也不会轮询。 - 要求 GitHub 远端:
REPO从git remote get-url origin解析,非 GitHub 托管的远端无法工作;无 origin 时回退到默认仓库名,仅适用于该仓库的 fork 本地场景。 - Node 版本:仓库根 package.json 要求
node >= 20.0.0,脚本仅用node:child_process,无额外依赖,可直接node .gemini/skills/ci/scripts/ci.mjs运行。 - run 发现的时间窗:分支无活动 run 时,只合并与最新 run 创建时间相差 60 秒以内的 run;commit status 通道再补上间接触发的 run。因此"先
git push再触发脚本"是最稳的用法,脚本启动时 CI 需要已被触发。 - 测试文件提取的假设:
extractTestFile()只识别.test.ts/.test.tsx/.test.js/.test.jsx命名,与仓库内 vitest 的测试命名一致;其他命名约定的失败不会进入精确文件级命令,只会落入Unknown File。 - 退出码语义:失败退出码 1、全绿退出码 0,可直接作为 CI 复现链路中的可判断信号,供上层 Agent 决定"复现成功 → 进入修复"还是"本地通过 → 可能是环境差异,转看 Job Errors"。
小结
这个 CI Skill 的设计可以概括为三层:上层是 SKILL.md 声明的行为契约(自动复现、fail-fast、禁止手动搜 run ID),中层是 ci.mjs 的双通道 run 发现 + 15 秒轮询 + 聚合状态行,底层是 grep 特征抓取 + 噪音过滤 + workspace 映射的命令生成。它把 monorepo 中"CI 红了一大片"的场景收敛为一条可复制的本地命令,让 Agent 在第一次 job 失败时就能拿到结构化分诊报告并直接进入复现-修复循环,是"文档声明能力、脚本落实执行"式 Agent Skill 的一个典型范例。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00