#!/usr/bin/env bash # 在服务器上执行:拉取后的常规发版(不覆盖 env 文件、默认不 seed) # 通过 DUKANG_DEPLOY_ENV=staging|production 区分同机双栈 set -euo pipefail DUKANG_DEPLOY_ENV="${DUKANG_DEPLOY_ENV:-production}" case "$DUKANG_DEPLOY_ENV" in staging|stage|test) DUKANG_DEPLOY_ENV=staging ;; production|prod) DUKANG_DEPLOY_ENV=production ;; *) echo "错误: DUKANG_DEPLOY_ENV 须为 staging 或 production,当前=$DUKANG_DEPLOY_ENV" >&2 exit 1 ;; esac if [[ "$DUKANG_DEPLOY_ENV" == "staging" ]]; then APP_ROOT="${APP_ROOT:-/opt/dukang-staging}" ENV_FILE_NAME=".env.staging" ECOSYSTEM_FILE="ecosystem.staging.config.cjs" VITE_API_DEFAULT="https://api-test.dukanghaoke.com" API_PORT=8190 USER_PORT=8191 PORT_GREP='819[0-4]' PM2_API=dukang-stg-api PM2_USER=dukang-stg-h5-user PM2_SHOP=dukang-stg-h5-shop PM2_PARTNER=dukang-stg-h5-partner PM2_ADMIN=dukang-stg-admin-web else APP_ROOT="${APP_ROOT:-/opt/dukang}" ENV_FILE_NAME=".env.production" ECOSYSTEM_FILE="ecosystem.config.cjs" VITE_API_DEFAULT="https://api.dukanghaoke.com" API_PORT=8090 USER_PORT=8091 PORT_GREP='809[0-4]' PM2_API=dukang-api PM2_USER=dukang-h5-user PM2_SHOP=dukang-h5-shop PM2_PARTNER=dukang-h5-partner PM2_ADMIN=dukang-admin-web fi DEPLOY_DIR="$APP_ROOT/deploy" export APP_ROOT export APP_ENV="$DUKANG_DEPLOY_ENV" export DUKANG_ENV_FILE="$ENV_FILE_NAME" SKIP_BUILD=false SKIP_DB=false RUN_SEED=false DB_PUSH_ACCEPT_DATA_LOSS=false usage() { cat <&2; usage; exit 1 ;; esac shift done cd "$APP_ROOT" echo "==> 发版环境: $DUKANG_DEPLOY_ENV ($APP_ROOT)" echo "==> 1. 启用 pnpm" corepack enable 2>/dev/null || true corepack prepare pnpm@11.2.2 --activate 2>/dev/null || true echo "==> 2. 安装依赖" # mini-user H5(Taro)构建峰值内存较高 export NODE_OPTIONS="${NODE_OPTIONS:---max-old-space-size=8192}" # C 端 H5 静态资源路径:staging 独立域名根路径;生产 nginx 入口为 /user/ if [[ "$DUKANG_DEPLOY_ENV" == "staging" ]]; then export TARO_H5_PUBLIC_PATH="${TARO_H5_PUBLIC_PATH:-/}" unset TARO_H5_ROUTER_BASENAME else export TARO_H5_PUBLIC_PATH="${TARO_H5_PUBLIC_PATH:-/user/}" export TARO_H5_ROUTER_BASENAME="${TARO_H5_ROUTER_BASENAME:-/user}" fi # C 端 H5 编译期注入的 API origin(勿落到 localhost) export VITE_API_TARGET="${VITE_API_TARGET:-$VITE_API_DEFAULT}" export VITE_APP_ENV="$DUKANG_DEPLOY_ENV" pnpm approve-builds --all 2>/dev/null || true pnpm install --frozen-lockfile 2>/dev/null || pnpm install if [[ "$SKIP_DB" == false ]]; then echo "==> 3. 数据库 schema 同步(构建前,读取 $ENV_FILE_NAME)" cd "$APP_ROOT/server/dukang-api" if [[ ! -f "$ENV_FILE_NAME" ]]; then echo "错误: 未找到 server/dukang-api/$ENV_FILE_NAME" >&2 exit 1 fi node scripts/with-api-env.cjs pnpm prisma:generate if [[ "$DB_PUSH_ACCEPT_DATA_LOSS" == true ]]; then node scripts/with-api-env.cjs npx prisma db push --accept-data-loss else node scripts/with-api-env.cjs npx prisma db push fi if [[ "$RUN_SEED" == true ]]; then echo "==> 执行 seed" node scripts/with-api-env.cjs pnpm prisma:seed fi cd "$APP_ROOT" else echo "==> 3. 跳过数据库" fi if [[ "$SKIP_BUILD" == false ]]; then echo "==> 4. 构建(C 端使用 mini-user H5,跳过 h5-user)" pnpm --filter @dukang/shared-types \ --filter @dukang/domain \ --filter @dukang/api \ --filter @dukang/mini-user \ --filter @dukang/h5-shop \ --filter @dukang/h5-partner \ --filter @dukang/admin-web \ build # serve 读取 dist/serve.json(相对静态根目录) if [[ -f "$APP_ROOT/apps/mini-user/serve.json" ]]; then cp -f "$APP_ROOT/apps/mini-user/serve.json" "$APP_ROOT/apps/mini-user/dist/serve.json" fi else echo "==> 4. 跳过构建" if [[ -f "$APP_ROOT/apps/mini-user/serve.json" && -d "$APP_ROOT/apps/mini-user/dist" ]]; then cp -f "$APP_ROOT/apps/mini-user/serve.json" "$APP_ROOT/apps/mini-user/dist/serve.json" fi fi echo "==> 5. 重启 PM2 ($ECOSYSTEM_FILE)" if pm2 describe "$PM2_API" &>/dev/null; then pm2 delete "$PM2_USER" 2>/dev/null || true pm2 start "$DEPLOY_DIR/$ECOSYSTEM_FILE" --only "$PM2_USER" pm2 restart "$PM2_API" "$PM2_SHOP" "$PM2_PARTNER" "$PM2_ADMIN" 2>/dev/null \ || pm2 restart "$PM2_API" "$PM2_SHOP" "$PM2_PARTNER" else pm2 start "$DEPLOY_DIR/$ECOSYSTEM_FILE" fi pm2 save echo "==> 6. 健康检查" sleep 2 pm2 list ss -tlnp | grep -E "$PORT_GREP" || true curl -sf -o /dev/null -w "mini-user(h5): %{http_code}\n" "http://127.0.0.1:${USER_PORT}/" || echo "mini-user(h5): FAIL" curl -sf -o /dev/null -w "api: %{http_code}\n" "http://127.0.0.1:${API_PORT}/api/v1/health" 2>/dev/null \ || curl -sf -o /dev/null -w "api: %{http_code}\n" "http://127.0.0.1:${API_PORT}/" \ || echo "api: FAIL" echo "==> 7. 记录系统版本" cd "$APP_ROOT/server/dukang-api" APP_ROOT="$APP_ROOT" DEPLOY_TRIGGER="${DEPLOY_TRIGGER:-manual}" DUKANG_ENV_FILE="$ENV_FILE_NAME" \ node scripts/with-api-env.cjs node scripts/record-system-version.cjs \ || echo "WARN: system_version 写入失败" echo "==> 发版完成 ($DUKANG_DEPLOY_ENV)" if [[ "$DUKANG_DEPLOY_ENV" == "staging" ]]; then echo "==> 8. 重载 nginx(staging)" if command -v nginx >/dev/null 2>&1 && nginx -t 2>/dev/null; then systemctl reload nginx || echo "WARN: nginx reload failed" else echo "WARN: nginx 未安装或配置校验失败,跳过重载" fi fi