docs: project scaffolding — README + architecture + roadmap + porting plan + ADRs
Initial documentation for route A (port claude-code-action to Gitea Actions). Research complete, no implementation code yet. See docs/02-roadmap.md for milestones.
This commit is contained in:
parent
cba50bc083
commit
c29af91113
35
.gitea/workflows/claude.yml.placeholder
Normal file
35
.gitea/workflows/claude.yml.placeholder
Normal file
@ -0,0 +1,35 @@
|
||||
# 占位 / 模板 —— 实际 workflow 会在 Milestone A2 阶段填充并改名为 claude.yml
|
||||
# 这里只展示目标形态,让任何来读仓库的人都能立刻理解最终调用方式
|
||||
|
||||
name: Claude Code Agent
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
# 仅在评论/正文含 @claude 时跑
|
||||
if: contains(github.event.comment.body, '@claude') || contains(github.event.issue.body, '@claude') || contains(github.event.pull_request.body, '@claude')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run claude-gitea-agent
|
||||
uses: pangtiankai/claude-gitea-agent@main
|
||||
with:
|
||||
gitea_token: ${{ secrets.GITEA_TOKEN }}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
# 可选:限制 Claude 能用的工具
|
||||
# allowed_tools: 'Read,Edit,Write,Bash'
|
||||
47
README.md
47
README.md
@ -1,3 +1,48 @@
|
||||
# claude-gitea-agent
|
||||
|
||||
Claude Code agent that responds to Gitea issues — interactive requirements discussion + auto PR (path A: ported claude-code-action)
|
||||
> Claude Code 自动响应 Gitea issue 的代理 —— 在 issue 里和你交互式确认需求,对齐后自动开发并提 PR。
|
||||
|
||||
## 项目目标
|
||||
|
||||
复刻 Anthropic 官方 [`claude-code-action`](https://github.com/anthropics/claude-code-action) 的 GitHub 体验,但跑在 Gitea 上:
|
||||
|
||||
- **触发**:在 Gitea issue 或 PR 评论里 `@claude ...`
|
||||
- **交互**:Claude 在评论里和你来回讨论需求(superpowers brainstorming 风格)
|
||||
- **执行**:对齐后自动建分支、写代码、跑测试、提 PR
|
||||
- **运行环境**:你的本地 act_runner(已部署)
|
||||
|
||||
## 当前状态
|
||||
|
||||
🟡 **筹划期** —— 仓库刚建好,docs 在写,代码移植还没开始。
|
||||
|
||||
| 阶段 | 状态 |
|
||||
|---|---|
|
||||
| 调研开源方案 | ✅ 完成([docs/02-roadmap.md](docs/02-roadmap.md)) |
|
||||
| 仓库脚手架 | ✅ 完成 |
|
||||
| Gitea API 客户端移植 | ⏳ 待开始 |
|
||||
| 触发器 + 评论交互 | ⏳ 待开始 |
|
||||
| PR 生成流程 | ⏳ 待开始 |
|
||||
| 端到端跑通 | ⏳ 待开始 |
|
||||
| 升级到 webhook 长驻服务(路线 B) | 🔮 未来 |
|
||||
|
||||
## 架构选择
|
||||
|
||||
**路线 A**:fork 上游 `claude-code-action`,替换 GitHub API 调用为 Gitea API,跑在 Gitea Actions 上。
|
||||
|
||||
- 优点:站在巨人肩膀上,提示词/上下文构造/PR 流程沿用官方逻辑
|
||||
- 缺点:Gitea Actions 冷启动 ~30s,每轮交互有延迟,不算"真实时"
|
||||
- 后续可升级到路线 B(长驻 webhook 服务 + Claude Agent SDK),细节见 [docs/02-roadmap.md](docs/02-roadmap.md)
|
||||
|
||||
详见 [docs/01-architecture.md](docs/01-architecture.md)。
|
||||
|
||||
## 快速链接
|
||||
|
||||
- [01 - 架构](docs/01-architecture.md)
|
||||
- [02 - 路线图](docs/02-roadmap.md)
|
||||
- [03 - 移植计划](docs/03-porting-plan.md)
|
||||
- [04 - 设计决策](docs/04-decisions.md)
|
||||
- 上游参考:[anthropics/claude-code-action](https://github.com/anthropics/claude-code-action)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
110
docs/01-architecture.md
Normal file
110
docs/01-architecture.md
Normal file
@ -0,0 +1,110 @@
|
||||
# 01 - 架构
|
||||
|
||||
## 总体数据流
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌────────────┐
|
||||
│ 开发者 │ ① @claude 在 issue/PR 评论提需求 │ Gitea │
|
||||
│ (浏览器) │ ───────────────────────────────────────▶│ (3000) │
|
||||
└─────────────┘ └─────┬──────┘
|
||||
│ ② webhook → workflow_dispatch
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ act_runner │
|
||||
│ (本机容器) │
|
||||
└─────┬─────────┘
|
||||
│ ③ 启动 Action
|
||||
▼
|
||||
┌────────────────────────────────────┐
|
||||
│ claude-gitea-agent Action │
|
||||
│ ┌──────────────────────────────┐ │
|
||||
│ │ 1. parse trigger │ │
|
||||
│ │ 2. fetch issue/PR context │ │
|
||||
│ │ 3. build prompt + skills │ │
|
||||
│ │ 4. exec `claude` CLI │ │
|
||||
│ │ 5. post comment / open PR │ │
|
||||
│ └──────────────────────────────┘ │
|
||||
└──────┬─────────────────────────────┘
|
||||
│ ④ Gitea API (评论/分支/PR)
|
||||
▼
|
||||
┌────────────┐
|
||||
│ Gitea │ ⑤ 用户在评论里看到 Claude 回复
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
## 关键组件
|
||||
|
||||
### 1. 触发器(trigger)
|
||||
|
||||
| 入口 | 上游 | Gitea 对应 |
|
||||
|---|---|---|
|
||||
| `issue_comment` | `github.event.comment.body` | `gitea.event.comment.body` ✅ 一致 |
|
||||
| `issues` (opened/edited) | 一致 | 一致 |
|
||||
| `pull_request_review_comment` | 一致 | 一致 |
|
||||
| `workflow_dispatch` | 手动触发 | 一致 |
|
||||
|
||||
触发短语:`@claude` 在评论/issue 正文里出现。
|
||||
|
||||
### 2. Gitea API 适配层(src/gitea/)
|
||||
|
||||
**对应上游 `src/github/` 的 1:1 重写**:
|
||||
|
||||
| 上游模块 | Gitea 移植 | 改造点 |
|
||||
|---|---|---|
|
||||
| `github/api/` | `gitea/api/` | 用 Gitea Swagger SDK 或 fetch;endpoint 90% 相似 |
|
||||
| `github/data/` | `gitea/data/` | 数据 schema 重写(少量字段差异) |
|
||||
| `github/operations/` | `gitea/operations/` | 评论 / branch / PR 创建逻辑 |
|
||||
| `github/validation/` | `gitea/validation/` | webhook 签名校验、权限检查 |
|
||||
| `github/utils/` | `gitea/utils/` | 工具函数 |
|
||||
|
||||
### 3. 提示词层(src/create-prompt/)
|
||||
|
||||
**几乎不动**。生成给 Claude 的初始 prompt(包含 issue 标题/正文、相关代码、历史评论)。
|
||||
|
||||
### 4. 模式层(src/modes/)
|
||||
|
||||
- `tag` 模式:被 @claude 提到时响应(**主用**)
|
||||
- `agent` 模式:显式 prompt 启动(高级用法,后期支持)
|
||||
|
||||
**几乎不动**。
|
||||
|
||||
### 5. Claude 执行层
|
||||
|
||||
调用本机 `claude` CLI 或 `@anthropic-ai/claude-agent-sdk`。携带:
|
||||
|
||||
- `CLAUDE_AGENT_SDK_AUTH`:用户的 API key(Gitea Secret)
|
||||
- `--add-dir`:仓库 checkout 后的工作目录
|
||||
- `--allowed-tools`:默认 Read/Edit/Write/Bash,可通过 inputs 限制
|
||||
|
||||
## Gitea Actions 兼容性预判
|
||||
|
||||
| 上游依赖 | Gitea Actions 表现 | 处理 |
|
||||
|---|---|---|
|
||||
| `@actions/core` | ✅ 完全兼容 | 原样用 |
|
||||
| `@actions/github` | ❌ 调用 GitHub API | 替换为 Gitea API |
|
||||
| `@octokit/rest` | ❌ GitHub-only | 替换为 Gitea SDK 或 fetch |
|
||||
| `@octokit/graphql` | ❌ Gitea 暂无 GraphQL | 改写为 REST 拼接 |
|
||||
| `@octokit/webhooks-types` | 类型定义 | 自己写 Gitea 的 types |
|
||||
| `@modelcontextprotocol/sdk` | ✅ 与平台无关 | 原样用 |
|
||||
| `@anthropic-ai/claude-agent-sdk` | ✅ 与平台无关 | 原样用 |
|
||||
| `actions/checkout@v4` | ✅ Gitea Actions 兼容 | 原样用 |
|
||||
|
||||
## 关键路径估算(路线 A)
|
||||
|
||||
| 阶段 | 工作量 | 关键风险 |
|
||||
|---|---|---|
|
||||
| 搭基础(types/auth/api 骨架) | 2-3 天 | Gitea SDK 选型 |
|
||||
| 端口 issue 评论 + PR 评论场景 | 3-4 天 | webhook payload 字段差异 |
|
||||
| 端口 branch 创建 + PR 提交 | 2-3 天 | Gitea PR API 偶尔有差异 |
|
||||
| 跑通 end-to-end demo | 1-2 天 | act_runner Docker socket 权限 |
|
||||
| 文档 + 自部署指南 | 1 天 | - |
|
||||
| **合计** | **~2 周** | |
|
||||
|
||||
## 升级到路线 B 的接口
|
||||
|
||||
设计时**保留这两个边界**便于以后切到长驻服务:
|
||||
|
||||
1. `EventDispatcher` 接口:当前实现读 `GITEA_EVENT_PATH` 文件;未来实现订阅 webhook
|
||||
2. `SessionStore` 接口:当前每次 Action run 都重新 build prompt;未来用同一个 Claude session 持续追加消息
|
||||
|
||||
具体见 [02-roadmap.md](02-roadmap.md)。
|
||||
111
docs/02-roadmap.md
Normal file
111
docs/02-roadmap.md
Normal file
@ -0,0 +1,111 @@
|
||||
# 02 - 路线图
|
||||
|
||||
## 调研结论(2026-05-25)
|
||||
|
||||
调研了 4 个候选 OSS:
|
||||
|
||||
| 项目 | Stars | 契合度 | 结论 |
|
||||
|---|---|---|---|
|
||||
| anthropics/claude-code-action | 7.7k | ⭐⭐⭐⭐⭐ | **GitHub Only**,移植到 Gitea 是这次项目的核心工作 |
|
||||
| All-Hands-AI/OpenHands | 40k+ | ⭐⭐⭐ | 非 Claude 原生,Gitea 支持弱,引入了大量不需要的东西 |
|
||||
| SWE-agent/SWE-agent | 19.3k | ⭐⭐ | 学术为主,一次性自动修,不擅长多轮交互 |
|
||||
| Turrain/claude-gitea-app | 1 | ⭐⭐⭐ | 名字对路但太小(20KB),不能依赖 |
|
||||
|
||||
**Gitea 生态周边可复用**:
|
||||
|
||||
- [Sqcows/forgejo-mcp](https://github.com/Sqcows/forgejo-mcp) — Forgejo/Gitea MCP server,103 工具
|
||||
- [pkulik0/gitea-skill](https://github.com/pkulik0/gitea-skill) — `tea` CLI 的 Claude Code Skill
|
||||
|
||||
**结论**:复刻 `claude-code-action` 是最直接的路径,OpenHands 等通用方案在交互性上不及。
|
||||
|
||||
---
|
||||
|
||||
## 路线 A:移植 `claude-code-action` 到 Gitea Actions(当前阶段)
|
||||
|
||||
**目标**:端到端跑通最小可用版本,体感先达到 "issue 里说话 → Claude 在评论里回复 → 同意后 PR 自动开"。
|
||||
|
||||
### Milestone A1:基础设施(第 1 周)
|
||||
|
||||
- [ ] fork upstream,确定 TS/Bun 工具链能在 act_runner 里跑
|
||||
- [ ] 在 Gitea 控制面板创建 `@claude` bot 用户 + 对应 access token
|
||||
- [ ] 验证 Gitea webhook → workflow_dispatch 链路
|
||||
- [ ] 写 Gitea API 客户端(`src/gitea/api/`),覆盖:
|
||||
- 评论 CRUD
|
||||
- issue/PR 读取
|
||||
- 分支创建 + push
|
||||
- PR 创建
|
||||
|
||||
### Milestone A2:tag 模式跑通(第 2 周)
|
||||
|
||||
- [ ] 移植 `src/modes/tag/`(@claude 触发的最常用模式)
|
||||
- [ ] 移植 `src/create-prompt/`(基本不动,确认 context 字段映射)
|
||||
- [ ] 移植 `src/entrypoints/`(适配 Gitea 事件载荷格式)
|
||||
- [ ] `.gitea/workflows/claude.yml` 示例工作流
|
||||
- [ ] 在本仓库自己开 issue 验证"对话+提 PR"流程
|
||||
|
||||
### 验收标准
|
||||
|
||||
```
|
||||
1. 我在某个 Gitea 仓库开 issue:「加一个返回当前时间的 /now 接口」
|
||||
2. issue 正文末尾 @claude
|
||||
3. 1 分钟内出现 Claude 评论:「先确认几点... a/b/c」
|
||||
4. 我回复评论:「a 这样,b 那样」
|
||||
5. Claude 再回复对齐 + 开始执行
|
||||
6. Claude push 分支 + 开 PR,PR 描述里贴着完整对话和实现说明
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 路线 B:长驻 webhook 服务(未来)
|
||||
|
||||
**触发条件**:路线 A 跑通后,发现每轮交互 30s+ 冷启动太难受,再启动 B。
|
||||
|
||||
### 架构差异
|
||||
|
||||
```
|
||||
路线 A:每次评论 → 启动 Action → 加载 context → 退出 (冷)
|
||||
路线 B:每次评论 → POST 到长驻服务 → 复用 Claude session → 立刻回复 (热)
|
||||
```
|
||||
|
||||
### 主要工作
|
||||
|
||||
- 用 Python 或 Node 写一个常驻 webhook server(FastAPI / Hono)
|
||||
- 用 `@anthropic-ai/claude-agent-sdk` 维护 `{issue_id: ClaudeSession}` 字典
|
||||
- 每条 Gitea 评论 webhook → 找到对应 session → `session.send_message()` → 回写评论
|
||||
- 长会话超时回收策略(24h 不活跃释放)
|
||||
- 加载 superpowers skills([skills 目录注入](https://docs.claude.com/api/agent-sdk))
|
||||
- 部署:和 Gitea 同机 docker-compose,加一个 `claude-agent` 服务
|
||||
|
||||
### 升级路径
|
||||
|
||||
设计路线 A 代码时已保留两个抽象接口:
|
||||
|
||||
```typescript
|
||||
interface EventDispatcher {
|
||||
// A: read GITEA_EVENT_PATH file
|
||||
// B: subscribe to webhook stream
|
||||
next(): Promise<GiteaEvent>
|
||||
}
|
||||
|
||||
interface SessionStore {
|
||||
// A: fresh prompt each run
|
||||
// B: persistent ClaudeSession per issue
|
||||
get(key: string): Promise<Session>
|
||||
save(key: string, session: Session): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
切换路线时只需新实现这两个接口,核心业务逻辑(trigger 解析、Gitea API、PR 流程)不动。
|
||||
|
||||
---
|
||||
|
||||
## 不做的事(明确拒绝)
|
||||
|
||||
- ❌ **不**追求支持 GitHub + Gitea + GitLab 多平台。Gitea-only,避免抽象税
|
||||
- ❌ **不**在路线 A 阶段做 Web UI / dashboard。命令行 + Gitea 评论已够
|
||||
- ❌ **不**做"全自动决策合并 PR"。Claude 提 PR,人审核合并
|
||||
- ❌ **不**移植上游所有 mode(review/agent 等),只做 tag mode
|
||||
|
||||
## 决策记录
|
||||
|
||||
非显然的设计选择记在 [04-decisions.md](04-decisions.md)。
|
||||
139
docs/03-porting-plan.md
Normal file
139
docs/03-porting-plan.md
Normal file
@ -0,0 +1,139 @@
|
||||
# 03 - 移植计划
|
||||
|
||||
> 把 `anthropics/claude-code-action` 源代码逐文件迁移到本仓库的清单。
|
||||
> 上游版本:master @ 2026-05-23(克隆在 `/home/pangtiankai/projects/claude-code-action-upstream/`)
|
||||
|
||||
## 目录映射
|
||||
|
||||
| 上游 | 本仓库 | 移植量 |
|
||||
|---|---|---|
|
||||
| `action.yml` | `action.yml` | 🟡 改 inputs:去掉 GitHub 特有字段(如 `github_token` → `gitea_token`) |
|
||||
| `package.json` | `package.json` | 🟡 替换 `@octokit/*` 依赖 |
|
||||
| `bunfig.toml` | `bunfig.toml` | ✅ 原样 |
|
||||
| `tsconfig.json` | `tsconfig.json` | ✅ 原样 |
|
||||
| `src/entrypoints/` | `src/entrypoints/` | 🟡 适配 Gitea 事件载荷字段 |
|
||||
| `src/modes/tag/` | `src/modes/tag/` | 🟢 90% 原样,少量字段重命名 |
|
||||
| `src/modes/agent/` | — | ⛔ **暂不移植**(后期再说) |
|
||||
| `src/create-prompt/` | `src/create-prompt/` | 🟢 90% 原样 |
|
||||
| `src/github/api/` | `src/gitea/api/` | 🔴 **完全重写**(Gitea API client) |
|
||||
| `src/github/data/` | `src/gitea/data/` | 🔴 重写 schema |
|
||||
| `src/github/operations/` | `src/gitea/operations/` | 🔴 重写(评论、branch、PR) |
|
||||
| `src/github/validation/` | `src/gitea/validation/` | 🔴 重写 |
|
||||
| `src/github/utils/` | `src/gitea/utils/` | 🟡 部分通用,部分重写 |
|
||||
| `src/auth/` | `src/auth/` | 🟢 90% 原样(Anthropic API key 认证不变) |
|
||||
| `src/mcp/` | `src/mcp/` | ✅ 原样 |
|
||||
| `src/utils/` | `src/utils/` | ✅ 原样 |
|
||||
| `base-action/` | `base-action/` | 🟢 90% 原样 |
|
||||
|
||||
**统计**:约 30% 文件要重写,60% 只改字段或导入路径,10% 完全不动。
|
||||
|
||||
## 重写优先级(P0 = 第一周必交付)
|
||||
|
||||
### P0 — Gitea API client(`src/gitea/api/`)
|
||||
|
||||
最低要凑齐的 endpoint:
|
||||
|
||||
| 操作 | Gitea API | 备注 |
|
||||
|---|---|---|
|
||||
| 获取 issue | `GET /repos/{owner}/{repo}/issues/{number}` | |
|
||||
| 获取 issue 评论列表 | `GET /repos/{owner}/{repo}/issues/{number}/comments` | |
|
||||
| 创建评论 | `POST /repos/{owner}/{repo}/issues/{number}/comments` | |
|
||||
| 更新评论 | `PATCH /repos/{owner}/{repo}/issues/comments/{id}` | 用于流式更新("思考中..."→ 最终回复) |
|
||||
| 获取 PR | `GET /repos/{owner}/{repo}/pulls/{number}` | |
|
||||
| 获取 PR diff | `GET /repos/{owner}/{repo}/pulls/{number}.diff` | |
|
||||
| 创建分支 | `POST /repos/{owner}/{repo}/branches` | |
|
||||
| 创建 PR | `POST /repos/{owner}/{repo}/pulls` | |
|
||||
| 获取仓库内容 | `GET /repos/{owner}/{repo}/contents/{filepath}` | |
|
||||
|
||||
**选择**:直接用 `fetch` + zod schema,不引入完整 Gitea SDK(依赖最小化)。
|
||||
|
||||
### P0 — Trigger 解析(`src/modes/tag/`)
|
||||
|
||||
需要适配的事件类型:
|
||||
|
||||
| Gitea 事件 | trigger 条件 |
|
||||
|---|---|
|
||||
| `issues.opened` | issue 正文含 `@claude` |
|
||||
| `issues.edited` | 同上 |
|
||||
| `issue_comment.created` | 评论正文含 `@claude` |
|
||||
| `pull_request.opened` | PR 正文含 `@claude` |
|
||||
| `pull_request_comment.created` | PR 评论含 `@claude` |
|
||||
|
||||
### P0 — Entrypoint(`src/entrypoints/`)
|
||||
|
||||
读取 `GITHUB_EVENT_PATH` → 改成 `GITEA_EVENT_PATH`(Gitea Actions 自动注入)。
|
||||
事件载荷字段:
|
||||
|
||||
```
|
||||
github.event.issue.user.login → gitea.event.issue.user.login ✅ 一致
|
||||
github.event.comment.user.login → gitea.event.comment.user.login ✅ 一致
|
||||
github.repository → gitea.repository ✅ 一致
|
||||
github.event.pull_request.head.sha → 一致
|
||||
```
|
||||
|
||||
### P1 — Status comment("我在干活" 进度评论)
|
||||
|
||||
上游用 GitHub Checks API 做进度展示,Gitea 没有等价 API([Gitea Issue #28381](https://github.com/go-gitea/gitea/issues/28381) 是 commit status,不一样)。
|
||||
|
||||
替代方案:**用一条评论的连续 edit 模拟**。
|
||||
- 第一条评论:"🤔 Claude 正在分析需求..."
|
||||
- 边干边 PATCH 更新这条评论的正文
|
||||
- 完成后定格成最终内容
|
||||
|
||||
### P2 — `agent` mode、`review` mode
|
||||
|
||||
后期再说。
|
||||
|
||||
## 依赖替换
|
||||
|
||||
### package.json
|
||||
|
||||
去掉:
|
||||
```json
|
||||
"@actions/github": "^...",
|
||||
"@octokit/rest": "^...",
|
||||
"@octokit/graphql": "^...",
|
||||
"@octokit/webhooks-types": "^..."
|
||||
```
|
||||
|
||||
新增(如果需要):
|
||||
```json
|
||||
// 如果用生成的 SDK
|
||||
"gitea-js": "latest"
|
||||
// 否则只用 fetch + zod,零新增
|
||||
```
|
||||
|
||||
保留原样:
|
||||
```json
|
||||
"@actions/core": "...", // 跟 Gitea Actions 兼容
|
||||
"@anthropic-ai/claude-agent-sdk": "...",
|
||||
"@modelcontextprotocol/sdk": "...",
|
||||
"shell-quote": "...",
|
||||
"zod": "..."
|
||||
```
|
||||
|
||||
## webhook payload 差异速查
|
||||
|
||||
Gitea 的 webhook payload 大部分字段和 GitHub 一致(这是有意为之的),主要差异:
|
||||
|
||||
| GitHub | Gitea | 说明 |
|
||||
|---|---|---|
|
||||
| `pull_request.head.repo` | `pull_request.head.repo` | Gitea 始终有此字段(GH 偶尔 null) |
|
||||
| `installation` | — | Gitea 没有 GitHub App 概念 |
|
||||
| `sender.type === "Bot"` | `sender.type === "user"` | Gitea 暂无 Bot 用户类型 |
|
||||
|
||||
完整差异参考:[Gitea webhook 文档](https://docs.gitea.com/usage/webhooks)。
|
||||
|
||||
## 测试策略
|
||||
|
||||
1. **本仓库自身做 dogfooding**:在 `claude-gitea-agent` 自己的 issue 里 @claude,让它给自己写功能
|
||||
2. **mock 测试**:`test/fixtures/` 放几个 Gitea webhook payload 样本
|
||||
3. **CI**:暂不上 CI,跑通 happy path 后再加
|
||||
|
||||
## 当前阻塞 / 风险
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|---|---|
|
||||
| Gitea Actions 跑 bun 容器可能要装 bun | runner image 用 `catthehacker/ubuntu:act-latest`(已包含 node,bun 安装 1 行命令) |
|
||||
| Gitea webhook 评论事件名是 `issue_comment` 还是 `issues_comment`? | 写 entrypoint 时先 dump payload 验证 |
|
||||
| Anthropic API rate limit 在循环交互场景下会不会爆 | 暂不优化,到了再说 |
|
||||
110
docs/04-decisions.md
Normal file
110
docs/04-decisions.md
Normal file
@ -0,0 +1,110 @@
|
||||
# 04 - 设计决策(ADR)
|
||||
|
||||
> 仅记录"非显然"的选择 —— 同一份代码读者扫一眼就懂的东西不在这里。
|
||||
|
||||
## ADR-001:fork 上游而不是从零写
|
||||
|
||||
**日期**:2026-05-25
|
||||
**状态**:✅ 采纳
|
||||
|
||||
**背景**:要做"issue 触发 Claude → 交互 → PR"的工具,候选方案有从 0 写、用 OpenHands、复刻 SWE-agent、移植 claude-code-action。
|
||||
|
||||
**决策**:移植 anthropics/claude-code-action。
|
||||
|
||||
**原因**:
|
||||
- 上游的 prompt 构造、context 收集、PR 生成流程是 Anthropic 内部生产用的,质量靠谱
|
||||
- TypeScript 实现,比起从 0 写省 2-3 周
|
||||
- Gitea API 与 GitHub API 大量相似,移植量约 30% 文件
|
||||
- 不引入 OpenHands 等大型框架的认知和维护成本
|
||||
|
||||
**代价**:
|
||||
- 引入 TS/Bun 工具链(如果团队更熟 Python,是个学习成本)
|
||||
- 跟随上游升级有维护成本(mitigated by:上游 API 变化不会太频繁)
|
||||
|
||||
---
|
||||
|
||||
## ADR-002:路线 A(Actions)先行,路线 B(webhook 服务)后置
|
||||
|
||||
**日期**:2026-05-25
|
||||
**状态**:✅ 采纳
|
||||
|
||||
**背景**:Gitea Actions 冷启动 ~30s,每轮交互体感差。一开始就上长驻服务能解决,但工作量翻倍。
|
||||
|
||||
**决策**:先做路线 A,跑通验收标准后再决定是否升级 B。
|
||||
|
||||
**原因**:
|
||||
- 体感差但能用 ≫ 体感好但跑不起来
|
||||
- 真实跑起来才知道哪些环节实际是瓶颈(可能 30s 启动里实际只有 5s 在等 Claude,瓶颈是别的)
|
||||
- 留好抽象接口(`EventDispatcher` / `SessionStore`),升级不重写业务
|
||||
|
||||
**代价**:
|
||||
- 用户首次体验差,可能让人产生"这玩意没用"的错觉
|
||||
- mitigated by:文档明说"路线 A 是过渡,B 才是终态"
|
||||
|
||||
---
|
||||
|
||||
## ADR-003:不引入完整 Gitea SDK,用 fetch + zod
|
||||
|
||||
**日期**:2026-05-25
|
||||
**状态**:✅ 采纳
|
||||
|
||||
**背景**:Gitea 有 `gitea-js` SDK,但维护活跃度一般。
|
||||
|
||||
**决策**:直接 `fetch` + zod schema 做 API 调用。
|
||||
|
||||
**原因**:
|
||||
- 需要的 endpoint 只有 ~10 个,自己写 client 不到 200 行
|
||||
- zod schema 比 SDK 自带类型更可控(出问题好排查)
|
||||
- 少一个依赖,少一份升级负担
|
||||
- 上游 `@octokit/rest` 本来也只用了 1% 的功能
|
||||
|
||||
**代价**:
|
||||
- 失去了 SDK 的 retry / rate-limit 内置逻辑
|
||||
- mitigated by:路线 A 阶段流量很小,需要时再加 wrapper
|
||||
|
||||
---
|
||||
|
||||
## ADR-004:Status comment 用"连续 edit 单条评论"模拟 GitHub Checks
|
||||
|
||||
**日期**:2026-05-25
|
||||
**状态**:✅ 采纳
|
||||
|
||||
**背景**:上游用 GitHub Checks API 做进度展示(带 ✅ / ⏳ 复选框)。Gitea 没有等价 API。
|
||||
|
||||
**决策**:发一条评论作为状态板,全程 PATCH 这条评论的正文模拟进度更新。
|
||||
|
||||
**原因**:
|
||||
- 唯一可行的方案(除非等 Gitea 实现 Checks API)
|
||||
- 用户看到的视觉效果差不多(一条会变的评论 vs 一个会变的 Check)
|
||||
- 实现简单
|
||||
|
||||
**代价**:
|
||||
- 不像 Checks 那样能在 PR 顶部显眼地显示状态
|
||||
- mitigated by:评论会自动滚到底部,user 自然能看到最新进度
|
||||
|
||||
---
|
||||
|
||||
## ADR-005:暂不支持 GitHub App 等价的 bot 身份
|
||||
|
||||
**日期**:2026-05-25
|
||||
**状态**:✅ 采纳
|
||||
|
||||
**背景**:上游支持 GitHub App 安装,bot 评论时会有 "Claude (bot)" 标识。Gitea 暂无原生 bot 用户类型。
|
||||
|
||||
**决策**:用普通用户 `@claude`(或任意命名)作为 bot 身份,access token 模式调用。
|
||||
|
||||
**原因**:
|
||||
- Gitea 没有 App 概念,硬模拟没必要
|
||||
- 普通用户 + token 模式简单直接,权限可控
|
||||
|
||||
**代价**:
|
||||
- 评论里没有 bot 标识,看起来像普通用户在说话
|
||||
- mitigated by:评论里加固定 footer "🤖 Powered by claude-gitea-agent"
|
||||
|
||||
---
|
||||
|
||||
## 待决策
|
||||
|
||||
- 长会话状态存哪?路线 B 时再选(候选:SQLite / Redis / 文件)
|
||||
- 是否支持私有仓库?默认支持,token 权限够就行
|
||||
- 多仓库共享 runner 还是每仓库独立?运行时按需启动,act_runner 已支持
|
||||
Loading…
Reference in New Issue
Block a user