---
name: gitea-fix-ci
description: "Use when a user asks to debug or fix failing Gitea Actions, Gitea PR checks, or CI workflow runs for a Gitea-hosted repository. Also triggers on Chinese phrasing such as CI 挂了/流水线红了/构建失败/工作流失败."
---

# Gitea Fix CI

## 概述

从 pull request 或 workflow run 诊断失败的 Gitea Actions，提取最小可用的失败上下文，
然后在改代码之前先给出修复计划。核心原则：CI 日志是证据，不要仅凭红色状态臆测原因。

本 skill 不是独立执行器。它指导你在当前工作区中使用随附的取证脚本
`scripts/fetch_ci_logs.py`、`tea`、本地 `git` 和 Gitea API。

取证阶段（step 2-4）优先使用随附脚本，把「探测版本 → 找失败 run → 列 job →
下载失败 job 日志」这段易记错 API 路径的逻辑交给它。脚本仅取证到日志，分类、
修复计划与改代码仍由你按本文档执行。将 `<skill-dir>` 替换为包含本 `SKILL.md`
的目录：

```bash
# 方式一（优先）：通过环境变量提供 API token
export GITEA_TOKEN=<token>
python <skill-dir>/scripts/fetch_ci_logs.py version           # 探测实例版本并确认连通性
python <skill-dir>/scripts/fetch_ci_logs.py runs --status failure --branch <branch>
python <skill-dir>/scripts/fetch_ci_logs.py jobs <run-id>     # 标注 failure 的 job
python <skill-dir>/scripts/fetch_ci_logs.py logs <job-id>     # 日志存临时文件并回显尾部

# 方式二：显式复用当前仓库的 Git HTTP 凭据
python <skill-dir>/scripts/fetch_ci_logs.py --use-git-credential version
python <skill-dir>/scripts/fetch_ci_logs.py --use-git-credential runs --status failure
```

`base-url`、owner 和 repo 默认从 git remote 推导。认证规则如下：

- 设置了 `GITEA_TOKEN` 时始终优先使用它，即使同时传入 `--use-git-credential`。
- 只有显式传入 `--use-git-credential` 才会非交互调用 `git credential fill`，并将
  当前仓库对应的用户名和密码用于 HTTP Basic 认证；脚本不会静默读取 Git 凭据。
- 未设置 token 且未传入该参数时按匿名方式访问，私有仓库通常会返回 401。
- 所有携带凭据的请求必须使用 HTTPS；认证 header 只发送到配置的 Gitea 同源地址，
  跨源请求和跨源重定向会被拒绝。凭据不会出现在命令参数、日志或异常文本中。

脚本不可用（未装 Python、旧版 Gitea 无 job-log API、脱离 Gitea 环境）时，回退到
下文的手动 `tea`/API/web 路径。

## 适用场景

- Gitea 托管的仓库出现失败的 Gitea Actions 或 PR checks
- 用户要求检查某个失败的 workflow run、job 或 CI 状态
- 用户要求在 push、更新分支或更新 pull request 之后修复 CI
- 本地测试通过，但远端 Gitea Actions 失败

## 不适用场景

- 仓库并非托管在 Gitea 或 Forgejo 兼容的基础设施上
- 失败属于外部 CI 服务，只是从 Gitea 链接出去
- 用户只想在本地跑测试或 lint
- 没有凭据、token 或网络访问权限，且用户也未提供失败的日志文本

## 输入

- 仓库路径，默认为当前工作区
- Gitea base URL 和仓库 owner/name，尽量从 `git remote -v` 获取
- Pull request 编号、分支、commit SHA 或 workflow run ID
- 认证方式：`tea` 登录 profile、用于 API 请求的 `GITEA_TOKEN`，或通过
  `--use-git-credential` 显式读取的当前仓库 Git HTTP 凭据
- 若无法远端访问，则由用户粘贴的 CI 日志

## 流程

1. **基线本地状态**

   - 记录 `git status --short`、当前分支和最新 commit SHA。
   - 从 `git remote -v` 识别 remote URL 和 owner/repo。
   - 继续之前先确认 remote 是 Gitea/Forgejo：对照已知的 Gitea 实例核对 host，
     或探测 `/api/v1/version`。若 remote 是 GitHub、GitLab 或其他服务，按"不适用场景"停止。
   - 收集 CI 证据期间不要修改文件。

