Cloud dispatch pool + auto PR (opt-in, skip clearly)
Worker pool: workers table + register/heartbeat (heartbeat is
registration, no 404), GET /workers with alive (120s default),
POST /workers/heartbeat (X-API-Key, missing worker_id 400).
Tasks gain executor (local/docker/cloud, None = any worker);
worker claim filters by executor in pick_next and in the CAS
WHERE clause, CAS semantics unchanged; CloudExecutor _name
now correctly returns cloud, not local.
CloudExecutor: generic HTTP runner interface (endpoint from
explicit arg or CLOUD_RUNNER_ENDPOINT, auth header name only,
token value only from env into the request header, never into
logs/body/errors); no endpoint configured = explicit failure,
never silent downgrade. Vendor adapters (ECS/Cloud Run) are
out of scope, documented as such.
Auto PR: projects.auto_pr (0/1, create-time only, no PATCH
route exists); POST /tasks/{id}/pr (maintainer+ via
approve_merge) only for merged tasks (else 409); skip reasons
in order: auto_pr_disabled, no_worktree, no_remote, no_gh,
gh_not_authenticated — all return 200 with pr_skipped, task
stays merged, no push attempted; push/pr-create failure
returns 502 with step, task stays merged; success writes
pr_url/pr_number and audits pr_created. No default auto-push
from the worker. Remote URLs in errors are scrubbed of
userinfo; gh credentials are managed by gh itself, never
stored by the control plane.
Tests: host 27 pass, cloud 323 pass (8 skipped are
environment-dependent real-Postgres tests).
This commit is contained in:
1 parent
d3e588e4b3
commit
306cdf0d0c
8 files changed
+1256
-34
No files matched your search
@@ -83,6 +83,14 @@ X-API-Key,因为外部服务无法持本站 API key,改靠各自的签名认
|
||||
本站 API key——它的认证靠随机 state(10 分钟过期、一次性)加
|
||||
client_secret(只走环境变量),不是无认证后门。
|
||||
|
||||
本轮新增端点(同样要求 X-API-Key):`GET /workers`(注册 worker
|
||||
列表,含 alive)、`POST /workers/heartbeat`(body 含 worker_id,
|
||||
心跳即注册)、`POST /tasks/{id}/pr`(已 merged 任务的自动 PR,
|
||||
默认关闭,缺条件时返回 pr_skipped 而不是失败;见下文“云端派发与
|
||||
自动 PR”一节)。`POST /tasks` 新增可选 `executor`
|
||||
(local/docker/cloud,未传表示任意 worker 可认领);
|
||||
`POST /projects` 新增可选 `auto_pr`(0/1,默认 0)。
|
||||
|
||||
### OAuth 真实浏览器登录(已实现,部署前置仍需用户动手)
|
||||
|
||||
流程(标准 OAuth2 授权码流程):
|
||||
@@ -626,3 +634,71 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
|
||||
恢复演练、monitor、多用户验证四项的主机实跑还没做(本轮只在 VM 上
|
||||
跑了单测、deploy 干跑、monitor 自测与 VM 实跑)。
|
||||
|
||||
## 云端派发与自动 PR(本轮新增:多 worker 池、通用 runner、默认关闭的自动 PR)
|
||||
|
||||
- 多 worker 池(`cloud/workers.py` + worker 注册/心跳):worker 启动
|
||||
时向控制面注册(workers 表:worker_id、executor、capacity、
|
||||
last_seen),`run_loop` 每轮结束写注册表心跳;`GET /workers` 列出
|
||||
全部 worker 及其 alive(last_seen 在 120 秒内,阈值可经环境变量
|
||||
`CLOUD_WORKER_ALIVE_SECONDS` 调);`POST /workers/heartbeat`
|
||||
要求 body 含 worker_id(缺了 400),心跳本身就是注册——worker_id
|
||||
不存在时自动注册,绝不 404。worker_id 可经 CLI `--worker-id`
|
||||
指定(生产部署应配稳定 id),未指定时默认“主机名-进程号”
|
||||
(重启后进程号会变,旧行自然变 stale,不要用随机 uuid 让注册表
|
||||
每次重启都多一行无法对应到同一台机器);capacity 经
|
||||
`--worker-capacity` 指定(默认 1,注册表展示用,本轮 run_loop
|
||||
仍是串行一轮一任务)。两个端点都要 X-API-Key(与其它已认证
|
||||
端点一致);worker 直接写库注册时不需要 key,HTTP 心跳是给有
|
||||
key 的外部 worker 用的另一条路。
|
||||
- 任务按执行器池派发:tasks 表新增 `executor` 列(可空,老库幂等
|
||||
补列);`POST /tasks` 接受可选 executor(local/docker/cloud,
|
||||
非法值 400),未传时为 None 表示任意 worker 可认领;worker
|
||||
认领时只认领 executor 为空或等于自己执行器类型的任务——
|
||||
pick_next 先过滤,`_claim` 的 WHERE 里再加
|
||||
`(executor IS NULL OR executor = ?)` 兜底,CAS 语义不变(仅当
|
||||
queued 时标 running,两个同池 worker 抢同一任务时只有一个拿到
|
||||
rowcount == 1);认领日志与审计里含 executor。注意
|
||||
`_executor_name` 的修正:CloudExecutor 以前会被算成 local,
|
||||
现在明确返回 cloud,否则 cloud worker 会认领 local 池的任务。
|
||||
- CloudExecutor 通用 runner 接口(厂商无关,`cloud/sandbox.py`):
|
||||
本轮不假装接通某个云厂商(ECS、Cloud Run 等专属适配明确不在
|
||||
范围)。CloudExecutor 接受一个 runner endpoint(显式参数或
|
||||
环境变量 `CLOUD_RUNNER_ENDPOINT`);未设时 execute 一律明确
|
||||
失败“未配置云端 runner”,绝不静默降级到 Docker/Local;已设时
|
||||
以 HTTP POST 把任务发给该 endpoint,请求体只有 id、prompt、
|
||||
repo_url(task["repo"]),不含任何密钥;endpoint 返回的 status
|
||||
为 completed 才算 completed,否则算 failed。认证只走 header
|
||||
名:`CLOUD_RUNNER_AUTH_HEADER` 给 header 名,token 值只从环境
|
||||
变量取(`CLOUD_RUNNER_AUTH_TOKEN_ENV` 指定的变量名,默认
|
||||
`CLOUD_RUNNER_AUTH_TOKEN`),只放进请求头,绝不写进日志、
|
||||
错误文本或返回结果。worker CLI 的 `--executor` 新增 cloud 选项,
|
||||
构造 CloudExecutor;worker 的 run_one 走 dispatch_task,
|
||||
CloudExecutor 的 dispatch_task/run_task 直接委托给 execute。
|
||||
- 自动 PR(默认关闭,显式触发,`POST /tasks/{id}/pr`,需项目
|
||||
maintainer 以上权限 approve_merge):仅当任务状态为 merged 时
|
||||
可调(其它状态 409;任务不存在 404;权限不足 403)。逻辑按顺序
|
||||
检查(见 `cloud/pr.py`):项目 auto_pr 不为 1 时返回 200 且
|
||||
pr_skipped=true、reason=auto_pr_disabled,不失败、不推;本地
|
||||
没有对应 host 任务 worktree(`<仓库根>/work/host-worktrees/
|
||||
<host 任务名>`,host 任务名 = cloud 任务 id 经安全字符过滤,
|
||||
分支名 = host/<host 任务名>,与 sandbox._host_name 同规则)时
|
||||
reason=no_worktree;worktree 里没有 git remote origin 时
|
||||
reason=no_remote;本机没有 gh 二进制时 reason=no_gh;gh 未登录
|
||||
(gh auth status 非零)时 reason=gh_not_authenticated;以上全过
|
||||
才 push 分支 + gh pr create,成功后把 pr_url/pr_number 写回任务
|
||||
记录并记审计 pr_created,返回 200 含 pr_url。push 或 pr create
|
||||
失败时返回 502 且错误文本写清是哪一步(push 还是 pr_create),
|
||||
任务状态保持 merged 不变,绝不把任务标 failed。worker 不自动推:
|
||||
自动 PR 只由显式调本端点触发,默认自动推会把测试任务也推到远端。
|
||||
projects 表新增 `auto_pr` 列(默认 0,老库幂等补列);
|
||||
`POST /projects` 接受可选 auto_pr(只接受 0/1,非法值 400);
|
||||
本轮没有现成的 PATCH /projects/{id} 路由,auto_pr 只能在创建时
|
||||
指定,建后改它需直接改库或重建项目,不要新造一个只为改 auto_pr
|
||||
的路由。gh 的登录凭证由 gh 自己管,控制面不存、不打任何 token。
|
||||
- 仍未做(不要宣称已做):厂商专属适配(ECS、Cloud Run 等,需
|
||||
用户选厂商后另做);默认自动推(本轮明确不做);主机上 gh 的安装
|
||||
与登录、远端写凭证(控制面不存,自动 PR 的真实成功路径仍需它们);
|
||||
多 worker 的并发容量 enforcement(capacity 目前只是注册表展示,
|
||||
run_loop 仍串行);worker 经 HTTP 发心跳的凭证供给(worker 直接
|
||||
写库时不需要,外部 worker 走 HTTP 时需自带 API key)。
|
||||
|
||||
Reference in new issue
Block a user