scense/build.sh

164 lines
7.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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"
# dist/ 不入 gitgitignoreclone 后必须本地构建;内层 build.sh 用 cat 拼接,无需 node
mkdir -p pkgs/bricks/dist
if [ ! -f "pkgs/bricks/dist/bricks.js" ]; then
(cd pkgs/bricks/bricks && bash build.sh) || { echo "ERROR: bricks build failed" >&2; exit 1; }
fi
[ -s "pkgs/bricks/dist/bricks.js" ] || { echo "ERROR: bricks.js missing/empty after build" >&2; exit 1; }
mkdir -p wwwroot
ln -sfn "../pkgs/bricks/dist" "wwwroot/bricks"
echo "==> [4/9] database + foundation/initial tables + seed"
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; }
# 种子:默认机构/角色/权限/超管(全新库无此数据则一切请求 401
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" \
< "${APP_SRC_DIR}/scripts/seed.sql" || { echo "ERROR: seed.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
# 基础模块的 wwwroot 也要软链rbac 登录页、appbase 页面),否则登录页 404
for m in appbase rbac; do
if [ -d "pkgs/${m}/wwwroot" ]; then
ln -sfn "../pkgs/${m}/wwwroot" "wwwroot/${m}"
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"