2. **验证 Gitea 访问**

   - 优先运行 `fetch_ci_logs.py version` 探测实例版本并确认脚本能连通实例。脚本从
     remote 推导 base URL，认证按 `GITEA_TOKEN` → 显式 `--use-git-credential` →
     匿名的顺序选择。注意：`version` 只读 `/api/v1/version`，成功仅代表连通与鉴权
     可用，**不代表 Actions API 一定可用**。Actions 端点是否存在需在后续
     `runs`/`jobs` 步骤中实际验证；旧版 Gitea（见下）会在此才暴露 404。
   - 脚本不可用时，回退到手动方式。优先使用已安装并已认证的 `tea`：
     - `tea login list`
     - 使用前先运行 `tea actions --help` 确认已安装的 `tea` 版本存在 `actions`
       子命令；并非所有版本都带这些子命令。
     - `tea actions runs list --status failure --branch <branch>`
     - `tea actions runs view <run-id>`
     - `tea actions runs logs <run-id> --job <job-id>`
     - `tea pulls view <pr>`
   - 若 `tea` 不可用，或已安装版本缺少 Actions 命令，则直接使用 Gitea API。
   - 当 API 行为不明确时，检查 `/api/v1/version` 和 `/swagger.v1.json`。较旧的
     Gitea（1.21 及更早）在 web UI 中提供 Actions 页面，但不提供 workflow
     run/job/log 的 API 端点；应对照上报的版本确认可用性，而不是想当然。
   - 绝不打印 token、用户名或密码。API token 只通过环境变量传递；Git 凭据只通过
     `git credential fill` 的标准输入/输出在脚本进程内传递，不放入 argv。

3. **定位失败的 workflow run**

   - 若入口是 PR 而非 run ID，先把 PR 解析成 run：取 PR 的 head 分支与 head SHA
     （`fetch_ci_logs.py` 的 `runs` 支持 `--branch`/`--sha`；手动则
     `GET /api/v1/repos/<owner>/<repo>/pulls/<pr>` 取 `head.ref`/`head.sha`），
     再按该 SHA 过滤 runs。PR 页面的 checks 列表可能聚合多个 workflow，逐一定位到
     具体失败 run，不要假设只有一个。
   - 优先用脚本：`fetch_ci_logs.py runs --status failure --branch <branch>`
     （也支持 `--sha`/`--event`/`--limit`），已知 run ID 时用
     `fetch_ci_logs.py jobs <run-id>` 直接列出各 job 并标注失败项。
   - 脚本不可用时回退到手动方式。已知 run ID 时，直接获取该 run。
   - 否则按分支、事件、状态或 commit SHA 过滤，列出最近的 workflow runs。
   - API 模式：
     - `GET /api/v1/repos/<owner>/<repo>/actions/runs`
     - `GET /api/v1/repos/<owner>/<repo>/actions/runs/<run>`
     - `GET /api/v1/repos/<owner>/<repo>/actions/runs/<run>/jobs`
   - 较旧 Gitea 的 web 回退：
     - `GET /<owner>/<repo>/actions`
     - 解析 `/<owner>/<repo>/actions/runs/<run>` 链接和状态标签。
   - 区别对待 `failure`、`cancelled` 和缺失的必需 checks。被取消（cancelled）的
     job 可能需要重跑或排查队列，而非改代码。

4. **获取 job 日志**

   - 优先用脚本：`fetch_ci_logs.py logs <job-id>`。它把日志存到受跟踪源码路径
     之外的临时文件、只回显尾部若干行，并打印临时文件路径供进一步查看
     （`--tail N` 调整行数，`--json` 输出结构化结果）。
   - 脚本不可用时回退到手动方式。对每个失败的 job，下载其日志：
     - `GET /api/v1/repos/<owner>/<repo>/actions/jobs/<job_id>/logs`
   - 在较旧 Gitea 的 web 回退（无 job-log API）下，按 UI 的 job index 下载日志：
     - `GET /<owner>/<repo>/actions/runs/<run>/jobs/<job-index>/logs`
     - 先打开 run 页面读取每个 job 的状态；取状态为 `failure` 的 job 的 index，
       不要默认用 index `0`。失败的 job 很少是第一个，盲目用 index `0` 通常会返回
       某个通过的 job 的日志。只有当 run 页面不暴露每个 job 的状态时，才回退到逐一
       扫描 index。
   - 大日志保存到受跟踪源码路径之外的临时文件。
   - 提取首个可操作的错误块、其上下文命令、job 名、workflow 名、run URL、分支和 SHA。
   - 若日志缺失，明确报告，而不是编造原因。

