App-token:推分支建 PR 改走 GitHub App installation token

- github_app:纯标准库 RS256 签 App JWT(PKCS#1/PKCS#8),
  换 installation token(现换现用、不缓存),按仓库名查安装
  记录,任一步失败回落 None
- pr:app_token 路径 push 走 credential helper(token 只进
  子进程环境),建 PR 走 REST API(读 default_branch 当 base);
  不传 token 时 gh 路径不变
- server:/pr 端点按项目 repo_url 解析仓库名、现换 token,
  响应加 auth_via=app|pat,换失败回落 PAT 不报错
- 测试:JWT/换 token/解析 9 个、pr App 路径 6 个、端点级
  App 与 PAT 回落 2 个
This commit is contained in:
host committed 2026-10-26 16:47:55 +08:00
1 parent ee4b01c8f6
commit 25d9b3d938
7 files changed
+1060 -20

No files matched your search

+26 -7
View File
@@ -172,7 +172,7 @@ client_secret(只走环境变量),不是无认证后门。
没有返回 404)后恢复原来的 oauth 账号登录。查看自己的绑定:
`GET /oauth/{provider}/bindings`(须持本站 key,只看得到自己的)。
### GitHub App 一键安装(已实现安装一半;App-token API 动作未做)
### GitHub App 一键安装与 App-token(安装 + token 均已实现)
目标:把手动建 webhook(填 URL、secret、选事件)变成“点一下
安装”。流程用 GitHub manifest 流程(GitHub 文档标准流程):
@@ -207,9 +207,18 @@ client_secret(只走环境变量),不是无认证后门。
/fix-review 也有内容去重(repo + PR + 评论 id,
`github_fix_review_dedup` 表),只建一次任务。
诚实边界:pem 虽然存下来了,但本阶段不用它签 JWT、不用它换
installation token;推分支、建 PR 仍走 gh + PAT(见“云端派发与
自动 PR”),App-token API 动作是下一阶段。
诚实边界(已缩小):pem 现在用于签 JWT、换 installation
token:`cloud/github_app.py` 的 `mint_app_jwt` 是纯标准库
RS256(解析 PKCS#1 / PKCS#8 私钥,不依赖 openssl、不落临时
文件),`exchange_installation_token` 换 1 小时有效的 token
(现换现用、不缓存、不存库),`resolve_app_token` 按仓库名
查 `github_app_installations` 找 installation id,任一步缺失
或失败返回 None(调用方回落 PAT,不报错)。推分支、建 PR
走 App 还是 PAT 见“云端派发与自动 PR”;host.py 本地操作
(pr / checks / merge)仍是操作员自己的 gh + PAT,不走
App-token。App 安装事件的现场记录路径仍待下一次真实
安装/增减仓库动作实弹验证(此前一次是数据修复,不是
webhook 实测)。
### 推送凭证清单
@@ -830,8 +839,17 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
才先把 worktree 改动提交到分支上(worktree 必须正好在
host/<host 任务名> 上,否则直接失败;nothing to commit 时跳过;
缺 git 身份时回退 cloud/cloud@local 再试),再 push 分支 +
gh pr create,成功后把 pr_url/pr_number 写回任务
记录并记审计 pr_created,返回 200 含 pr_url。push 或 pr create
建 PR,成功后把 pr_url/pr_number 写回任务
记录并记审计 pr_created,返回 200 含 pr_url。App-token
路径:项目 repo_url 能解析出 github.com 仓库名且库里有该
仓库的 App 安装记录时,端点现换 installation token 走 App
路径(响应里 auth_via=app):push 用 git credential helper,
token 只进子进程环境变量 GIT_TOKEN(不进命令行参数、不进
文件,helper 命令里只有变量名),建 PR 走 GitHub REST API
(先读仓库 default_branch 当 base,不猜 main);没有安装
记录或换 token 失败时自动回落原来的 gh + PAT 路径
(auth_via=pat),不因此报错;App 没装的仓库与 gh 路径
的行为与原来完全一致。push 或 pr create
失败时返回 502 且错误文本写清是哪一步(commit、push 还是
pr_create),
任务状态保持 merged 不变,绝不把任务标 failed。顺序:任务
@@ -846,7 +864,8 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
的路由。gh 的登录凭证由 gh 自己管,控制面不存、不打任何 token。
- 仍未做(不要宣称已做):厂商专属适配(ECS、Cloud Run 等,需
用户选厂商后另做);默认自动推(本轮明确不做);主机上 gh 的安装
与登录、远端写凭证(控制面不存,自动 PR 的真实成功路径仍需它们);
与登录、远端写凭证(控制面不存;PAT 路径的自动 PR 真实成功
仍需它们,App-token 路径不需要 gh);
多 worker 的并发容量 enforcement(capacity 目前只是注册表展示,
run_loop 仍串行);worker 经 HTTP 发心跳的凭证供给(worker 直接
写库时不需要,外部 worker 走 HTTP 时需自带 API key)。