- migrate-test-db.sh: export DEV_PG_CONTAINER (inner script reads it, not CONTAINER) - launch-single.sh: SYSTEM_DB_* must override root-.env CONTEXT_DB_* (set -a re-source clobbers both external exports and :- fallbacks; two misdirected launches audited, dev DB unchanged - report §5.8-3) - task record done; full test report (nightly five chains PASS @5e409c1e, migration S0-verified, deployment acceptance, rollback paths, follow-ups)
166 lines
6.0 KiB
Bash
Executable File
166 lines
6.0 KiB
Bash
Executable File
#!/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 宿主读取约定)。
|
||
# 注意:SYSTEM_DB_* 必须压过根 .env 的 dev 值 CONTEXT_DB_*(分层加载先读根 .env),
|
||
# 因此这里不用 :- 回退而直接覆盖;attach 模式可用 APP_DB_DSN 显式指定。
|
||
export CONTEXT_DB_HOST="127.0.0.1"
|
||
export CONTEXT_DB_PORT="${SYSTEM_DB_PUBLISH_PORT:-5434}"
|
||
export CONTEXT_DB_USER="${SYSTEM_DB_USER:-app}"
|
||
export CONTEXT_DB_PASSWORD="${SYSTEM_DB_PASSWORD:?SYSTEM_DB_PASSWORD required}"
|
||
export 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 保留可回滚"
|