Skip to content

接入 D3 自动部署 + 服务侧忙锁门禁 - #246

Merged
zj1123581321 merged 24 commits into
mainfrom
card/VideoTranscriptAPI-261015-d3
Oct 16, 2026
Merged

zj1123581321 merged 24 commits into
mainfrom
card/VideoTranscriptAPI-261015-d3

Conversation

@zj1123581321

@zj1123581321 zj1123581321 commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

概述

接入 D3 自动部署。push main 后:构建镜像(docker/Dockerfile,仓根上下文)→ 推 ACR(不可变 12 位短 SHA tag)→ SSH n305(/opt/media/VideoTranscriptAPI)→ 健康探针(/livez)→ 部署;探针失败自动回滚;忙锁门禁启用,服务侧对同一文件持共享 flock。

  • Task-Id: VideoTranscriptAPI-20261015-01(部署链路)+ VideoTranscriptAPI-20261015-02(服务侧忙锁,并入本 PR)
  • 第一轮(552f496e..692bdce5):deploy.yml + compose 种子迁本地 tag 形态 + DEPLOY.md + 进度存档。
  • 第二轮(仲裁落地,692bdce5..8040b6bc):旧手动链路退役(删 pull_and_deploy.sh + pull 脚本测试随退 + 文档收尾)。
  • 第三轮(gate primary 两条 major 裁决就地修复,8040b6bc..fb04dce4):deploy.yml 启用忙锁门禁;DEPLOY.md 灾备兜底修正。
  • 第四轮(服务侧忙锁,fb04dce4..eb75e55b,Task-Id VideoTranscriptAPI-20261015-02):BusyLock + POST /api/transcribe 受理挂接 + 终态释放 + compose 挂载。
  • 第五轮(gate primary 第 3 轮裁决,eb75e55b..418e7210):deploy.yml 增加 post-deploy 忙锁 inode 对账(fail-loud);README 区分 registry 可变 tag 与部署机本地 latest 别名。
  • 第六轮(gate primary 第 4 轮裁决,418e7210..5b35302c):替换前 pre-flight inode 对账(ship needs 它);事后 verify 保留;首次容器重建改走 ssh docker compose up -d。
  • 第七轮(gate primary 第 5 轮裁决,5b35302c..ea6c076c):服务侧忙锁改 fail-closed(锁基础设施异常一律拒绝新任务);verify-busy-lock-inode 失败自动回滚到 pre-flight 记录的镜像锚点;SSH 身份改读 repo variable(ship 输入 + pre-flight/verify env 同源)。
  • 第八轮(gate primary 第 6 轮裁决,ea6c076c..15697bad):回滚 step 改 if: always() + 运行时按对账 outcome 显式分支(触发确定性不依赖条件表达式语义);回滚锚点按 run 隔离(last_good_image.<run_id>,防并发部署覆盖共享锚点);DEPLOY.md 忙锁描述对齐 fail-closed 现实。
  • 第九轮(gate primary 第 7 轮裁决,15697bad..969dfcf2):回滚锚点重构为「last verified good」单一事实源(.deploy-state/last_verified_good,verify 对账通过后原子写,废弃 pre-flight 锚点与 run id 隔离文件);服务侧禁止自建锁基础设施(删 makedirs/O_CREAT,锁文件缺失即拒绝);SSH 会话不可达情形答辩不入码。

服务侧忙锁(第四轮)

  • 挂接点:受理只改 api/routes/tasks.py 的 transcribe_video——create_task 之前非阻塞 acquire_for_task;拿不到 → HTTP 503(deployment in progress, retry shortly),不创建任务行。释放只挂 api/services/terminal_status.py 的 finalize_terminal_status_and_notify(try/finally),覆盖 worker 正常完成与 _fail_task_after_creation。orphan recovery 仍直写 update_task_status,不加锁也不释放(flock 随旧进程死亡消失)。
  • 持锁粒度:每任务一个独立 fd(BusyLock + task_id→fd 映射,dict + threading.Lock)。入队成功后持有至终态。
  • fail-closed(第七轮):.deploy-state 目录不可创建、锁路径非普通文件、flock 的非 BlockingIOError OSError → 该实例 disabled 且 admission 一律拒绝新任务(HTTP 503),一次性 BUSY LOCK WARN 保留(errno 区分 permission/mount 原因);文件内已无 return True——唯一放行=持有共享 flock 且实例未 disabled。VTA_BUSY_LOCK_PATH env 仅供无挂载沙箱/开发环境显式覆盖(集成实验 scripts/perf/local_upload_capacity.py 消费)。窗口期语义(合并→首次人工部署间新任务 503)见 服务侧部署忙锁:上线前自动部署可打断在飞转写任务(deferred gate finding reliability-deploy-during-active-task) #249。
  • compose:docker/docker-compose.deploy.yml 增加 ./.deploy-state:/app/.deploy-state,与部署侧 BUSY_LOCK_FILE 共用同一 inode。

