Production-grade: webhook execution, Postgres backup, rate limit, worker timeout/retry

GitHub webhook: delivery-id dedup persisted in webhook_deliveries,
/fix-review creates a queued task with audit (no model run in the
webhook itself), check_run/check_suite parsing without mutating
task state (CI backfill still via pr-checks).
Control plane: Postgres pg_dump backup branch + POST /admin/backup
(maintainer+, password only via PGPASSWORD env), per-key/IP rate
limit (default 120/min, 429), health returns backend + db_ok,
metrics by_status always has all seven status keys.
Worker: per-task timeout (default 1800s, --task-timeout), timeout
is failed not interrupted, limited retries (default 1, backoff,
fallback failures not re-retried), stale codex- container cleanup
on loop/CLI start.
Tests: cloud 271 (267 pass, 4 skipped are pre-existing real-
Postgres tests), host 27 pass.
This commit is contained in:
host committed 2026-10-26 03:55:07 +08:00
1 parent 86160ad175
commit 33841355b5
9 files changed
+1366 -52

No files matched your search

+35
View File
@@ -405,6 +405,41 @@ class DockerExecutor(Executor):
except TypeError:
return self.runner(cmd)
def list_containers(self, prefix="codex-"):
"""列出名字以 prefix 开头的容器(默认 codex-),失败返回 []。
用 `docker ps -a --format {{.Names}}`,输出按行取容器名;
runner 抛异常或输出不可解析时返回空列表,调用方(worker
启动清理)据此只记日志、不炸掉 worker。
"""
try:
result = self._call_runner(
["docker", "ps", "-a", "--format", "{{.Names}}"])
except Exception:
return []
out = getattr(result, "stdout", "") or ""
names = []
for line in str(out).splitlines():
name = line.strip()
if name and name.startswith(prefix):
names.append(name)
return names
def cleanup_stale_containers(self, prefix="codex-"):
"""清理上一轮残留的 codex- 容器:列出并逐个 kill+rm。
返回已清理的容器名列表;单个容器清理失败只跳过它(kill
本身尽力而为),列出失败时返回 [],绝不抛异常。
"""
cleaned = []
for name in self.list_containers(prefix=prefix):
try:
if self.kill_container(name):
cleaned.append(name)
except Exception:
continue
return cleaned
def kill_container(self, task_or_name):
"""杀容器:docker kill + docker rm -f,尽力而为不抛异常。