152 lines
6.4 KiB
Bash
Executable File
152 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# =============================================================================
|
||
# 唤景平台世界模式(scense)一键部署脚本 —— 测试环境
|
||
# 部署目录 ~/scense_app,应用端口 9380,数据库 localhost:3306/scense
|
||
# 规范:产线技能库 webapp-deploy(含「模块安装四步」)
|
||
#
|
||
# 步骤:
|
||
# 0) venv
|
||
# 1) 创建 pkgs/,全部模块 git clone 进来(基础共享包 + 6 业务模块;禁止 mv 源码)
|
||
# 2) pip install 全部模块(-e,失败即退出)
|
||
# 3) bricks 前端构建 + wwwroot/bricks 软链
|
||
# 4) 建库 + 基础框架表与初始业务表(scripts/ddl.sql,幂等 IF NOT EXISTS)
|
||
# 5) i18n 合并
|
||
# 6) 逐模块四步安装:① models/json2ddl 建表 ② json/build.sh(xls2ui) ③ wwwroot 软链 ④ 数据导入
|
||
# 7) 应用代码同步 + runtime 目录
|
||
# 8) 重启 + 健康检查(/healthz 必须 200,否则 exit 1)
|
||
# 硬性约束:set -euo pipefail,任何一步失败立即终止,禁止带病启动。
|
||
# =============================================================================
|
||
set -euo pipefail
|
||
|
||
DEPLOY_DIR="${DEPLOY_DIR:-$HOME/scense_app}"
|
||
VENV="${DEPLOY_DIR}/venv"
|
||
APP_PORT="${APP_PORT:-9380}"
|
||
DB_HOST="${DB_HOST:-localhost}"
|
||
DB_PORT="${DB_PORT:-3306}"
|
||
DB_NAME="${DB_NAME:-scense}"
|
||
DB_USER="${DB_USER:-test}"
|
||
DB_PASSWORD="${DB_PASSWORD:-test123}"
|
||
GIT_HTTPS="https://git.opencomputing.cn/yumoqing"
|
||
|
||
FOUNDATION_PKGS="apppublic sqlor ahserver rbac xls2ddl appbase bricks-for-python bricks"
|
||
BUSINESS_MODULES="world scene entity script_engine world_snapshot world_sync"
|
||
|
||
APP_SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
mkdir -p "${DEPLOY_DIR}"
|
||
export PATH="${VENV}/bin:${PATH}"
|
||
|
||
echo "==> [0/9] venv"
|
||
if [ ! -x "${VENV}/bin/python" ]; then
|
||
python3 -m venv "${VENV}"
|
||
fi
|
||
"${VENV}/bin/pip" install -q --upgrade pip >/dev/null 2>&1 || true
|
||
"${VENV}/bin/pip" install -q wheel >/dev/null 2>&1 || true
|
||
|
||
echo "==> [1/9] clone modules into pkgs/ (git clone only, no mv)"
|
||
cd "${DEPLOY_DIR}"
|
||
mkdir -p pkgs
|
||
for m in ${FOUNDATION_PKGS} ${BUSINESS_MODULES}; do
|
||
if [ ! -d "pkgs/${m}/.git" ]; then
|
||
rm -rf "pkgs/${m}"
|
||
git clone -q "${GIT_HTTPS}/${m}.git" "pkgs/${m}" || { echo "ERROR: clone ${m} failed" >&2; exit 1; }
|
||
echo " cloned ${m}"
|
||
else
|
||
git -C "pkgs/${m}" pull -q --ff-only || echo "WARN: pull ${m} failed, using existing"
|
||
fi
|
||
done
|
||
|
||
echo "==> [2/9] pip install all modules"
|
||
for m in ${FOUNDATION_PKGS} ${BUSINESS_MODULES}; do
|
||
if [ -f "pkgs/${m}/pyproject.toml" ] || [ -f "pkgs/${m}/setup.py" ] || [ -f "pkgs/${m}/setup.cfg" ]; then
|
||
"${VENV}/bin/pip" install -q -e "pkgs/${m}" || { echo "ERROR: pip install ${m} failed" >&2; exit 1; }
|
||
echo " installed ${m}"
|
||
else
|
||
echo "WARN: ${m} has no packaging metadata, skipped pip install"
|
||
fi
|
||
done
|
||
|
||
echo "==> [3/9] bricks frontend build + symlink"
|
||
if [ ! -f "pkgs/bricks/dist/bricks.js" ]; then
|
||
(cd pkgs/bricks/bricks && bash build.sh) || { echo "ERROR: bricks build failed" >&2; exit 1; }
|
||
fi
|
||
mkdir -p wwwroot
|
||
ln -sfn "../pkgs/bricks/dist" "wwwroot/bricks"
|
||
|
||
echo "==> [4/9] database + foundation/initial tables"
|
||
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" \
|
||
-e "CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" \
|
||
|| { echo "ERROR: create database failed" >&2; exit 1; }
|
||
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" \
|
||
< "${APP_SRC_DIR}/scripts/ddl.sql" || { echo "ERROR: ddl.sql failed" >&2; exit 1; }
|
||
|
||
echo "==> [5/9] i18n merge"
|
||
mkdir -p wwwroot/i18n/zh wwwroot/i18n/en
|
||
"${VENV}/bin/python" - <<'PY'
|
||
import json, os
|
||
ok = 0
|
||
for lang in ("zh", "en"):
|
||
merged = {}
|
||
for m in ("apppublic","sqlor","ahserver","appbase","rbac","world","scene","entity","script_engine","world_snapshot","world_sync"):
|
||
p = f"pkgs/{m}/i18n/{lang}/msg.txt"
|
||
if os.path.isfile(p):
|
||
for line in open(p, encoding="utf-8"):
|
||
line = line.rstrip("\n")
|
||
if "=" in line:
|
||
k, v = line.split("=", 1)
|
||
merged[k.strip()] = v.strip()
|
||
out = f"wwwroot/i18n/{lang}/i18n.json"
|
||
json.dump(merged, open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
|
||
ok += len(merged)
|
||
print(f"i18n merged: {ok} keys")
|
||
PY
|
||
|
||
echo "==> [6/9] per-module install: ① json2ddl建表 ② json/build.sh ③ wwwroot软链 ④ 数据导入"
|
||
for m in ${BUSINESS_MODULES}; do
|
||
echo " -- ${m}"
|
||
# ① 建表:模块 models 是唯一事实源;json2ddl 输出 DROP+CREATE,每次部署权威重建
|
||
(cd "pkgs/${m}/models" && "${VENV}/bin/json2ddl" mysql .) > "/tmp/${m}_ddl.sql" \
|
||
|| { echo "ERROR: ${m} json2ddl failed" >&2; exit 1; }
|
||
[ -s "/tmp/${m}_ddl.sql" ] || { echo "ERROR: ${m} DDL empty" >&2; exit 1; }
|
||
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" \
|
||
< "/tmp/${m}_ddl.sql" || { echo "ERROR: ${m} create tables failed" >&2; exit 1; }
|
||
# ② CRUD 页面(模块仓库自带 json/build.sh)
|
||
(cd "pkgs/${m}/json" && bash build.sh) || { echo "ERROR: ${m} json/build.sh failed" >&2; exit 1; }
|
||
# ③ wwwroot 软链(非 cp)
|
||
ln -sfn "../pkgs/${m}/wwwroot" "wwwroot/${m}"
|
||
# ④ 初始化数据(有 data/*.xlsx 才导)
|
||
if ls "pkgs/${m}/data/"*.xlsx >/dev/null 2>&1; then
|
||
(cd "pkgs/${m}/data" && for f in *.xlsx; do "${VENV}/bin/dbloader" "${DEPLOY_DIR}" "${DB_NAME}" "$f"; done) \
|
||
|| { echo "ERROR: ${m} dbloader failed" >&2; exit 1; }
|
||
fi
|
||
done
|
||
|
||
echo "==> [7/9] sync app code + runtime dirs"
|
||
for d in app conf scripts wwwroot; do
|
||
if [ -d "${APP_SRC_DIR}/${d}" ] && [ "$(realpath "${APP_SRC_DIR}/${d}")" != "$(realpath "${DEPLOY_DIR}/${d}")" ]; then
|
||
mkdir -p "${DEPLOY_DIR}/${d}"
|
||
cp -a "${APP_SRC_DIR}/${d}/." "${DEPLOY_DIR}/${d}/"
|
||
fi
|
||
done
|
||
mkdir -p logs files
|
||
|
||
echo "==> [8/9] restart service"
|
||
bash "${APP_SRC_DIR}/stop.sh" || true
|
||
sleep 1
|
||
bash "${APP_SRC_DIR}/start.sh"
|
||
|
||
echo "==> [9/9] health check: GET /healthz"
|
||
ok=""
|
||
code=""
|
||
for i in $(seq 1 15); do
|
||
code=$(curl -s -o /tmp/scense_healthz.json -w "%{http_code}" "http://127.0.0.1:${APP_PORT}/healthz" || true)
|
||
if [ "${code}" = "200" ]; then ok=1; break; fi
|
||
sleep 2
|
||
done
|
||
if [ -z "${ok}" ]; then
|
||
echo "ERROR: /healthz not 200 after 30s (last=${code:-000}); logs/scense.log tail:" >&2
|
||
tail -30 logs/scense.log 2>/dev/null >&2 || true
|
||
exit 1
|
||
fi
|
||
echo "healthz: $(cat /tmp/scense_healthz.json)"
|
||
echo "==> deploy OK: app=scense port=${APP_PORT} healthz=200"
|