与 live-recorder 先例(zlxlabs/ci-templates build-deploy.yml@v2 caller)的差异清单

项 live-recorder 本仓 理由
触发分支 master main 本仓默认分支是 main
忙锁两个 input 已开启 已开启(busy_lock_file: /opt/media/VideoTranscriptAPI/.deploy-state/busy.lock、busy_lock_timeout: "600") gate primary finding 裁决采纳:锁文件缺失有设计内降级(v2 脚本 684-689 行:WARN + 自动创建后继续)
服务侧持锁 已落地 已落地(本 PR 第四轮) POST /api/transcribe 持共享 flock;无挂载环境 fail-closed 拒绝(VTA_BUSY_LOCK_PATH 显式覆盖口除外)
探针 http://localhost:8080/healthz http://localhost:8200/livez /livez 纯存活恒 200;/health 深检依赖下游,抖动误触发回滚
探针预算 默认(warmup 未设) healthcheck_warmup: "20"、healthcheck_interval: "3"(retries 默认 5) 生产实测启动到 healthy 约 10-25s(startup ~10s 内),warmup 取 2 倍裕度;末次探针 ~35s 覆盖上界
runner 留默认 显式 runner: self 显式钉死免疫上游默认漂移;该形态下 runner 直连 tailnet 目标机,临时入网步骤整体跳过(ci-templates v2 build-deploy.yml 第 223 行 if: inputs.runner == 'hosted')
ssh_user 显式写 repo variable vars.VTA_DEPLOY_SSH_USER(ship 输入 + pre-flight/verify env 三处同源) pre-push public-scan 闸拒绝公开仓明文身份字段;variable 引用不落明文,且消除对 ci-templates default 的隐式耦合;未配置时 pre-flight 第一步失败并给配置指引
compose 种子 天生本地 tag 形态 从 digest-env 迁移 D3 部署脚本拉 ${ACR_IMAGE}:${GIT_SHA} 后本机 retag 成 IMAGE_NAME:latest,compose 与镜像对账都认本地名
secrets 7 个显式传 相同(含 FEISHU_CI_WEBHOOK,未配置时通知层 warning 跳过不影响结论) 对齐上游显式最小权限契约

gate primary findings 处置(第三轮,均采纳就地修复;第四轮闭环服务侧半边)

  1. reliability-deploy-during-active-task:deploy.yml 直接带上忙锁参数(第三轮)。第四轮补上服务侧半边:在飞转写任务对同一文件持共享 flock,部署侧 LOCK_EX 被阻塞直至任务终态。无任务时 LOCK_EX 可立即获取。
  2. correctness-disaster-fallback-pulls-unqualified-image:DEPLOY.md 灾备兜底修正——原 docker compose pull 在本地 tag 形态下会去 registry 找名为 video-transcript-api 的镜像(不存在),改为:日常灾备 docker compose up -d(用宿主已有的本地 tag,由每次 D3 部署 retag 维持);钉定版本时 docker pull crpi-0vsre5argteykh9m.cn-guangzhou.personal.cr.aliyuncs.com/zlx-personal/video-transcript-api:<git-sha> && docker tag ... video-transcript-api:latest && docker compose up -d(ACR 全路径按 build-deploy.yml 组装规则 ACR_REGISTRY/ACR_NAMESPACE/IMAGE_NAME 与本卡实际值推出)。

