HA 阶段 1:worktree_host + 跨主机 /pr 队列化 (#50)

* 部署自动化:唯一发布链路 deploy.py + host_deploy.sh,sha/commit 双重验真,失败回滚

旧链路三次栽过的坑:通用 tar 路径导致旧包误部署、传输不验 sha、
主机脚本多份漂移、health 通过不等于新代码上线。本单把四点变硬门禁:
- 产物唯一命名 codex-app-server-<commit>.tar.gz + .sha256,远端唯一路径
- git archive 产物 + DEPLOYED_COMMIT 清单,默认拒绝脏工作区
- 传输链 scp -> scp_run -> chunk 分片,每路传完远端验 sha,不符删掉重来
- 主机侧唯一脚本:先验 sha/清单,staging 解包 + 编译检查,换目录上线,
  work/ 原样保留,health + 上线 commit 不符即回滚并退出非零
- deploy.py 成功后独立 ssh 再读一次 live 清单二次核对
- CI:host-verify 加 deploy/tests;release.yml 只在手动/tag + production
  environment 审批时部署,主干推送绝不自动部署生产
- deploy.sh 标记为旧链路弃用
- health 的 commit 字段已在父提交 HA 阶段 0 里随 server.py 落地,
  本分支整体包含它;commit 为空时只靠文件核对,门禁同样成立
测试:host 32、cloud 487(4 跳过)、deploy 9,全部通过;
host_deploy.sh 仿真覆盖成功/坏 sha/清单不符/health commit 不符回滚。
生产实机部署未做,等确认后再用新链路推。

* HA stage 1: worktree_host + queued cross-host PR jobs

---------

Co-authored-by: host <host@test.local>
This commit is contained in:
openqjandhost authored and GitHub committed 2026-10-27 00:05:18 +08:00
1 parent 455513dd33
commit fc8e337d0f
14 files changed
+1639 -67

No files matched your search

+1
View File
@@ -13,3 +13,4 @@ jobs:
python-version: "3.12"
- run: python3 -m unittest discover -s tests -v
- run: python3 -m unittest discover -s cloud/tests -v
- run: python3 -m unittest discover -s deploy/tests -v
+95
View File
@@ -0,0 +1,95 @@
name: release
# 发布链路:主干推送绝不自动部署生产。
# 触发只有两种,两种都要经过 host-verify 同款测试:
# 1. 手动 workflow_dispatch(在 Actions 里点 Run,选 environment: production,
# GitHub 的 environment 审批人要点批准才会继续,见仓库 Settings > Environments)。
# 2. 打 tag v*(例如 v0.2.0),同样进 production environment 审批。
# 部署本身不由 Actions 直接拼 ssh 命令,而是复用 deploy/deploy.py:
# - 构建产物来自 git archive,含 DEPLOYED_COMMIT + sha256;
# - 传输、远端验 sha、主机 staging 换目录、失败回滚、RELEASE_OK 核对
# 全在 deploy.py / host_deploy.sh 里,Actions 只负责“何时触发 + 审批”。
# SSH 凭据只走 secrets,不写进日志:DEPLOY_HOST、DEPLOY_USER、
# DEPLOY_SSH_KEY(私钥内容,Actions 里写 ~/.ssh/id_ed25519,0600)。
# 没有配这些 secrets 时,build 作业仍产出可下载的发布包 artifact,
# deploy 作业会明确失败并提示“未配部署凭据,改走 VM 侧 deploy.py”,
# 绝不静默跳过部署又报成功。
on:
workflow_dispatch:
inputs:
ref:
description: "要部署的 ref(默认 main 的 HEAD)"
required: false
default: "main"
push:
tags:
- "v*"
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
outputs:
commit: ${{ steps.build.outputs.commit }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: 测试(与 host-verify 同款,失败则不产发布包)
run: |
python3 -m unittest discover -s tests -v
python3 -m unittest discover -s cloud/tests -v
python3 -m unittest discover -s deploy/tests -v
- name: 构建发布包(git archive + DEPLOYED_COMMIT + sha256)
id: build
run: |
python3 deploy/deploy.py --build-only --skip-tests \
--ref "${{ inputs.ref || github.ref }}" \
--out-dir "$GITHUB_WORKSPACE/release-artifact"
echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
ls -lh release-artifact/
- name: 上传发布包 artifact
uses: actions/upload-artifact@v4
with:
name: codex-app-server-release
path: release-artifact/
retention-days: 30
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: 下载发布包
uses: actions/download-artifact@v4
with:
name: codex-app-server-release
path: release-artifact
- name: 检查部署凭据是否齐全
run: |
if [ -z "${{ secrets.DEPLOY_HOST }}" ] || [ -z "${{ secrets.DEPLOY_SSH_KEY }}" ]; then
echo "未配 DEPLOY_HOST / DEPLOY_SSH_KEY,无法自动部署生产。" >&2
echo "发布包已在 artifact 里,改走 VM 侧:python3 deploy/deploy.py <host> --tarball <包> --skip-tests" >&2
exit 1
fi
- name: 写 SSH key(0600,不打印)
run: |
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
- name: 部署(deploy.py:传输验 sha + 主机换目录 + 回滚 + 双重核对)
env:
SSH_PROXY_CMD: ""
run: |
TARBALL=$(ls release-artifact/*.tar.gz | head -n 1)
DEPLOY_USER="${{ secrets.DEPLOY_USER }}"
python3 deploy/deploy.py "${{ secrets.DEPLOY_HOST }}" \
"${DEPLOY_USER:-root}" \
--tarball "$TARBALL" --skip-tests