5. **分类失败**

   - 代码/测试失败：断言失败、编译错误、lint 错误、类型错误
   - 环境失败：缺失 secret、runner 镜像、依赖安装、网络、缓存、权限或服务启动
   - 工作流失败：YAML 非法、语法不支持、触发条件错误、路径错误、分支/ref 假设错误
   - 基础设施失败：runner 离线、队列卡住、run 被取消、超时

6. **制定修复计划**

   - 用确切的 job/run 标识总结失败证据。
   - 提出与证据匹配的最小代码或工作流改动。
   - 包含本地验证命令和远端复检路径。
   - 在用户批准修复计划之前不要实施。

7. **批准后实施**

   - 只应用已批准的修复。
   - 运行最接近复现该失败 job 的本地命令。
   - 若失败仅涉及工作流，验证工作流文件语法及其引用的路径或脚本。

8. **复检**

   - 告诉用户需要在 Gitea 中 push 或重跑什么。
   - 若获准，使用 Gitea API 检查重跑状态。
   - 最终输出必须区分本地验证与远端 CI 状态。

## 输出约定

- `Target:` 仓库、分支/SHA、PR 或 run ID
- `Failed CI:` workflow、job、状态、run URL 或 API 路径
- `Evidence:` 精简的日志片段与分类
- `Plan:` 提出的修复、本地验证、远端复检
- `Changes:` 批准后改动的文件
- `Result:` 已运行的本地检查与剩余的远端状态

## 成功标准

- 失败分析基于 Gitea Actions 的 run/job 数据或粘贴的日志
- 修复计划指明其针对的确切 workflow run 或 job
- 计划批准前不发生任何代码或工作流改动
- 验证区分本地命令与远端 Gitea Actions 结果
- 不回显 token；私有日志只保留可操作的片段，不整段外泄

## 危险信号（Red Flags）

出现以下情况说明流程走偏，停下纠正而非继续：

- **凭红色状态臆测原因**：还没下载 job 日志就断言失败原因或动手改代码。
- **误读双字段结果**：把 `status`（生命周期）当成结果判定。Gitea Actions 沿用
  GitHub 兼容的双字段模型——`status=completed` 只表示跑完了，真正的成败在
  `conclusion`（`failure`/`success`/`cancelled`）。判定失败必须看有效结果，
  而非 `status=completed` 就当通过。
- **盲取 job index `0`**：在 web 回退下不看每个 job 状态就用 index `0` 下载日志；
  失败的 job 很少是第一个，通常会误取到某个通过 job 的日志。
- **把 `version` 成功当作 Actions API 可用**：`version` 只探连通与鉴权，Actions
  端点可能仍返回 404（旧版 Gitea）。
- **把 `cancelled`/基础设施问题当代码 bug 修**：被取消、runner 离线、队列卡住、
  超时应重跑或排查环境，不是改代码。
- **回显 token 或整段私有日志**：只保留可操作的最小片段。
- **静默读取或降级传输凭据**：Git 凭据必须由 `--use-git-credential` 显式启用；
  token 和 Git 凭据都不得通过 HTTP 或跨源重定向发送。

## 失败处理

- 401 且未使用认证时，通过环境提供 `GITEA_TOKEN` 或显式传入
  `--use-git-credential`；401 且已使用认证时检查凭据是否有效，不要在对话中索要 secret
- 403 表示所选凭据缺少仓库或 Actions 读取权限；补足权限，而不是切换到匿名访问
- 404 优先核对 owner/repo、端点和 Gitea 版本；旧版本缺少 Actions API 时使用 web 回退
- 若 Gitea 版本缺少 Actions API 端点，请用户提供相关日志文本或从浏览器复制的 job 日志
- 若失败的 check 归属外部 CI 服务，报告外部 URL 并在证据收集处停止
- 若失败仅为基础设施问题，建议重跑/排查 runner，而非改代码
