代码更新
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Production defaults. Server overrides belong in production.local.env.
|
||||
APP_ENV=production
|
||||
PORT=21021
|
||||
PORT=20133
|
||||
# Server-only Go API origin. Browser requests use the current site's /api/*.
|
||||
UPSTREAM_API_URL=https://webhgcgapi.mfcg.me
|
||||
API_CHANNEL=
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
9ce909dab17ba6f48c9de93430a6a579
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
# Linux 生产服 tar.gz 更新入口;需预先配置 Supervisor 程序并手动调用。
|
||||
set -Eeuo pipefail
|
||||
DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
PROGRAM="${DEPLOY_PROGRAM:-web_hgcg_h5}"
|
||||
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_hgcg_h5.tar.gz}"
|
||||
CHECKSUM_NAME="$PACKAGE_NAME.sha256"
|
||||
[[ "$PROGRAM" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$ ]] || { echo 'DEPLOY_PROGRAM 格式无效' >&2; exit 1; }
|
||||
[[ "$PACKAGE_NAME" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.-]*\.tar\.gz$ ]] || { echo 'DEPLOY_PACKAGE_NAME 必须是 .tar.gz 文件名' >&2; exit 1; }
|
||||
CURRENT="$DIR/next"
|
||||
LOCK="$DIR/.$PROGRAM.lock"
|
||||
STAGING=''; UPLOAD=''; PREVIOUS=''; FAILED=''
|
||||
MOVED_OLD=0; INSTALLED=0; STOPPED=0; WAS_RUNNING=0
|
||||
fail() { printf '错误:%s\n' "$*" >&2; exit 1; }
|
||||
for tool in node npm sha256sum tar supervisorctl mktemp; do command -v "$tool" >/dev/null || fail "缺少命令 $tool"; done
|
||||
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'
|
||||
mkdir "$LOCK" 2>/dev/null || fail "已有更新进程或残留锁:$LOCK"
|
||||
finish() {
|
||||
local result=$?
|
||||
trap - EXIT INT TERM
|
||||
if [ "$result" != 0 ] && { [ "$STOPPED" = 1 ] || [ "$INSTALLED" = 1 ] || [ "$MOVED_OLD" = 1 ]; }; then
|
||||
printf '更新失败,恢复上一版本。\n' >&2
|
||||
supervisorctl stop "$PROGRAM" >/dev/null 2>&1 || true
|
||||
if [ "$INSTALLED" = 1 ]; then
|
||||
FAILED="$DIR/next.failed.$(date +%Y%m%d%H%M%S).$$"
|
||||
mv "$CURRENT" "$FAILED" || { echo "无法保存失败版本:$CURRENT" >&2; result=1; }
|
||||
fi
|
||||
if [ "$MOVED_OLD" = 1 ]; then
|
||||
mv "$PREVIOUS" "$CURRENT" || { echo "需要手动恢复:$PREVIOUS → $CURRENT" >&2; result=1; }
|
||||
fi
|
||||
if [ "$WAS_RUNNING" = 1 ]; then supervisorctl start "$PROGRAM" || result=1; fi
|
||||
fi
|
||||
if [ -n "$STAGING" ] && [ -d "$STAGING" ]; then rm -rf "$STAGING"; fi
|
||||
if [ -n "$UPLOAD" ] && [ -d "$UPLOAD" ]; then rm -rf "$UPLOAD"; fi
|
||||
rmdir "$LOCK" 2>/dev/null || true
|
||||
exit "$result"
|
||||
}
|
||||
trap finish EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
[ ! -L "$CURRENT" ] && { [ ! -e "$CURRENT" ] || [ -d "$CURRENT" ]; } || fail 'next 必须是普通目录'
|
||||
STATUS=$(supervisorctl status "$PROGRAM" 2>/dev/null || true)
|
||||
[[ "$STATUS" == "$PROGRAM "* ]] && [[ "$STATUS" != *'ERROR'* ]] || fail "请先配置 Supervisor 程序 $PROGRAM,command 指向 node $CURRENT/start.mjs"
|
||||
if [[ "$STATUS" == *'RUNNING'* ]]; then WAS_RUNNING=1; fi
|
||||
[ -s "$DIR/$PACKAGE_NAME" ] && [ -s "$DIR/$CHECKSUM_NAME" ] || fail "缺少 $PACKAGE_NAME 或 SHA-256"
|
||||
# Freeze the uploaded pair before validation/extraction; concurrent uploads cannot replace this copy.
|
||||
UPLOAD=$(mktemp -d "$DIR/.prod-upload.XXXXXX")
|
||||
cp "$DIR/$PACKAGE_NAME" "$DIR/$CHECKSUM_NAME" "$UPLOAD/"
|
||||
(cd "$UPLOAD" && sha256sum -c "$CHECKSUM_NAME") || fail 'SHA-256 校验失败'
|
||||
LC_ALL=C tar -tzf "$UPLOAD/$PACKAGE_NAME" > "$UPLOAD/members" || fail 'tar.gz 压缩包损坏'
|
||||
while IFS= read -r entry; do
|
||||
entry="${entry#./}"
|
||||
case "$entry" in
|
||||
/*|../*|*/../*|*/..|node_modules|node_modules/*|*/node_modules/*|*\\*) fail "非法 tar.gz 路径:$entry" ;;
|
||||
esac
|
||||
done < "$UPLOAD/members"
|
||||
# Reject links and special entries before extraction, including hard links to outside paths.
|
||||
LC_ALL=C tar -tvzf "$UPLOAD/$PACKAGE_NAME" > "$UPLOAD/types" || fail '无法检查 tar.gz 文件类型'
|
||||
while IFS= read -r entry; do
|
||||
case "$entry" in
|
||||
-?*|d?*) ;;
|
||||
*) fail '生产包只允许普通文件和目录,不允许链接或特殊文件' ;;
|
||||
esac
|
||||
done < "$UPLOAD/types"
|
||||
STAGING=$(mktemp -d "$DIR/.prod-release.XXXXXX")
|
||||
tar -xzf "$UPLOAD/$PACKAGE_NAME" -C "$STAGING" --no-same-owner --no-same-permissions
|
||||
[ -z "$(find "$STAGING" -type l -print -quit)" ] || fail '生产包不允许符号链接'
|
||||
for file in start.mjs server.js .next/BUILD_ID config/production.env package.json package-lock.json scripts/next-env.mjs scripts/check-health.mjs; do
|
||||
[ -s "$STAGING/$file" ] || fail "包内缺少 $file"
|
||||
done
|
||||
[ -d "$STAGING/.next/static" ] && [ -d "$STAGING/public" ] || fail '包内缺少静态资源'
|
||||
# Install for the server's real OS/CPU, rather than shipping this Mac's node_modules.
|
||||
(cd "$STAGING" && npm ci --omit=dev --no-audit --no-fund)
|
||||
if [ -f "$CURRENT/config/production.local.env" ] && [ ! -L "$CURRENT/config/production.local.env" ]; then
|
||||
cp "$CURRENT/config/production.local.env" "$STAGING/config/production.local.env"
|
||||
chmod 600 "$STAGING/config/production.local.env"
|
||||
fi
|
||||
if [ -d "$CURRENT/.next/cache/seo" ] && [ ! -L "$CURRENT/.next/cache/seo" ]; then
|
||||
mkdir -p "$STAGING/.next/cache"
|
||||
cp -R "$CURRENT/.next/cache/seo" "$STAGING/.next/cache/seo"
|
||||
fi
|
||||
APP_PORT=$(cd "$STAGING" && node --input-type=module -e 'import{nextCommand}from"./scripts/next-env.mjs";console.log(nextCommand("production","start",[],{root:process.cwd()}).env.PORT)')
|
||||
# Stop only after checksum, installation and configuration validation have succeeded.
|
||||
if [ "$WAS_RUNNING" = 1 ]; then supervisorctl stop "$PROGRAM"; STOPPED=1; fi
|
||||
if [ -d "$CURRENT" ]; then
|
||||
PREVIOUS="$DIR/next.prev.$(date +%Y%m%d%H%M%S).$$"
|
||||
mv "$CURRENT" "$PREVIOUS"; MOVED_OLD=1
|
||||
fi
|
||||
mv "$STAGING" "$CURRENT"; STAGING=''; INSTALLED=1
|
||||
supervisorctl start "$PROGRAM"
|
||||
node "$CURRENT/scripts/check-health.mjs" "http://127.0.0.1:$APP_PORT" production
|
||||
supervisorctl status "$PROGRAM"
|
||||
printf '生产服更新完成:%s;上一版本:%s\n' "$CURRENT" "${PREVIOUS:-无}"
|
||||
+42
-90
@@ -1,91 +1,43 @@
|
||||
#!/bin/bash
|
||||
# 91短剧正式服:构建 → 测试 → Linux standalone + MD5 → 可选触发 rsupdate.sh。
|
||||
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_91dj/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_91dj_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
|
||||
check_local production
|
||||
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
|
||||
printf 'DRY RUN:正式环境;Linux %s/%s;不构建、不连接服务器。\n' "$TARGET_ARCH" "$TARGET_LIBC"; public_profile production
|
||||
printf ' npm run check → npm run bundle → npm 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 '构建正式环境并运行回归测试'
|
||||
npm run check
|
||||
step '整理正式环境 standalone 和目标 Linux 原生依赖'
|
||||
npm run bundle
|
||||
npm run bundle:linux -- "$TARGET_ARCH" "$TARGET_LIBC"
|
||||
for file in start.mjs server.js .next/BUILD_ID config/production.env runtime-target.json; 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"
|
||||
# 生产部署:本地构建 Next.js standalone → tar.gz → scp → 远端 up_next_h5_prod.sh
|
||||
set -Eeuo pipefail
|
||||
DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
|
||||
SSH_TARGET="${DEPLOY_SSH_TARGET:-root@172.104.166.87}"
|
||||
REMOTE_DIR="${DEPLOY_REMOTE_DIR:-/server/web_hgcg/h5}"
|
||||
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_hgcg_h5.tar.gz}"
|
||||
PROGRAM="${DEPLOY_PROGRAM:-web_hgcg_h5}"
|
||||
REMOTE_SCRIPT="up_next_h5_prod.sh"
|
||||
|
||||
fail() { printf '错误:%s\n' "$*" >&2; exit 1; }
|
||||
for tool in node npm tar shasum ssh scp; do command -v "$tool" >/dev/null || fail "缺少命令 $tool"; done
|
||||
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'
|
||||
[ -f "$DIR/deploy/$REMOTE_SCRIPT" ] || fail "缺少 deploy/$REMOTE_SCRIPT"
|
||||
|
||||
STAGE="$DIR/dist/.prod-stage"
|
||||
rm -rf "$STAGE" "$DIR/dist/$PACKAGE_NAME" "$DIR/dist/$PACKAGE_NAME.sha256"
|
||||
mkdir -p "$STAGE"
|
||||
|
||||
echo "[1/5] 构建 production..."
|
||||
(cd "$DIR" && npm run build)
|
||||
|
||||
echo "[2/5] 打包 standalone..."
|
||||
(cd "$DIR" && npm run bundle)
|
||||
[ -d "$DIR/dist/production" ] || fail '构建产物 dist/production 不存在'
|
||||
|
||||
echo "[3/5] 生成 tar.gz 和 SHA-256..."
|
||||
# 从 dist/production/ 下进入打 tar,避免外层目录名出现在压缩包里;up_next_h5_prod.sh 预期入口在根(start.mjs 等)。
|
||||
# node_modules 不打进包,远端 npm ci --omit=dev 自己装 Linux 原生依赖;--no-xattrs 去 macOS 扩展属性噪音。
|
||||
(cd "$DIR/dist/production" && tar --no-xattrs --exclude='./node_modules' --exclude='.DS_Store' -czf "$STAGE/$PACKAGE_NAME" --owner=0 --group=0 .)
|
||||
(cd "$STAGE" && shasum -a 256 "$PACKAGE_NAME" > "$PACKAGE_NAME.sha256")
|
||||
ls -lh "$STAGE/$PACKAGE_NAME" "$STAGE/$PACKAGE_NAME.sha256"
|
||||
|
||||
echo "[4/5] 上传到 $SSH_TARGET:$REMOTE_DIR ..."
|
||||
# 先传服务器端脚本(可能更新了逻辑),再传压缩包和校验和。
|
||||
scp "$DIR/deploy/$REMOTE_SCRIPT" "$SSH_TARGET:$REMOTE_DIR/$REMOTE_SCRIPT"
|
||||
scp "$STAGE/$PACKAGE_NAME" "$STAGE/$PACKAGE_NAME.sha256" "$SSH_TARGET:$REMOTE_DIR/"
|
||||
|
||||
echo "[5/5] 远端执行解包和热替换..."
|
||||
ssh "$SSH_TARGET" "chmod +x $REMOTE_DIR/$REMOTE_SCRIPT && DEPLOY_PROGRAM=$PROGRAM DEPLOY_PACKAGE_NAME=$PACKAGE_NAME bash $REMOTE_DIR/$REMOTE_SCRIPT"
|
||||
|
||||
echo DEPLOY_OK
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 91短剧测试服:构建 → 测试 → tar.gz + SHA-256 → 可选上传并执行 run.sh。
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
cd "$SCRIPT_DIR"
|
||||
source "$SCRIPT_DIR/scripts/deploy-common.sh"
|
||||
HOST="${DEPLOY_HOST:-root@172.104.181.99}"
|
||||
SSH_PORT="${DEPLOY_SSH_PORT:-22889}"
|
||||
# 保留 91dj-web 已约定的上传位置;项目包和 Supervisor 标识使用 91dj。
|
||||
REMOTE_DIR="${DEPLOY_DIR:-/server/web_91dj/h5}"
|
||||
# 远端启动脚本目录独立于上传目录,按服务器约定执行。
|
||||
RUN_DIR="${DEPLOY_RUN_DIR:-/server/web_91dj/h5}"
|
||||
CHECK_URL="${DEPLOY_CHECK_URL:-}"
|
||||
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_91dj_h5.tar.gz}"
|
||||
CHECKSUM_NAME="$PACKAGE_NAME.sha256"
|
||||
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 %s/。\n' "$HOST" "$SSH_PORT" "${REMOTE_DIR%/}"
|
||||
printf '上传成功后:ssh -p %s %s "cd %s && bash run.sh"\n' "$SSH_PORT" "$HOST" "$RUN_DIR"
|
||||
printf '可覆盖 DEPLOY_HOST、DEPLOY_DIR、DEPLOY_RUN_DIR(%s)、DEPLOY_SSH_PORT、DEPLOY_PACKAGE_NAME(%s)、DEPLOY_CHECK_URL。\n' "$RUN_DIR" "$PACKAGE_NAME"
|
||||
exit 0 ;;
|
||||
*) fail "不支持的参数 $argument" ;;
|
||||
esac
|
||||
done
|
||||
[[ "$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 "$HOST" DEPLOY_HOST; valid_path "$REMOTE_DIR" DEPLOY_DIR; valid_path "$RUN_DIR" DEPLOY_RUN_DIR; valid_port "$SSH_PORT" DEPLOY_SSH_PORT
|
||||
need ssh; need scp
|
||||
fi
|
||||
check_local test
|
||||
if [ -n "$CHECK_URL" ]; then
|
||||
node --input-type=module -e 'const u=new URL(process.argv[1]);if(!["http:","https:"].includes(u.protocol)||u.username||u.password||u.search||u.hash||u.pathname!=="/")process.exit(1)' "$CHECK_URL" || fail 'DEPLOY_CHECK_URL 必须是 HTTP(S) 站点根地址'
|
||||
fi
|
||||
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
|
||||
printf 'DRY RUN:测试环境;不构建、不连接服务器。\n'; public_profile test
|
||||
printf ' npm run build:test → npm test → npm run bundle:test → %s + %s\n' "$PACKAGE_NAME" "$CHECKSUM_NAME"
|
||||
printf ' 包内不含 node_modules、本地环境覆盖和缓存;服务端 npm ci --omit=dev 后 node start.mjs。\n'
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then printf ' 仅生成本地包。\n'
|
||||
else
|
||||
printf ' 发布目标:%s:%s(SSH %s)。\n' "$HOST" "$REMOTE_DIR" "$SSH_PORT"
|
||||
printf ' 上传 %s、%s 和 up_next_h5.sh。\n' "$PACKAGE_NAME" "$CHECKSUM_NAME"
|
||||
printf ' 上传成功后:ssh -p %s %s "cd %s && bash run.sh"\n' "$SSH_PORT" "$HOST" "$RUN_DIR"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
need tar
|
||||
[ -s deploy/up_next_h5.sh ] || fail '缺少 deploy/up_next_h5.sh'
|
||||
bash -n deploy/up_next_h5.sh
|
||||
lock_build
|
||||
step '构建测试环境并运行回归测试'
|
||||
npm run build:test
|
||||
TEST_APP_ENV=test npm test
|
||||
step '整理 standalone 测试包'
|
||||
npm run bundle:test
|
||||
for file in start.mjs server.js .next-test/BUILD_ID config/test.env; do
|
||||
[ -s "dist/test/$file" ] || fail "测试包缺少 $file"
|
||||
done
|
||||
[ -d dist/test/.next-test/static ] && [ -d dist/test/public ] || fail '测试包缺少静态资源'
|
||||
step "生成 $PACKAGE_NAME 和 SHA-256"
|
||||
TEMP_PACKAGE=$(mktemp "$SCRIPT_DIR/dist/.test-archive.XXXXXX")
|
||||
if ! COPYFILE_DISABLE=1 COPY_EXTENDED_ATTRIBUTES_DISABLE=1 tar --no-xattrs \
|
||||
--exclude='node_modules' --exclude='*/node_modules' --exclude='._*' --exclude='.DS_Store' \
|
||||
--exclude='*.local.env' --exclude='.env*' --exclude='*.log' \
|
||||
-czf "$TEMP_PACKAGE" -C dist/test .; then
|
||||
rm -f "$TEMP_PACKAGE"; fail '压缩失败'
|
||||
fi
|
||||
tar -tzf "$TEMP_PACKAGE" >/dev/null
|
||||
mv -f "$TEMP_PACKAGE" "$PACKAGE_NAME"
|
||||
chmod 600 "$PACKAGE_NAME"
|
||||
node --input-type=module - "$PACKAGE_NAME" "$CHECKSUM_NAME" <<'NODE'
|
||||
import {createHash} from 'node:crypto';
|
||||
import {readFileSync,writeFileSync} from 'node:fs';
|
||||
const [archive,checksum]=process.argv.slice(2);
|
||||
const bytes=readFileSync(archive);
|
||||
writeFileSync(checksum,createHash('sha256').update(bytes).digest('hex')+' '+archive+'\n');
|
||||
// The test and production commands share a package name; refresh its companion checksum too.
|
||||
writeFileSync(archive.replace(/\.tar\.gz$/,'.md5'),createHash('md5').update(bytes).digest('hex')+'\n');
|
||||
NODE
|
||||
printf '测试包:%s/%s;校验:%s/%s\n' "$SCRIPT_DIR" "$PACKAGE_NAME" "$SCRIPT_DIR" "$CHECKSUM_NAME"
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then exit 0; fi
|
||||
step "上传到 $HOST:$REMOTE_DIR/"
|
||||
ssh -p "$SSH_PORT" "$HOST" "mkdir -p '$REMOTE_DIR'"
|
||||
scp -P "$SSH_PORT" "$PACKAGE_NAME" "$CHECKSUM_NAME" deploy/up_next_h5.sh "$HOST:$REMOTE_DIR/"
|
||||
printf '测试包上传完成:%s:%s/%s\n' "$HOST" "${REMOTE_DIR%/}" "$PACKAGE_NAME"
|
||||
step "执行测试服 $RUN_DIR/run.sh"
|
||||
ssh -p "$SSH_PORT" "$HOST" "cd '$RUN_DIR' && bash run.sh"
|
||||
if [ -n "$CHECK_URL" ]; then node scripts/check-health.mjs "$CHECK_URL" test; fi
|
||||
printf '测试服 run.sh 执行完成:%s:%s/run.sh\n' "$HOST" "${RUN_DIR%/}"
|
||||
@@ -80,7 +80,7 @@ const getReadIdentity=cache(async ()=>{
|
||||
const userAgent=requestHeaders.get('user-agent')||'91-drama-web/1.0';
|
||||
const token=jar.get('drama_session')?.value||'';
|
||||
const ip=(requestHeaders.get('x-real-ip')||requestHeaders.get('x-forwarded-for')?.split(',')[0]||'').trim();
|
||||
return {token,device,userAgent,clientIp:isIP(ip)?ip:undefined,prepared:requestHeaders.has(SSR_SESSION_HEADER),channel:(jar.get('drama_channel')?.value||process.env.API_CHANNEL||'').slice(0,100)};
|
||||
return {token,device,userAgent,clientIp:isIP(ip)?ip:undefined,prepared:requestHeaders.get(SSR_SESSION_HEADER)==='ready',channel:(jar.get('drama_channel')?.value||process.env.API_CHANNEL||'').slice(0,100)};
|
||||
});
|
||||
const getReadGuest=cache(async ()=>{
|
||||
const identity=await getReadIdentity();
|
||||
|
||||
Reference in New Issue
Block a user