Files
web_hgdjb_h5/deploy_prod.sh
T
谢宇宁andClaude Opus 5.5 6fb2b1e486 Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:26:18 +07:00

96 lines
5.5 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.
#!/bin/bash
# 熟果短剧正式服(wdb_sgdj):构建 → Linux standalone + MD5 → 可选触发 rsupdate.sh。
# 对齐 jha-1048-hddj-web;H5 监听 21038,包目录 /home/server/web_sgdj/h5。
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
cd "$SCRIPT_DIR"
source "$SCRIPT_DIR/scripts/deploy-common.sh"
JUMP_HOST="${DEPLOY_JUMP_HOST:-server@34.92.204.175}"
# 压缩包上传完成后,在同一目录执行服务器现有的 rsupdate.sh。
JUMP_DIR="${DEPLOY_JUMP_DIR:-/home/server/web_sgdj/h5}"
# valid_path 限制目录为安全字符;命令文本与手动执行一致,兼容 SSH 命令白名单。
REMOTE_UPDATE_COMMAND="cd ${JUMP_DIR%/} && bash rsupdate.sh"
SCP_PORT="${DEPLOY_SCP_PORT:-22889}"
SSH_PORT="${DEPLOY_SSH_PORT:-22889}"
TARGET_ARCH="${DEPLOY_TARGET_ARCH:-x64}"
TARGET_LIBC="${DEPLOY_TARGET_LIBC:-glibc}"
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_sgdj_h5.tar.gz}"
MD5_NAME="${PACKAGE_NAME%.tar.gz}.md5"
RELEASE="$SCRIPT_DIR/dist/production-linux-$TARGET_ARCH-$TARGET_LIBC"
PACKAGE_ONLY=0
for argument in "$@"; do
case "$argument" in
--dry-run) DEPLOY_DRY_RUN=1 ;;
--package-only) PACKAGE_ONLY=1 ;;
-h|--help)
printf '用法:%s [--dry-run] [--package-only]\n' "$0"
printf '仅打包:--package-only。仅预览:--dry-run(不构建、不联网)。\n'
printf '上传目标:%s:%s/;上传成功后在该目录执行 bash rsupdate.sh。\n' "$JUMP_HOST" "${JUMP_DIR%/}"
printf '可覆盖 DEPLOY_JUMP_HOST、DEPLOY_JUMP_DIR、DEPLOY_SCP_PORT(%s)、DEPLOY_SSH_PORT(%s)、DEPLOY_PACKAGE_NAME(%s)、DEPLOY_TARGET_ARCH(x64/arm64)、DEPLOY_TARGET_LIBC(glibc/musl)。\n' "$SCP_PORT" "$SSH_PORT" "$PACKAGE_NAME"
exit 0 ;;
*) fail "不支持的参数 $argument" ;;
esac
done
case "$TARGET_ARCH" in x64|arm64) ;; *) fail 'DEPLOY_TARGET_ARCH 仅支持 x64 或 arm64' ;; esac
case "$TARGET_LIBC" in glibc|musl) ;; *) fail 'DEPLOY_TARGET_LIBC 仅支持 glibc 或 musl' ;; esac
[[ "$PACKAGE_NAME" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.-]*\.tar\.gz$ ]] || fail 'DEPLOY_PACKAGE_NAME 必须是 .tar.gz 文件名'
if [ "$PACKAGE_ONLY" = 0 ] && [ "${DEPLOY_DRY_RUN:-0}" != 1 ]; then
valid_host "$JUMP_HOST" DEPLOY_JUMP_HOST; valid_path "$JUMP_DIR" DEPLOY_JUMP_DIR
valid_port "$SCP_PORT" DEPLOY_SCP_PORT; valid_port "$SSH_PORT" DEPLOY_SSH_PORT
need ssh; need scp; need tee
fi
need node; need pnpm
node -e 'const [a,b]=process.versions.node.split(".").map(Number);if(a<20||a===20&&b<9)process.exit(1)' || fail '需要 Node.js >=20.9,推荐 22'
[ -d node_modules/next ] && [ -d node_modules/dotenv ] || fail '请先执行 pnpm install'
[ -s config/production.env ] && [ -s pnpm-lock.yaml ] || fail '缺少 config/production.env 或 pnpm-lock.yaml'
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
printf 'DRY RUN:正式环境;Linux %s/%s;不构建、不连接服务器。\n' "$TARGET_ARCH" "$TARGET_LIBC"; public_profile production
printf ' pnpm run build → pnpm run bundle → pnpm run bundle:linux -- %s %s\n' "$TARGET_ARCH" "$TARGET_LIBC"
printf ' 保留 standalone 裁剪依赖,仅安装 lockfile 中目标 Linux 的 Sharp/libvips。\n'
printf ' 生成 %s + %s(纯 MD5 hash)。\n' "$PACKAGE_NAME" "$MD5_NAME"
if [ "$PACKAGE_ONLY" = 1 ]; then printf ' 仅生成本地包。\n'
else
printf ' scp -P %s %s %s %s:%s/\n' "$SCP_PORT" "$PACKAGE_NAME" "$MD5_NAME" "$JUMP_HOST" "${JUMP_DIR%/}"
printf ' ssh -p %s %s "%s"\n' "$SSH_PORT" "$JUMP_HOST" "$REMOTE_UPDATE_COMMAND"
fi
exit 0
fi
need tar
command -v md5sum >/dev/null || command -v md5 >/dev/null || fail '缺少 md5sum 或 macOS md5'
lock_build
step '构建正式环境 standalone'
pnpm run build
step '整理正式环境 standalone 和目标 Linux 原生依赖'
pnpm run bundle
pnpm run bundle:linux -- "$TARGET_ARCH" "$TARGET_LIBC"
for file in start.mjs server.js .next/BUILD_ID config/production.env runtime-target.json ecosystem.config.cjs; do
[ -s "$RELEASE/$file" ] || fail "正式包缺少 $file"
done
for directory in node_modules .next/static public; do [ -d "$RELEASE/$directory" ] || fail "正式包缺少 $directory"; done
step "生成 $PACKAGE_NAME"
TEMP_PACKAGE=$(mktemp "$SCRIPT_DIR/dist/.production-archive.XXXXXX")
if ! COPYFILE_DISABLE=1 COPY_EXTENDED_ATTRIBUTES_DISABLE=1 tar --no-xattrs \
--exclude='._*' --exclude='.DS_Store' --exclude='.env*' --exclude='*.local.env' --exclude='*.log' \
-czf "$TEMP_PACKAGE" -C "$RELEASE" .; then
rm -f "$TEMP_PACKAGE"; fail '压缩失败'
fi
tar -tzf "$TEMP_PACKAGE" >/dev/null
mv -f "$TEMP_PACKAGE" "$PACKAGE_NAME"
chmod 600 "$PACKAGE_NAME"
if command -v md5sum >/dev/null; then md5sum "$PACKAGE_NAME" | awk '{print $1}' > "$MD5_NAME"
else md5 -q "$PACKAGE_NAME" > "$MD5_NAME"; fi
printf '正式包:%s/%s;校验:%s/%s\n' "$SCRIPT_DIR" "$PACKAGE_NAME" "$SCRIPT_DIR" "$MD5_NAME"
if [ "$PACKAGE_ONLY" = 1 ]; then exit 0; fi
step "上传到 $JUMP_HOST:$JUMP_DIR/"
scp -P "$SCP_PORT" "$PACKAGE_NAME" "$MD5_NAME" "$JUMP_HOST:${JUMP_DIR%/}/"
step '执行正式服 rsupdate.sh'
printf '远端命令:%s\n' "$REMOTE_UPDATE_COMMAND"
RUN_LOG=$(mktemp "$SCRIPT_DIR/dist/production-rsupdate.XXXXXX")
if ! ssh -p "$SSH_PORT" "$JUMP_HOST" "$REMOTE_UPDATE_COMMAND" 2>&1 | tee "$RUN_LOG"; then fail "rsupdate.sh 执行失败,日志:$RUN_LOG"; fi
if ! node - "$RUN_LOG" <<'NODE'
const output=require('node:fs').readFileSync(process.argv[2],'utf8').replace(/\x1b\[[0-?]*[ -/]*[@-~]/g,'');
if(/\b(?:BACKOFF|FATAL|ERROR)\b|\bspawn\s+error\b/i.test(output))process.exit(1);
NODE
then fail "远端输出包含失败状态,日志:$RUN_LOG"; fi
printf '正式服 rsupdate.sh 执行完成;请核对远端服务状态。日志:%s\n' "$RUN_LOG"