scense/build.sh

145 lines
5.4 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 应用一键部署脚本(元景项目)
# 唯一部署单元:一个入口 app/scense.py + 一个端口 ${SERVER_PORT:-9380}
# 门禁:部署完成后 30s 端口监听门禁——30s 内未监听自动重启一次兜底,
# 再等 30s 仍失败才输出诊断并以非零退出(不静默通过)。
# =============================================================================
set -uo pipefail
APP_NAME="scense"
APP_PORT="${SERVER_PORT:-9380}"
WORKDIR="$(cd "$(dirname "$0")" && pwd)"
VENV_DIR="$WORKDIR/venv"
PKGS_DIR="$WORKDIR/pkgs"
LOGS_DIR="$WORKDIR/logs"
ENV_FILE="$WORKDIR/.env"
# 模块清单(基础引用 + 本应用业务模块,与 yuanjing_spec.json generated_modules 一致)
FOUNDATION_MODULES=(apppublic sqlor ahserver bricks_for_python appbase rbac)
BIZ_MODULES=(world scene entity script_engine world_snapshot world_sync)
ALL_MODULES=("${FOUNDATION_MODULES[@]}" "${BIZ_MODULES[@]}")
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
die() { log "FATAL: $*"; exit 1; }
# 0) 加载 .env含 MYSQL_* / REDIS_* / SERVER_PORT / PASSWORD_KEY / SCENSE_ADMIN_*
if [ -f "$ENV_FILE" ]; then
set -a; . "$ENV_FILE"; set +a
fi
MYSQL_ARGS=(--host="${MYSQL_HOST:-127.0.0.1}" --port="${MYSQL_PORT:-3306}"
--user="${MYSQL_USER:-root}" --password="${MYSQL_PASSWORD:-}")
DBNAME="${MYSQL_DATABASE:-scense}"
# 1) 系统依赖 + venv
command -v python3 >/dev/null 2>&1 || die "python3 not found"
command -v mysql >/dev/null 2>&1 || die "mysql client not found"
[ -d "$VENV_DIR" ] || python3 -m venv "$VENV_DIR"
# shellcheck disable=SC1091
. "$VENV_DIR/bin/activate"
pip install --quiet --upgrade pip
pip install --quiet xls2ddl
# 2) 克隆并安装模块(基础 + 业务)
mkdir -p "$PKGS_DIR"
for m in "${ALL_MODULES[@]}"; do
if [ ! -d "$PKGS_DIR/$m" ]; then
git clone "https://git.opencomputing.cn/yumoqing/$m.git" "$PKGS_DIR/$m" 2>/dev/null \
|| git clone "git@git.opencomputing.cn:yumoqing/$m.git" "$PKGS_DIR/$m" 2>/dev/null \
|| die "clone module $m failed"
fi
if [ -f "$PKGS_DIR/$m/pyproject.toml" ]; then
(cd "$PKGS_DIR/$m" && pip install --quiet -e .)
fi
done
pip install --quiet bricks_for_python
# 3) 前端 wwwroot 软链(软链非 cp保持同步
mkdir -p "$WORKDIR/wwwroot"
for m in "${BIZ_MODULES[@]}"; do
[ -d "$PKGS_DIR/$m/wwwroot" ] && ln -sfn "../pkgs/$m/wwwroot" "$WORKDIR/wwwroot/$m"
done
[ -d "$PKGS_DIR/bricks_for_python/dist" ] && ln -sfn "../pkgs/bricks_for_python/dist" "$WORKDIR/wwwroot/bricks"
# 4) i18n 合并merge_i18n.py 存在则执行,否则跳过不阻断)
if [ -f "$PKGS_DIR/apppublic/tools/merge_i18n.py" ]; then
python "$PKGS_DIR/apppublic/tools/merge_i18n.py" "$WORKDIR/wwwroot" "${BIZ_MODULES[@]}" \
|| log "i18n merge skipped (non-fatal)"
fi
# 5) 数据库:建库 + 各模块 DDL + 应用级 DDL/seed
mysql "${MYSQL_ARGS[@]}" -e "CREATE DATABASE IF NOT EXISTS \`${DBNAME}\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" \
|| die "create database failed"
for m in "${BIZ_MODULES[@]}"; do
if [ -d "$PKGS_DIR/$m/models" ]; then
(cd "$PKGS_DIR/$m/models" && json2ddl mysql . > "$WORKDIR/mysql.ddl.$m.sql" 2>/dev/null) \
|| (cd "$PKGS_DIR/$m/models" && xls2ddl mysql . > "$WORKDIR/mysql.ddl.$m.sql" 2>/dev/null) || true
if [ -f "$WORKDIR/mysql.ddl.$m.sql" ]; then
mysql "${MYSQL_ARGS[@]}" "$DBNAME" < "$WORKDIR/mysql.ddl.$m.sql" || log "ddl $m warn"
fi
fi
done
[ -f "$WORKDIR/scripts/ddl.sql" ] && mysql "${MYSQL_ARGS[@]}" "$DBNAME" < "$WORKDIR/scripts/ddl.sql"
[ -f "$WORKDIR/scripts/seed.sql" ] && mysql "${MYSQL_ARGS[@]}" "$DBNAME" < "$WORKDIR/scripts/seed.sql"
# 6) CRUD 生成xls2ui 从模块 json/ 生成 .dspy + .ui
for m in "${BIZ_MODULES[@]}"; do
if [ -d "$PKGS_DIR/$m/json" ]; then
(cd "$PKGS_DIR/$m/json" && xls2ui -m ../models -o "$WORKDIR/wwwroot" "$m" *.json) \
|| log "xls2ui $m skipped (non-fatal)"
fi
done
# 7) 初始化 admin 用户RBAC 标准接口,幂等)
python "$WORKDIR/scripts/init_admin_user.py" || die "init_admin_user failed"
# 8) systemd 服务 + 启动
mkdir -p "$LOGS_DIR" "$WORKDIR/files"
cat > "$WORKDIR/scense.service" <<EOF
[Unit]
Description=scense app (yuanjing)
After=network.target
[Service]
Type=simple
User=$(whoami)
WorkingDirectory=$WORKDIR
EnvironmentFile=$ENV_FILE
ExecStart=$VENV_DIR/bin/python $WORKDIR/app/scense.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
EOF
sudo cp "$WORKDIR/scense.service" /etc/systemd/system/scense.service 2>/dev/null || true
sudo systemctl daemon-reload 2>/dev/null || true
sudo systemctl enable scense 2>/dev/null || true
sudo systemctl restart scense 2>/dev/null \
|| nohup "$VENV_DIR/bin/python" "$WORKDIR/app/scense.py" >> "$LOGS_DIR/scense.log" 2>&1 &
# 9) 端口监听门禁30s未监听自动重启一次兜底再等 30s
wait_port() {
local port="$1" tries="${2:-30}"
for _ in $(seq 1 "$tries"); do
if (exec 3<>"/dev/tcp/127.0.0.1/$port") 2>/dev/null; then
exec 3>&- 3<&-; return 0
fi
sleep 1
done
return 1
}
if ! wait_port "$APP_PORT" 30; then
log "WARN: port $APP_PORT not listening in 30s, restart once (bootstrap)"
sudo systemctl restart scense 2>/dev/null || true
if ! wait_port "$APP_PORT" 30; then
log "ERROR: port $APP_PORT still not listening after restart bootstrap"
tail -n 50 "$LOGS_DIR/scense.log" 2>/dev/null || true
exit 1
fi
fi
log "OK: scense is listening on port $APP_PORT"