gate primary 第 3 轮 findings 处置

  1. reliability-deploy-during-active-task(拆两半)
    • (a) 挂载/权限异常静默降级:采纳修复。 deploy.yml 在 ship 成功后新增 verify-busy-lock-inode:同一 SSH 身份检查宿主 .deploy-state/busy.lock 存在、容器 /app/.deploy-state/busy.lock 存在、两者 stat -c %i inode 一致(证明 bind mount 真生效、服务侧与部署侧锁同一 inode)。任一失败则 workflow failure(fail-loud,不静默降级)。reusable workflow 无法在 caller 插入步骤,故对账发生在健康探针之后、workflow 宣告成功之前(新 SSH 会话,同一 host/key/deploy_dir)。
    • (b) 首次自动部署空档:答辩不入码。 旧容器跑的是无 BusyLock 的代码,任何新代码都无法让旧进程持锁,物理上不可消除。缓解:合并后首次部署由主脑 workflow_dispatch 触发(非 push main 自动路径),触发前人工确认非终态任务为 0(tracking issue 服务侧部署忙锁:上线前自动部署可打断在飞转写任务(deferred gate finding reliability-deploy-during-active-task) #249);之后进入自动模式时新容器已持锁。
  2. design-production-latest-policy-contradiction(minor):采纳。README 把「registry 上的可变 tag 禁令」与「部署机本地 retag 别名 video-transcript-api:latest」分开写——registry 可变 tag 仍禁;本地别名是 D3 部署脚本 retag 的对接点,仅存在于部署机,不入 registry。

gate primary 第 4 轮 findings 处置

主审「inode 对账是事后报警、替换前不保护」成立,采纳事前门禁。

  1. 事前 pre-flight:ship 以 needs: pre-flight 卡住。对当前运行容器做替换前验证——docker exec 容器内 /app/.deploy-state/busy.lock 与宿主 <deploy_dir>/.deploy-state/busy.lock 的 stat -c %i 对账。容器不存在、容器内路径不存在(旧容器无挂载,即首次部署场景)、或 inode 不一致 → pre-flight 失败、ship 不执行(fail-closed,部署 defer,人工介入)。
  2. 事后 verify-busy-lock-inode:保留不动,对账新容器。事前+事后双闸。
  3. 首次部署走人工通道:首次容器重建由主脑 ssh 执行 docker compose up -d(部署前确认无非终态任务),不走 deploy.yml。pre-flight 从第二次部署起自然通过。详见 服务侧部署忙锁:上线前自动部署可打断在飞转写任务(deferred gate finding reliability-deploy-during-active-task) #249。

gate primary 第 5 轮 findings 处置(第七轮,5b35302c..ea6c076c)

主审五轮收紧的终点是 fail-closed + 事后回滚 + 稳定身份,三条全部采纳就地修复。

  1. fail-open 互斥失效 → 服务侧 fail-closed:busy_lock.py 全部基础设施异常路径(实例 disabled 短路、锁路径非普通文件、目录不可创建/打开失败的 OSError、flock 的非 BlockingIOError OSError)一律 admission False(拒绝新任务 → HTTP 503),一次性 BUSY LOCK WARN 保留并按 errno 区分 permission/mount 原因;BlockingIOError(部署真持有排他锁)路径维持 False 不变。挂接点 503 detail 细化为 service busy or lock unavailable, retry shortly(同一拒绝路径)。文件内 return True 计数 0。红验:init-OSError 分支注入 return True → test_uncreatable_path_disables_and_denies_once 转红(assert True is False),仅还原注入行。
  2. 事后校验失败不回滚 → 自动回滚:pre-flight 在 inode 对账全部通过后把当前运行镜像 ID 写入宿主 .deploy-state/last_good_image(跨 job 回滚锚点,与上游 STATE_DIR 同目录);verify-busy-lock-inode 失败时自动 retag 锚点镜像为 video-transcript-api:latest 并 cd deploy_dir && docker compose up -d(与上游 deploy_tag 完全同机制:本地 tag 已存在即不触发 pull),复查容器镜像 ID==锚点;锚点缺失/格式坏/回滚任一步失败 → rollback_failed 错误行 + job 双重失败,不吞错。回滚成功不改变 job 结论(对账失败已红)——坏镜像曾上线的事实不被回滚洗掉;不自动重试部署,回滚后人工判断。
  3. ssh_user 正则解析 ci-templates default → repo variable:删除两个 job 的 ci-templates checkout + 正则解析步骤;workflow 级 env.SSH_USER: vars.VTA_DEPLOY_SSH_USER 供 pre-flight/verify 的 SSH 使用;ship 的 ssh_user: 输入钉同一变量(消除对上游 default 的隐式耦合)。变量未配置时 pre-flight 第一步即失败并给配置指引(不会带空用户名碰生产)。合并前动作(主脑):Settings > Secrets and variables > Actions > Variables 配置 VTA_DEPLOY_SSH_USER=zlx。

窗口期语义:fail-closed 后「合并 → 首次人工部署」窗口内新任务一律 503(分钟级,主脑控制窗口时长),已在 #249 记录。

gate primary 第 6 轮 findings 处置(第八轮,ea6c076c..15697bad)

三条裁决均采纳。根因先行:deploy.yml 仅在 main push 触发(gh run list --branch 上只有 CI/gate-shadow/Browser regression),PR 分支从未有过 deploy run——本轮 finding 来自模型主审对 YAML 的静态审查,非实际 run 观测。

  1. reliability-post-verify-no-rollback:上轮回滚 step 用自定义条件 if: steps.verify.outcome == 'failure'。按 GH 文档语义该条件会在对账 step 失败时为真、回滚会运行,但确有真实盲区:对账 step 被跳过时 outcome='skipped' 条件为假,且触发路径静态不可读(主审无法从 YAML 直观确证回滚必然可达)。按裁决改为 if: always() + step 内运行时显式分支:读取对账 step 的 outcome,failure → 执行回滚;success/skipped/cancelled → 空转成功(job 结论不变,不在不可达或半途状态上做无把握回滚)。回滚可达性不再依赖条件表达式语义,if: always() 可 grep。
  2. reliability-rollback-anchor-race:回滚锚点按 run 隔离——pre-flight 对账通过后写 .deploy-state/last_good_image.<run_id>,verify 失败回滚只读本 run 的文件(RUN_ID=GITHUB_RUN_ID 经 ssh 前缀传入远端,两端各加 ^[0-9]+$ 校验),接续/并发部署不再可能覆盖共享锚点导致回滚到错镜像。串行化由 ship 内部既有 host 级部署锁保证,未新增锁。清理逻辑不加(卡面授权项):每文件 ~80B、部署为分钟级人工节奏、读侧 run 隔离使陈旧文件无语义影响;将来需要时可在 pre-flight 写前 prune 保留最近 N=3(需配合 host 级并发组时序),当前规模不值得。
  3. design-preflight-lock-doc-contradiction(minor):docker/DEPLOY.md 删除「落地前锁文件由部署脚本自动创建(WARN 后继续,门禁空转)」旧行为描述,改为与 fail-closed 现实一致:服务侧锁基础设施异常一律拒绝新任务(HTTP 503);宿主锁文件缺失时 pre-flight 对账即失败,自动部署 fail-closed 延期、人工介入;事后对账失败自动回滚锚点镜像。

gate primary 第 7 轮 findings 处置(第九轮,15697bad..969dfcf2)

三条裁决:①②采纳修复,③答辩不入码。

  1. reliability-rollback-anchor-race → 锚点语义重构(last verified good 单一事实源):第八轮的「pre-flight 记录运行镜像」方案有设计缺陷——pre-flight 时旧容器健康状况从未被验证,锚点可能采到不健康版本。重构:verify-busy-lock-inode 对账通过后在宿主 tmp+rename 原子写 .deploy-state/last_verified_good(内容=新容器镜像引用,写前 sha256 格式校验);回滚一律只读该文件,缺失/格式坏 → rollback_failed(fail-loud 人工介入);pre-flight 不再写任何锚点文件,run id 隔离文件代码(RUN_ID 传参/校验/文件名后缀)全部删除。并发时序正确性:ship 的 host 部署锁串行化容器替换,各自 verify 在自己 ship 之后写锚点——A 对账成功写 X、B 替换 Z 后对账失败 → B 回滚到 X(上一验证好版本),不会采到未验证版本。
  2. reliability-busy-lock-missing-path-fail-open → 服务侧禁止自建锁基础设施:busy_lock.py 删除 os.makedirs 与 O_CREAT,改为只读打开既有文件(os.O_RDONLY);ENOENT(锁文件或其目录缺失)→ 实例 disabled、admission False、一次性 WARN「lock file missing — deployment mount likely broken」。理由:自建文件落在容器层时与宿主部署锁不同 inode,互斥静默失效,等价 fail-open。运维契约:宿主锁文件由部署侧预建(首次人工部署步骤含 touch busy.lock,已同步 issue 服务侧部署忙锁:上线前自动部署可打断在飞转写任务(deferred gate finding reliability-deploy-during-active-task) #249 由主脑更新);契约写入代码注释。红验:向 acquire 注入旧自建行为 → test_missing_lock_file_denies_and_warns 转红 assert True is False,仅还原注入行。
  3. reliability-post-verify-no-rollback(minor,SSH 会话无法建立)→ 答辩不入码:对账/回滚共用同一 SSH 通道(同 host/key/deploy_dir),会话不可达时回滚自身同样无法建立连接,物理上不存在代码可修复的通道——任何「降级重试」只会把同一网络故障重放一遍。缓解已是完备态:verify job 红(workflow 失败可见)+ 飞书 CI 告警(ship 通知链路已有)。人工通道:DEPLOY.md 灾备兜底(宿主手工 docker compose up -d 回本地 tag)。

gate primary 第 9 轮 findings 处置(第十轮,969dfcf2..f8dc283b)

三条裁决:major 采纳修复、minor(终态锁持有时长)采纳修复、minor(post-verify 候选容器继续运行)答辩保持现状。

  1. reliability-rollback-bypasses-busy-lock(major)→ 回滚先拿忙锁:回滚也是容器替换,绕过忙锁即可打断在飞任务。回滚 step 在 docker compose up -d 前对 .deploy-state/busy.lock 申请排他锁,打开方式与上游 pull_and_deploy.sh 一致(只读 fd——宿主部署用户对锁文件常只有读权限,锁互斥作用在 inode 上,只读 fd 即可申请 LOCK_EX);flock -w 600 预算与 ship 的 busy_lock_timeout: "600" 对齐。rc=1(预算耗尽、任务仍持共享锁)→ ::error::rollback_deferred_busy + job 失败(fail-loud 人工介入,不静默放弃回滚、不打断任务);其余非零(锁命令本身故障,非锁竞争)→ rollback_failed 报真实错误;拿到锁 → retag + compose up + 镜像对账 + flock -u 显式释放(失败路径靠 SSH 会话退出时内核释放,与上游 exit 释放同一保证)。锁文件缺失 → rollback_failed(pre-flight/verify 双闸已证明它该在,缺失即环境异常 fail-closed)。容器已在锚点镜像上时跳过替换且不拿锁(无替换即无打断面,不必要地关 admission 反而伤害门禁本该保护的对象)。
  2. reliability-terminal-notify-holds-busy-lock(minor)→ 终态写入后提前释放:finalize_terminal_status_and_notify 原先函数尾部 finally 释放忙锁,通知投递(含其重试队列)全程占锁,拉长部署侧排他锁等待。改为:CAS 写入段(含 slow_alert.cancel_all)except BaseException 兜底释放后原样上抛(终态未落库的异常路径);CAS 丢失路径显式释放后 return False;CAS 赢得后、deliver_terminal_notification 之前释放——此后任何路径(suppress/defer 早退、投递)都不再持锁;通知投递失败仍由 deliver 自行记录不吞(既有语义不变)。时序不变式由测试锁定(见验证证据)。
  3. reliability-post-verify-candidate-container-kept(minor)→ 答辩保持现状:post-verify 失败后候选容器继续运行、自动恢复靠人工。理由:回滚已是尽力路径——忙锁被任务占满时强行回滚正是本轮 finding 1 要消除的行为(回滚等待 600s 后 rollback_deferred_busy 人工介入);verify 失败本身已让 job 变红 + 飞书 CI 告警可见。候选容器运行在健康探针已通过、仅 inode 对账失败的镜像上,此刻强行替换换锚点镜像的收益,不抵再次打断在飞任务的代价。

gate primary 第 11 轮 findings 处置

三条裁决:1 major 采纳修复、1 minor 采纳修复、1 minor 答辩保持现状。

  1. reliability-post-verify-candidate-identity-unverified(major)→ verify 镜像身份校验:verify-busy-lock-inode 作业最前面新增当前运行容器镜像身份与本 run 部署候选镜像比对。候选引用即 ship 部署的 video-transcript-api:latest 所指向的本地候选镜像 ID(由 docker inspect --format='{{.Id}}' 读取);当前运行容器镜像由 docker inspect --format='{{.Image}}' 读取。比对不匹配(含手工灾备插队部署其他镜像)→ 报 deploy_identity_mismatch 并 job 失败,且不回滚、不写锚点(当前运行的可能是 owner 人工合法部署,回滚/覆盖才是破坏);比对匹配 → 继续进行 busy.lock inode 对账与 last_verified_good 锚点原子写入。效果:锚点只可能记录「身份确认过的本 run 候选 + inode 对账通过」的镜像。
  2. reliability-post-cas-busy-lock-leak(minor)→ 终态写入后锁泄漏幂等兜底:terminal_status.py 的 finalize_terminal_status_and_notify 中,CAS 成功路径(含 suppress/defer 早退与通知投递)外再包一层 try/finally 调用 release_for_task(task_id) 进行幂等兜底(release 对未知 task_id 已是 no-op)。既有显式释放点保留(首释放生效、兜底 no-op),确保任何 post-CAS 异常路径都不可能永久持锁。测试锁定该时序不变式。
  3. reliability-readonly-fd-flock-exclusive(minor)→ 答辩保持现状:只读 fd 申请 LOCK_EX 是上游 ci-templates pull_and_deploy.sh 的刻意设计(注释明确:flock(2) 互斥作用在 inode 上、不要求写权限 fd;改读写会要求部署用户对锁文件有写权限、在真实权限场景反而稳定失败)。Linux 本地 bind mount 上语义确定,live-recorder 同模式已在生产稳定运行。

旧链路仲裁结果(第二轮,2026-10-15 主脑裁决)

第一轮 make test 曾出现 13 个新红(tests/deployment/test_pull_and_deploy.py),根因:旧 docker/pull_and_deploy.sh 把同目录种子当部署目录自迁移模板、要求 digest-env 形态,与本地 tag 形态种子互斥。主脑裁决:接受种子迁本地 tag 形态(锁定决策①维持),旧手动链路退役。落地:删除 docker/pull_and_deploy.sh;test_pull_and_deploy.py → test_push_to_ghcr.py(保留 build_script 守卫 + 2 个种子 compose 静态守卫);test_healthcheck_route.py 删 legacy fallback 测试(无孤立 import);README/DEPLOY.md 收尾 + 灾备兜底。

验证证据

  • 第十一轮:uv run pytest tests/unit/test_busy_lock.py -q → 15 passed 全过(新增 post-CAS 异常路径锁释放用例 test_finalize_releases_lock_on_post_cas_exception)。yaml parse ok;grep -c "deploy_identity_mismatch" .github/workflows/deploy.yml => 1。红验转红原文:E AssertionError: assert not True + E + where True = _host_ex_blocked(...)(注入=移除 finally 兜底,仅该用例红、其余 14 绿)。make test 全量唯一失败=继承红 AF_UNIX,新红 0。
  • 第十轮:uv run pytest tests/unit/test_busy_lock.py → 14 用例全过(新增时序用例 2:投递探针内断言锁已释放、CAS-loss/defer 早退路径锁释放)。make test 全量唯一失败=继承红 AF_UNIX(base 52fbbd4 临时 detached worktree 同红复核),新红 0。yaml parse ok。Accept-Check:rollback_deferred_busy 计数 2(错误行+注释)、flock 计数 3(flock -w 600 -x 8、flock -u 8、rc 分流错误行——预估 2,实现为准)。红验转红原文:E assert [False] == [True](test_finalize_releases_lock_before_notification_delivery,注入=提前释放点移到投递后、suppress/defer 路径补迟释放保证最终态一致,仅该用例红、其余 13 绿)。
  • 第九轮:uv run pytest tests/unit/test_busy_lock.py -q → 13 用例全过(新增「文件不存在→拒绝+WARN」用例;依赖服务侧自建文件的用例全部改为 _provision 预建 fixture——测试扮演部署侧,服务侧只认既有文件)。make test 全量重跑(本轮触 Python 代码):唯一失败=继承红 AF_UNIX,新红 0。yaml parse ok;actionlint 8 条 info 全为既有类别。涟漪同步(越界申报,与第七轮同类):test_api_routes.py/test_processing_options.py 钉路径 fixture 各补 touch 一行、local_upload_capacity.py 沙箱预建锁文件(mkdir+touch)——服务侧禁自建后「只钉路径不建文件」的测试支撑全部会 503。红验转红原文:E assert True is False(+where True = acquire())。
  • 第八轮:yaml parse ok;actionlint 8 条 info 全为既有类别(codex 自定义 label ×2、ssh 行 SC2086/SC2029 同款风格 ×6),无新增。Accept-Check:if: always() 计数 1;last_good_image 计数 3(含 run-id 后缀文件名两处);RUN_ID='${GITHUB_RUN_ID}' 传参两处可 grep。本轮仅改 deploy.yml 与 DEPLOY.md,不触 Python 测试面,未重跑 make test(第七轮全量基线:唯一失败=继承红 AF_UNIX,新红 0)。红验不适用(本轮无生产代码语义注入面;回滚触发语义由 always() + 运行时分支的静态结构保证)。
  • 第七轮:uv run pytest tests/unit/test_busy_lock.py -q → 11 用例全过(含新增 flock OSError 拒绝用例);make test 全量唯一失败=继承红 AF_UNIX;yaml parse ok,actionlint 仅既有风格 info(codex 自定义 label、SSH_OPTS 刻意分词,非本次引入)。Accept-Check:busy_lock.py 内 return True 计数 0;VTA_DEPLOY_SSH_USER 计数 2(workflow env + ship 输入);rollback_failed / deployment in progress 失败可见性标记在位。
  • 第七轮 fail-closed 测试涟漪同步:此前依赖 fail-open 放行的用例(本机无 /app 挂载→基础设施故障→旧行为放行)共 20 条转 503——test_api_routes.py(15,模块级 autouse fixture 钉 BUSY_LOCK_PATH 到 tmp,3 个类各自内联建 client)、test_processing_options.py(4,client fixture 同法)、scripts/perf/local_upload_capacity.py 沙箱(clean_env 注入 VTA_BUSY_LOCK_PATH);断言零改动,收敛后新红 0。
  • 第六轮:yaml parse ok;Accept-Check:pre-flight 可 grep、needs: pre-flight 计数 1、busy_lock_file 计数 1。verify-busy-lock-inode 保留。
  • 第五轮:yaml parse ok;Accept-Check:busy_lock_file 计数 1(≥1)、stat -c %i 可 grep、verify-busy-lock-inode 可 grep。README 变更不触测试。
  • 第四轮:uv run pytest tests/unit/test_busy_lock.py -q → 10 passed。真实 fcntl:LOCK_EX 持有时 admission False / HTTP 503 且不 create_task;释放后 202 且 LOCK_EX 被 SH 挡住;终态写入后 LOCK_EX 可获取。红验:acquire 在 BlockingIOError 下改 return True → assert True is False 与 assert 200 == 503。既有 tests/unit/test_api_routes.py 零改动通过。make test 4003 collected,唯一失败为继承红 AF_UNIX。
  • 第三轮:两 YAML parse ok;Accept-Check(忙锁启用后口径):busy_lock_file 计数 1(≥1)、livez 计数 1、本地 tag 计数 1、digest env 计数 0;DEPLOY.md 无 compose pull && 残留。
  • 第二轮:make test 全量唯一红为继承红 AF_UNIX;13 个新红消失;tests/deployment/ 4 项全绿。
  • 第一轮 Accept-Check 与红验:yaml parse 闸单行最小注入 → expected ',' or ']', but got '-' 转红 → 单行还原。

红红分离(最终态)

  • 继承红(1):tests/unit/test_asr_monitor_alert_delivery.py::TestNetworkGuard::test_unix_domain_socket_is_not_blocked(AF_UNIX path too long;主干基线 gh api 不可用,按卡面声明接受为 base 同红)。
  • 新红(0)。

后续(不在本卡范围)

  1. 主脑合并后迁移 n305 部署目录 compose 到本仓种子形态(现网仍是 digest-env),再 ssh 执行 docker compose up -d 做首次容器重建(不要走 deploy.yml / workflow_dispatch)。触发前人工确认非终态任务为 0,并确认宿主 .deploy-state/busy.lock 存在(没有则 touch)。此后自动路径的 pre-flight 才会通过(tracking issue 服务侧部署忙锁:上线前自动部署可打断在飞转写任务(deferred gate finding reliability-deploy-during-active-task) #249)。
  2. TS_AUTHKEY 按上游 secrets 契约仍需配齐(runner: self 时部署运行时不消费,但 caller 契约要求显式传参)。

第 12 轮后收口(合并后补记)

合并后 dispatch run 38834509231 实测 pre-flight SSH Host key verification failed(VM201 runner 不在 tailnet,100.64/10 走公网路由);pre-flight/verify 前置 Tailscale 入网的修复转 PR #252(tracking #249)。

zj1123581321 and others added 8 commits October 15, 2026 23:54
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-151234-61378b
Task-Id: VideoTranscriptAPI-20261015-01
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-151234-61378b
Task-Id: VideoTranscriptAPI-20261015-01
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-151234-61378b
Task-Id: VideoTranscriptAPI-20261015-01
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-151234-61378b
Task-Id: VideoTranscriptAPI-20261015-01
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-151234-61378b
Task-Id: VideoTranscriptAPI-20261015-01
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-160700-3a051b
Task-Id: VideoTranscriptAPI-20261015-01
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-160700-3a051b
Task-Id: VideoTranscriptAPI-20261015-01
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-160700-3a051b
Task-Id: VideoTranscriptAPI-20261015-01
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Required Gate v2 — 状态面板

当前状态:fail · 要修代码

当前裁决:code_fail / primary_findings

PRIMARY-FINDINGS — 主审发现(4 / 4 条)

severity id location title
major correctness-verify-manual-deploy-race .github/workflows/deploy.yml 新增的一次性身份检查没有消除检查与使用之间的 TOCTOU 竞态,可能把未由本次 run 部署的手工镜像静默认定为本次部署结果,并污染回滚事实源。
major correctness-candidate-sha-check-skipped .github/workflows/deploy.yml 关键的本次 run 候选身份校验在镜像标签被清理或缺失时静默降级为仅比较 latest,无法保证运行镜像确实对应当前提交。
minor reliability-terminal-cas-loss-lock-leak src/video_transcript_api/api/services/terminal_status.py CAS 丢失后的异常路径仍可能泄漏任务忙锁,导致后续部署等待 600 秒并拒绝新任务。
minor reliability-rollback-releases-lock-before-health .github/workflows/deploy.yml 回滚健康检查期间可能有新任务被受理并进入尚未恢复的服务,造成任务失败或不一致。

PR 累计主审轮(影子):12/5 · arbitration_required

Gate 历史(v1;来源为持久化 gate_terminal 制品)

Run Attempt Head 状态 PR 累计主审轮(影子) 收件人动作
38764788505 1 692bdce unavailable — 修基础设施
38768890591 1 8040b6b fail — 要修代码
38770699923 1 fb04dce fail — 要修代码
38780718944 1 eb75e55 fail — 要修代码
38783785355 1 418e721 fail — 要修代码
38785415385 1 5b35302 fail — 要修代码
38790775392 1 ea6c076 fail — 要修代码
38792914446 1 15697ba fail — 要修代码
38795814304 1 969dfcf fail — 要修代码
38797958088 1 52fbbd4 fail — 要修代码
38800859790 1 f8dc283 fail — 要修代码
38803485243 1 edce824 fail — 要修代码
38806596908 1 80c6722 fail 12/5 · arbitration_required 要修代码

历史行按 run_id + run_attempt 去重并只增不删;删除本评论后可由 gate_terminal 制品重建。

Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-163618-62a47e
Task-Id: VideoTranscriptAPI-20261015-01
zj1123581321 added 2 commits October 16, 2026 01:40
POST /api/transcribe 在 create_task 之前非阻塞申请 LOCK_SH,拿不到返回 503;
入队成功后按 task_id 持独立 fd,终态入口释放。无挂载环境 disabled 放行并打一次性 BUSY LOCK WARN。

Agent-Executor: grok-pi
Agent-Model: grok-4.6
Agent-Effort: high
Dispatch-Id: dlg-20261015-173031-4ced47
Task-Id: VideoTranscriptAPI-20261015-02
Agent-Executor: grok-pi
Agent-Model: grok-4.6
Agent-Effort: high
Dispatch-Id: dlg-20261015-173031-4ced47
Task-Id: VideoTranscriptAPI-20261015-02
@zj1123581321 zj1123581321 changed the title 接入 D3 自动部署:deploy.yml + compose 种子迁移本地 tag 形态(第一阶段,无忙锁) 接入 D3 自动部署 + 服务侧忙锁门禁 Oct 15, 2026
zj1123581321 and others added 13 commits October 16, 2026 02:12
Agent-Executor: grok-pi
Agent-Model: grok-4.6
Agent-Effort: high
Dispatch-Id: dlg-20261015-180253-09c8dc
Task-Id: VideoTranscriptAPI-20261015-02
Agent-Executor: grok-pi
Agent-Model: grok-4.6
Agent-Effort: high
Dispatch-Id: dlg-20261015-182148-ad0d7a
Task-Id: VideoTranscriptAPI-20261015-02
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-183602-01b626
Task-Id: VideoTranscriptAPI-20261015-04
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-183602-01b626
Task-Id: VideoTranscriptAPI-20261015-04
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-183602-01b626
Task-Id: VideoTranscriptAPI-20261015-04
…closed

Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-191411-4d6f2c
Task-Id: VideoTranscriptAPI-20261015-04
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-193030-83b646
Task-Id: VideoTranscriptAPI-20261015-04
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-193030-83b646
Task-Id: VideoTranscriptAPI-20261015-04
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-194955-f48af5
Task-Id: VideoTranscriptAPI-20261015-05
Agent-Executor: glm-pi
Agent-Model: glm-5.3-flash
Agent-Effort: high
Dispatch-Id: dlg-20261015-200530-d8d5ce
Task-Id: VideoTranscriptAPI-20261015-06
Agent-Executor: agy
Agent-Model: gemini-3.8-flash-high
Agent-Effort: unknown
Dispatch-Id: dlg-20261015-202842-9e9ebf
Task-Id: VideoTranscriptAPI-20261015-07
Agent-Executor: agy
Agent-Model: gemini-3.8-flash-high
Agent-Effort: unknown
Dispatch-Id: dlg-20261015-204702-c2f1d8
Task-Id: VideoTranscriptAPI-20261015-08
Agent-Executor: agy
Agent-Model: gemini-3.8-flash-high
Agent-Effort: unknown
Dispatch-Id: dlg-20261015-204702-c2f1d8
Task-Id: VideoTranscriptAPI-20261015-08
@zj1123581321
zj1123581321 merged commit 6080131 into main Oct 16, 2026
11 of 13 checks passed
@zj1123581321
zj1123581321 deleted the card/VideoTranscriptAPI-261015-d3 branch October 16, 2026 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant