Files
web_hgdja_h5/deploy/up_next_h5.sh
T
谢宇宁andClaude Opus 5.5 6bd5cc665a Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:30:26 +07:00

94 lines
5.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.
#!/bin/bash
# Linux 测试服 tar.gz 更新入口;需预先配置 Supervisor 程序并手动调用。
set -Eeuo pipefail
DIR=$(cd "$(dirname "$0")" && pwd)
PROGRAM="${DEPLOY_PROGRAM:-91dj_next_h5}"
PACKAGE_NAME="${DEPLOY_PACKAGE_NAME:-web_91dj_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/.91dj-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/.91dj-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-test/BUILD_ID config/test.env package.json package-lock.json scripts/next-env.mjs scripts/check-health.mjs; do
[ -s "$STAGING/$file" ] || fail "包内缺少 $file"
done
[ -d "$STAGING/.next-test/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/test.local.env" ] && [ ! -L "$CURRENT/config/test.local.env" ]; then
cp "$CURRENT/config/test.local.env" "$STAGING/config/test.local.env"
chmod 600 "$STAGING/config/test.local.env"
fi
if [ -d "$CURRENT/.next-test/cache/seo" ] && [ ! -L "$CURRENT/.next-test/cache/seo" ]; then
mkdir -p "$STAGING/.next-test/cache"
cp -R "$CURRENT/.next-test/cache/seo" "$STAGING/.next-test/cache/seo"
fi
APP_PORT=$(cd "$STAGING" && node --input-type=module -e 'import{nextCommand}from"./scripts/next-env.mjs";console.log(nextCommand("test","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" test
supervisorctl status "$PROGRAM"
printf '测试服更新完成:%s;上一版本:%s\n' "$CURRENT" "${PREVIOUS:-无}"