feat(ci): CI/CD分层(NAS自动验证+VPS dispatch)
promote.sh 加 --target nas|vps|all(拆 NAS/VPS,默认 all 兼容)。ci-cd.yml 重构: push→test→nas-deploy(推NAS后端+前端dist+restart容器)→nas-verify(login+strategy/list+dbbardata 秒级 gate),不再自动推 VPS。vps-deploy.yml 新建: workflow_dispatch+sha input,checkout 指定 commit→promote --target vps→verify,严格版本一致(VPS=NAS验证过的 commit)。两种 approve: 人工 Gitea UI Run workflow / agent MCP dispatch_workflow。原因: Gitea 不支持 environment approval gate,此前 promote.sh 同时推 NAS+VPS 无隔离,push 即上 prod。
This commit is contained in:
+32
-26
@@ -24,20 +24,22 @@ ALL_MODS=(sanguo_api sanguo_backtest sanguo_common sanguo_data sanguo_factor
|
||||
sanguo_research sanguo_trader sanguo_web scripts config tests)
|
||||
ROOT_FILES=(pyproject.toml pytest.ini requirements-lock.txt run_web.py)
|
||||
|
||||
# 解析 --module 参数
|
||||
# 解析参数: --module <名>(单模块) / --target <nas|vps|all>(目标机器,默认 all)
|
||||
PUSH_MOD=""
|
||||
if [[ "${1:-}" == "--module" ]]; then
|
||||
if [ -z "${2:-}" ]; then
|
||||
echo "ERROR: --module 需要模块名参数" >&2
|
||||
echo "可用模块: ${ALL_MODS[*]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
PUSH_MOD="$2"
|
||||
if [ ! -d "$PUSH_MOD" ]; then
|
||||
echo "ERROR: 本地目录不存在: $PUSH_MOD" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
TARGET="all"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--module)
|
||||
[ -z "${2:-}" ] && { echo "ERROR: --module 需要模块名. 可用: ${ALL_MODS[*]}" >&2; exit 1; }
|
||||
PUSH_MOD="$2"; shift 2
|
||||
[ ! -d "$PUSH_MOD" ] && { echo "ERROR: 本地目录不存在: $PUSH_MOD" >&2; exit 1; }
|
||||
;;
|
||||
--target)
|
||||
case "${2:-}" in nas|vps|all) TARGET="$2"; shift 2;; *) echo "ERROR: --target 需 nas|vps|all" >&2; exit 1;; esac
|
||||
;;
|
||||
*) echo "ERROR: 未知参数 $1 (支持 --module <名> / --target <nas|vps|all>)" >&2; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# rsync exclude: 前导 / = 仅根级匹配,不影响模块内同名子目录
|
||||
RSYNC_EXCLUDE=(--exclude='/data' --exclude='/data_backup' --exclude='/logs'
|
||||
@@ -60,21 +62,24 @@ else
|
||||
fi
|
||||
echo "=============================================="
|
||||
|
||||
# ---- Step 1/3: Mac → NAS (rsync 代码镜像) ----
|
||||
echo ""
|
||||
echo "[1/3] rsync Mac → NAS ($NAS:$NAS_CODE) ..."
|
||||
if [ -n "$PUSH_MOD" ]; then
|
||||
rsync -az "${RSYNC_EXCLUDE[@]}" "$PUSH_MOD/" "$NAS:$NAS_CODE/$PUSH_MOD/"
|
||||
echo " ✅ NAS 单模块镜像完成: $PUSH_MOD"
|
||||
else
|
||||
# 不用 --delete! NAS 有 data_backup 等独有目录,删了就丢数据
|
||||
rsync -avz "${RSYNC_EXCLUDE[@]}" ./ "$NAS:$NAS_CODE/"
|
||||
echo " ✅ NAS 全量镜像完成"
|
||||
if [[ "$TARGET" == "nas" || "$TARGET" == "all" ]]; then
|
||||
# ---- Step: Mac → NAS (rsync 代码镜像) ----
|
||||
echo ""
|
||||
echo "[*] rsync Mac → NAS ($NAS:$NAS_CODE) ..."
|
||||
if [ -n "$PUSH_MOD" ]; then
|
||||
rsync -az "${RSYNC_EXCLUDE[@]}" "$PUSH_MOD/" "$NAS:$NAS_CODE/$PUSH_MOD/"
|
||||
echo " ✅ NAS 单模块镜像完成: $PUSH_MOD"
|
||||
else
|
||||
# 不用 --delete! NAS 有 data_backup 等独有目录,删了就丢数据
|
||||
rsync -avz "${RSYNC_EXCLUDE[@]}" ./ "$NAS:$NAS_CODE/"
|
||||
echo " ✅ NAS 全量镜像完成"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Step 2/3: Mac → VPS (scp 按模块/根文件推) ----
|
||||
if [[ "$TARGET" == "vps" || "$TARGET" == "all" ]]; then
|
||||
# ---- Step: Mac → VPS (scp 按模块/根文件推) ----
|
||||
echo ""
|
||||
echo "[2/3] scp Mac → VPS ($VPS:$VPS_CODE) ..."
|
||||
echo "[*] scp Mac → VPS ($VPS:$VPS_CODE) ..."
|
||||
if [ -n "$PUSH_MOD" ]; then
|
||||
MODS=("$PUSH_MOD")
|
||||
ROOT_PUSH=() # 单模块不推根文件
|
||||
@@ -134,10 +139,11 @@ if ssh -o ConnectTimeout=20 "$VPS" "$VERIFY_CMD" 2>/dev/null | grep -q '\.py'; t
|
||||
else
|
||||
echo " ⚠️ VPS 验证未取到 $VERIFY_MOD 的 .py 文件(可能目录为空或 ssh 超时,请手动核查)"
|
||||
fi
|
||||
fi # end target=vps|all
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo " ✅ 晋升完成 $(date '+%F %T')"
|
||||
echo " ✅ 晋升完成 $(date '+%F %T') [target=$TARGET]"
|
||||
echo " - NAS 代码镜像: $NAS:$NAS_CODE"
|
||||
echo " - VPS 生产代码: $VPS:$VPS_CODE"
|
||||
echo " - reload: VPS 无常驻 web,下次 schtask/回测启动自动生效"
|
||||
|
||||
Reference in New Issue
Block a user