merge: candidate test-single-binary-2026-09-27 into dev
This commit is contained in:
@@ -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。
|
||||
Executable
+163
@@ -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 保留可回滚"
|
||||
Executable
+35
@@ -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"
|
||||
Reference in New Issue
Block a user