merge: candidate test-single-binary-2026-09-27 into dev

This commit is contained in:
weichen
2026-09-27 16:21:34 +00:00
3 changed files with 278 additions and 0 deletions
@@ -0,0 +1,80 @@
---
title: "dev→test 晋升(rc-v1.1.0-beta.4)与 test 单二进制化"
task_id: test-single-binary-2026-09-27
status: in_progress
created: 2026-09-27
updated: 2026-09-27
owner: pi
task_type: ops
task_tier: multi_stage
policy_revision: "AGENTS+worktree-ops @ 05d45022"
branch: feature/test-single-binary-2026-09-27
worktree: .worktrees/test-single-binary-2026-09-27
base_dev_sha: "05d45022b142a8a4c64a001618f6691093447f01"
write_scope:
- scripts/migrate-test-db.sh
- scripts/launch-single.sh
- .doc/ops/short-term/2026-09-27-test-single-binary.md
- .doc/test-reports/2026-09-27-test-single-binary-test-report.md
- .doc/test-reports/assets/2026-09-27-test-single-binary
- .doc/ops/CURRENT.md
allowed_environments: ["dev", "test"]
capabilities: [docs:publish, env:dev, env:test, git:dev, git:test]
mutex: env:dev+env:test+git:dev+git:test
review_state: pending
integration_state: pending
deployment: pending
cleanup: pending
remote_sync: not_requested
---
# dev→test 晋升与 test 单二进制化
## 用户目标与授权
- 用户目标:把 `dev` 合入 `test`;并将 test 环境转换为单二进制服务(与 dev 同形态)。
- 授权来源:用户本会话明确指令(含 test 晋升与环境写操作)。未要求 push,远端同步不做。
- 交付终点:test 分支 rc 晋升 + test 环境以单二进制形态运行并通过验收;任务记录、测试报告、CURRENT 收口。
## 背景与方案
- dev 已于 2026-09-24 完成单二进制化(localpg managed :5433 + PI sidecar :18865 + `launch.sh --no-docker`);设计文档 P6 即 test 部署演练。
- 当前 test 仍为 4 容器(app/events/pi-agent/system-pg,schema 66);dev 领先 test 273 提交(dev schema 76)。
- 方案(遵循设计 §4.2,PG 取 managed——单机测试机):
1. 脚本落地:`scripts/migrate-test-db.sh`(复用参数化的 migrate-dev-db.sh,钉 test 默认值)+ `scripts/launch-single.sh`(非 dev 环境单二进制启动入口:SYSTEM_DB_*→CONTEXT_DB_* 映射、环境专属 sidecar、无 Vite)。
2. 脚本合入 dev 后跑全面门禁(arch-check / go vet / go test -short / npm test+build / scripts unittest)。
3. 真实 PI nightly(五例)@ dev tip → promotion receipt(PASS + digests)。
4. 晋升:任务 worktree 制备候选 M(P1=旧 test tip,`--no-ff` 合 dev tip)→ validate → `git:test` 租约内 update-ref + `rc-v1.1.0-beta.4` + candidate-images-apply test(compose 镜像 pin 保留为回滚路径)。
5. test 转换(`env:test` 租约,主目录执行):S0 快照 → `migrate-test-db.sh`(容器 dump → localpg :5434,PGDATA=data/test/pg,停 system-db 容器、volume 保留)→ 停 app/events/pi-agent 容器 → 从 rc tag `git archive` 构建 `bin/agent-server-test`(sha256 入报告)→ `launch-single.sh test`。
6. 验收:/health /ready(schema ≥76)、embed SPA :8081、PI sidecar health、test 真实 Runner 聊天链路、回滚路径说明。
- 端口/数据/模型:8081 / 5434 / 18866;data/test;MiniMax-M2.7-highspeed(现行 test pin,configs/.env.test);SEED_DEMO_USERS=false。
## 非目标
- main 环境任何变更;FAV live 放量;push 远端;改 gap-matrix 门禁结论(仅按既有模式追加本轮证据区段)。
- 前端/后端业务代码改动(如遇门禁失败按问题处理流程另立 fix 任务)。
## 相关问题与经验
- 问题台账/经验检索:「单二进制/localpg/no-docker」无直接命中;采纳 `LESSON-20260927`(隔离库配方、避开 5433/5434/5435 子串端口——隔离 gate-pg 用 5436;worktree 测试显式钉 DB)。
- 遗留现场(本任务发现的待收口,非本任务造成):`integrate-upload-doc-access-2026-09-27` 仍 active,其 env:dev 租约有 recovery_required run(进程组含在运行的 dev agent-server PID 2636482,不能回收);dev server 正常服务中。处置:本任务收口时经 launch.sh 重启 dev server 脱离旧进程组后 recover 并关闭该任务,或登记给后续维护者。
## 步骤与验收
1. [x] 建 worktree / 注册 / CURRENT 认领
2. [x] 脚本落地(migrate-test-db.sh / launch-single.sh)
3. [ ] worktree 验证(bash -n、scripts unittest、门禁套件)
4. [ ] 集成入 dev(候选 + validate + integrate)
5. [ ] dev tip 全面门禁 + nightly 真实 PI + receipt + candidate images
6. [ ] test 晋升(候选 M + rc-v1.1.0-beta.4)
7. [ ] test 单二进制转换 + 验收
8. [ ] 报告 / CURRENT / 收口
## 验证与证据
见 [测试报告](../../test-reports/2026-09-27-test-single-binary-test-report.md)(执行后填写)。
## 收口与交接
- 分别记录 review_state / integration_state / deployment / cleanup / remote_sync。
- 回滚(任一步失败且不可前滚时):容器路径 `docker compose -f docker-compose.test.yml -p agent-test up -d`(volumes 未删);DB 回滚用 S0 快照 restore 到容器 PG。
+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env bash
# launch-single.sh — 非容器单二进制启动入口(非 dev 环境;当前用于 test)
#
# 与 launch.sh --no-docker(dev 专用:含 Vite/热更)相对,本脚本服务长期部署形态:
# - PI sidecar(环境专属端口/工作区)+ agent-server 单二进制(embed 静态,无 Vite)
# - LOCALPG_MODE 默认 managed(PGDATA=$DATA_DIR/pg,端口沿用环境约定)
# - 从 configs/.env.<env> + 根 .env 分层加载配置;SYSTEM_DB_*(compose 约定)
# 在此映射为宿主运行所需 CONTEXT_DB_*
#
# 用法:
# ./scripts/launch-single.sh test [--bin /path/to/agent-server] [--build]
# --bin 指定二进制路径(默认 bin/agent-server-<env>)
# --build 从当前工作树构建。test 部署应使用 rc tag 构建的制品,勿默认依赖 --build
#
# 日志:.tmp/launch/{server,pi-sidecar}-<env>.log
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENV_NAME=""
if [[ $# -gt 0 && "${1:0:1}" != "-" ]]; then
ENV_NAME="$1"
shift
fi
BIN_OVERRIDE=""
DO_BUILD=0
while [[ $# -gt 0 ]]; do
case "$1" in
--bin) BIN_OVERRIDE="${2:-}"; shift 2 ;;
--build) DO_BUILD=1; shift ;;
-h|--help)
sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
*) echo "unknown arg: $1" >&2; exit 1 ;;
esac
done
if [[ -z "$ENV_NAME" || "$ENV_NAME" == "dev" ]]; then
echo "error: dev 请使用 ./scripts/launch.sh --no-docker;本脚本用于非 dev 环境(如 test)" >&2
exit 1
fi
LOG_DIR="$ROOT/.tmp/launch"
mkdir -p "$LOG_DIR"
info() { printf '\033[1;34m[launch-single]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m ✅ %s\033[0m\n' "$*"; }
skip() { printf '\033[1;32m ⏭️ %s\033[0m\n' "$*"; }
act() { printf '\033[1;33m 🔄 %s\033[0m\n' "$*"; }
fail() { printf '\033[1;31m ❌ %s\033[0m\n' "$*"; }
# ---------- 分层加载配置:根 .env(公共密钥)→ configs/.env.<env>(环境差异) ----------
# shellcheck disable=SC1091
set -a
[[ -f "$ROOT/.env" ]] && . "$ROOT/.env"
if [[ -f "$ROOT/configs/.env.$ENV_NAME" ]]; then
. "$ROOT/configs/.env.$ENV_NAME"
else
fail "缺少 configs/.env.$ENV_NAME(参考 configs/.env.$ENV_NAME.example)"
exit 1
fi
set +a
# ---------- 宿主运行派生(进程环境变量优先于 .env 文件) ----------
export ENV="$ENV_NAME"
DATA_DIR="${DATA_DIR:-$ROOT/data/$ENV_NAME}"
case "$DATA_DIR" in /*) ;; *) DATA_DIR="$ROOT/$DATA_DIR" ;; esac
export DATA_DIR
HTTP_PORT="${HTTP_PORT:-8081}"
export HTTP_HOST="${HTTP_HOST:-${APP_PUBLISH_HOST:-0.0.0.0}}"
export HTTP_PORT
export SEED_DEMO_USERS="${SEED_DEMO_USERS:-false}"
export LOCALPG_MODE="${LOCALPG_MODE:-managed}"
# SYSTEM_DB_*(compose 约定)→ CONTEXT_DB_*(config.Load 宿主读取约定)
export CONTEXT_DB_HOST="${CONTEXT_DB_HOST:-127.0.0.1}"
export CONTEXT_DB_PORT="${CONTEXT_DB_PORT:-${SYSTEM_DB_PUBLISH_PORT:-5434}}"
export CONTEXT_DB_USER="${CONTEXT_DB_USER:-${SYSTEM_DB_USER:-app}}"
export CONTEXT_DB_PASSWORD="${CONTEXT_DB_PASSWORD:-${SYSTEM_DB_PASSWORD:?SYSTEM_DB_PASSWORD required}}"
export CONTEXT_DB_NAME="${CONTEXT_DB_NAME:-${SYSTEM_DB_NAME:-agent_system_$ENV_NAME}}"
PI_PORT="${PI_AGENT_RUNNER_PORT:-18866}"
export PI_AGENT_RUNNER_URL="${PI_AGENT_RUNNER_URL:-http://127.0.0.1:$PI_PORT}"
# 与 compose 对齐的 vault 状态根(FAV 关闭时 inert;无 master key 时 health 同样报 disconnected)
export VAULT_STATE_ROOT="${VAULT_STATE_ROOT:-$ROOT/data/vault/$ENV_NAME}"
export VAULT_STORAGE_IDENTITY="${VAULT_STORAGE_IDENTITY:-${ENV_NAME}-vault}"
BIN="${BIN_OVERRIDE:-$ROOT/bin/agent-server-$ENV_NAME}"
wait_http() {
local url="$1" retries="${2:-30}" i
for ((i = 0; i < retries; i++)); do
curl -sf -m 2 "$url" >/dev/null 2>&1 && return 0
sleep 1
done
return 1
}
stop_pid_on_port() {
local port="$1" pid
pid="$(lsof -nP -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n1 || true)"
if [[ -n "$pid" ]]; then
act "停止占用 :$port 的进程 PID $pid"
kill "$pid" 2>/dev/null || true
for ((i = 0; i < 10; i++)); do
lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1 || return 0
sleep 1
done
lsof -nP -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | xargs -r kill -9 2>/dev/null || true
fi
}
info "环境: $ENV_NAME(HTTP :$HTTP_HOST:$HTTP_PORT / DB :$CONTEXT_DB_PORT/$CONTEXT_DB_NAME / PI :$PI_PORT / DATA_DIR=$DATA_DIR)"
# ---------- PI sidecar ----------
info "检测 PI sidecar :$PI_PORT ..."
if wait_http "http://127.0.0.1:$PI_PORT/health" 2; then
skip "PI sidecar 已健康"
else
stop_pid_on_port "$PI_PORT"
act "启动 PI sidecar(日志 $LOG_DIR/pi-sidecar-$ENV_NAME.log)..."
(
export PORT="$PI_PORT"
export DATA_DIR="$DATA_DIR/workspaces"
export PI_CODING_AGENT_DIR="$DATA_DIR/pi-agent"
nohup bash "$ROOT/deploy/pi-agent-sidecar/start.sh" >"$LOG_DIR/pi-sidecar-$ENV_NAME.log" 2>&1 &
)
if wait_http "http://127.0.0.1:$PI_PORT/health" 60; then
ok "PI sidecar 就绪"
else
fail "PI sidecar 健康检查超时,见 $LOG_DIR/pi-sidecar-$ENV_NAME.log"
exit 1
fi
fi
# ---------- agent-server ----------
if [[ "$DO_BUILD" -eq 1 ]]; then
act "从当前工作树构建 $BIN ..."
OUT_BIN="$BIN" bash "$ROOT/scripts/build-single-binary.sh" || { fail "构建失败"; exit 1; }
fi
if [[ ! -x "$BIN" ]]; then
fail "二进制不存在或不可执行: $BIN(用 --bin 指定 rc tag 构建的制品,或 --build)"
exit 1
fi
stop_pid_on_port "$HTTP_PORT"
act "后台启动 agent-server(LOCALPG_MODE=$LOCALPG_MODE,日志 $LOG_DIR/server-$ENV_NAME.log)..."
(
cd "$ROOT"
nohup "$BIN" >"$LOG_DIR/server-$ENV_NAME.log" 2>&1 &
)
if wait_http "http://127.0.0.1:$HTTP_PORT/api/v1/health" 120; then
ok "后端已启动并通过健康检查"
else
fail "后端健康检查超时,见 $LOG_DIR/server-$ENV_NAME.log"
exit 1
fi
info "完成。入口: http://127.0.0.1:$HTTP_PORT PI: http://127.0.0.1:$PI_PORT"
info "若首次从容器切换,请先跑: ./scripts/migrate-test-db.sh(test),容器 volume 保留可回滚"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# migrate-test-db.sh — dump 容器 system-db (shuimu-system-pg-test :5434) → 本机 localpg :5434
#
# 复用 migrate-dev-db.sh 的通用逻辑(其全部差异点已参数化);本脚本只钉 test 默认值:
# CONTAINER=shuimu-system-pg-test DATA_DIR=data/test PORT=5434 DB=agent_system_test
# 前置:
# - 容器 shuimu-system-pg-test 健康(作为 dump 源)
# - brew postgresql@18 + pgvector 已安装(LOCALPG_BIN 可覆盖)
# - configs/.env.test 提供 SYSTEM_DB_*(或用环境变量覆盖)
# 不删除 docker volume;回滚:docker compose -f docker-compose.test.yml -p agent-test up -d system-db
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# 读取 test 环境 SSOT(SYSTEM_DB_* 等;含密钥,勿回显)
# shellcheck disable=SC1091
if [[ -f "$ROOT/configs/.env.test" ]]; then
set -a
. "$ROOT/configs/.env.test"
set +a
fi
export CONTAINER="${DEV_PG_CONTAINER:-shuimu-system-pg-test}"
export DATA_DIR="${MIGRATE_DATA_DIR:-$ROOT/data/test}" # 绝对路径,避免 PGDATA 相对解析漂移
export PORT="${CONTEXT_DB_PORT:-${SYSTEM_DB_PUBLISH_PORT:-5434}}"
export CONTEXT_DB_PORT="$PORT"
export DB_USER="${CONTEXT_DB_USER:-${SYSTEM_DB_USER:-app}}"
export DB_PASS="${CONTEXT_DB_PASSWORD:-${SYSTEM_DB_PASSWORD:?SYSTEM_DB_PASSWORD required (configs/.env.test)}}"
export DB_NAME="${CONTEXT_DB_NAME:-${SYSTEM_DB_NAME:-agent_system_test}}"
export DUMP="${DUMP_PATH:-$ROOT/.tmp/test-pg-migrate.dump}"
# migrate-dev-db.sh 的 dump 步骤读取 SYSTEM_DB_USER / SYSTEM_DB_NAME
export SYSTEM_DB_USER="${SYSTEM_DB_USER:-app}"
export SYSTEM_DB_NAME="${SYSTEM_DB_NAME:-agent_system_test}"
bash "$ROOT/scripts/migrate-dev-db.sh"