迁移892-without-redis分支全量功能
This commit is contained in:
257
bin/prod/deploy_from_package.sh
Executable file
257
bin/prod/deploy_from_package.sh
Executable file
@@ -0,0 +1,257 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
JAVA_BIN="/home/webapp/env/java/bin/java"
|
||||
BACKEND_PORT=63310
|
||||
SPRING_PROFILE="uat"
|
||||
JAVA_OPTS="-Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"
|
||||
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
|
||||
BACKEND_DIR="$SCRIPT_DIR/backend"
|
||||
FRONTEND_DIR="$SCRIPT_DIR/frontend"
|
||||
BACKEND_JAR_TARGET="$BACKEND_DIR/ruoyi-admin.jar"
|
||||
BACKEND_PID_FILE="$BACKEND_DIR/backend.pid"
|
||||
BACKEND_LOG_FILE="$BACKEND_DIR/backend-console.log"
|
||||
FRONTEND_DIST_ARCHIVE="$FRONTEND_DIR/dist.zip"
|
||||
FRONTEND_DIST_DIR="$FRONTEND_DIR/dist"
|
||||
BACKEND_MARKER="-Dloan.pricing.home=$SCRIPT_DIR"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法:
|
||||
./deploy_from_package.sh
|
||||
EOF
|
||||
}
|
||||
|
||||
timestamp() {
|
||||
date "+%Y%m%d%H%M%S"
|
||||
}
|
||||
|
||||
log_info() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >&2
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [ -n "${WORK_DIR:-}" ] && [ -d "$WORK_DIR" ]; then
|
||||
rm -rf "$WORK_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
require_dir() {
|
||||
if [ ! -d "$1" ]; then
|
||||
log_error "缺少目录: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_command() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
log_error "缺少命令: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
find_release_archive() {
|
||||
archives=$(find "$SCRIPT_DIR" -maxdepth 1 -type f -name '*.zip' ! -name 'dist.zip')
|
||||
count=$(printf '%s\n' "$archives" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||
|
||||
if [ "$count" -ne 1 ]; then
|
||||
log_error "脚本同目录发布 zip 数量不正确,期望 1 个,实际 $count 个"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$archives"
|
||||
}
|
||||
|
||||
extract_release_package() {
|
||||
release_archive="$1"
|
||||
release_extract_dir="$2"
|
||||
|
||||
mkdir -p "$release_extract_dir"
|
||||
unzip -oq "$release_archive" -d "$release_extract_dir"
|
||||
}
|
||||
|
||||
assert_single_jar() {
|
||||
search_dir="$1"
|
||||
count=$(find "$search_dir" -type f -name '*.jar' ! -path '*/__MACOSX/*' ! -name '._*' | wc -l | tr -d ' ')
|
||||
|
||||
if [ "$count" -ne 1 ]; then
|
||||
log_error "后端 jar 数量不正确,期望 1 个,实际 $count 个"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find "$search_dir" -type f -name '*.jar' ! -path '*/__MACOSX/*' ! -name '._*' | head -n 1
|
||||
}
|
||||
|
||||
assert_single_dist_zip() {
|
||||
search_dir="$1"
|
||||
count=$(find "$search_dir" -type f -name 'dist.zip' ! -path '*/__MACOSX/*' ! -name '._*' | wc -l | tr -d ' ')
|
||||
|
||||
if [ "$count" -ne 1 ]; then
|
||||
log_error "前端 dist.zip 数量不正确,期望 1 个,实际 $count 个"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find "$search_dir" -type f -name 'dist.zip' ! -path '*/__MACOSX/*' ! -name '._*' | head -n 1
|
||||
}
|
||||
|
||||
backup_backend_jar() {
|
||||
if [ -f "$BACKEND_JAR_TARGET" ]; then
|
||||
mv "$BACKEND_JAR_TARGET" "$BACKEND_JAR_TARGET.$(timestamp).bak"
|
||||
fi
|
||||
}
|
||||
|
||||
backup_frontend_dist() {
|
||||
if [ -d "$FRONTEND_DIST_DIR" ]; then
|
||||
mv "$FRONTEND_DIST_DIR" "$FRONTEND_DIR/dist-$(timestamp)"
|
||||
fi
|
||||
}
|
||||
|
||||
deploy_backend_jar() {
|
||||
source_jar="$1"
|
||||
mv "$source_jar" "$BACKEND_JAR_TARGET"
|
||||
}
|
||||
|
||||
deploy_frontend_dist() {
|
||||
source_dist_zip="$1"
|
||||
rm -f "$FRONTEND_DIST_ARCHIVE"
|
||||
rm -rf "$FRONTEND_DIST_DIR"
|
||||
mv "$source_dist_zip" "$FRONTEND_DIST_ARCHIVE"
|
||||
unzip -oq "$FRONTEND_DIST_ARCHIVE" -d "$FRONTEND_DIR"
|
||||
|
||||
if [ ! -d "$FRONTEND_DIST_DIR" ]; then
|
||||
log_error "dist.zip 解压后未找到 $FRONTEND_DIST_DIR"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
collect_backend_pids() {
|
||||
ps -ef | awk -v marker="$BACKEND_MARKER" -v jar="$BACKEND_JAR_TARGET" '
|
||||
index($0, "<defunct>") == 0 && index($0, marker) > 0 {
|
||||
for (i = 1; i < NF; i++) {
|
||||
if ($i == "-jar" && $(i + 1) == jar) {
|
||||
print $2
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
' | xargs 2>/dev/null || true
|
||||
}
|
||||
|
||||
stop_backend() {
|
||||
pids=$(collect_backend_pids)
|
||||
|
||||
if [ -z "${pids:-}" ]; then
|
||||
rm -f "$BACKEND_PID_FILE"
|
||||
log_info "未发现运行中的后端进程"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "停止后端进程: $pids"
|
||||
for pid in $pids; do
|
||||
kill -TERM "$pid" 2>/dev/null || true
|
||||
done
|
||||
|
||||
elapsed=0
|
||||
remaining="$pids"
|
||||
while [ "$elapsed" -lt 30 ]; do
|
||||
remaining=""
|
||||
for pid in $pids; do
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
remaining="$remaining $pid"
|
||||
fi
|
||||
done
|
||||
|
||||
remaining=$(echo "$remaining" | xargs 2>/dev/null || true)
|
||||
if [ -z "${remaining:-}" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
elapsed=$((elapsed + 1))
|
||||
done
|
||||
|
||||
if [ -n "${remaining:-}" ]; then
|
||||
log_info "执行强制停止: $remaining"
|
||||
for pid in $remaining; do
|
||||
kill -KILL "$pid" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
rm -f "$BACKEND_PID_FILE"
|
||||
}
|
||||
|
||||
start_backend() {
|
||||
if [ ! -x "$JAVA_BIN" ]; then
|
||||
log_error "未检测到可执行 Java: $JAVA_BIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$BACKEND_JAR_TARGET" ]; then
|
||||
log_error "未找到后端 jar: $BACKEND_JAR_TARGET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$(collect_backend_pids)" ]; then
|
||||
log_error "检测到后端已在运行,请先停止旧进程"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '\n===== %s deploy =====\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> "$BACKEND_LOG_FILE"
|
||||
|
||||
nohup "$JAVA_BIN" $JAVA_OPTS "$BACKEND_MARKER" -jar "$BACKEND_JAR_TARGET" \
|
||||
--spring.profiles.active="$SPRING_PROFILE" \
|
||||
--server.port="$BACKEND_PORT" >> "$BACKEND_LOG_FILE" 2>&1 &
|
||||
backend_pid=$!
|
||||
printf '%s\n' "$backend_pid" > "$BACKEND_PID_FILE"
|
||||
|
||||
sleep 1
|
||||
|
||||
if ! kill -0 "$backend_pid" 2>/dev/null; then
|
||||
log_error "后端启动失败,请检查日志: $BACKEND_LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "后端已启动,PID: $backend_pid"
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 0 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_dir "$BACKEND_DIR"
|
||||
require_dir "$FRONTEND_DIR"
|
||||
require_command unzip
|
||||
require_command find
|
||||
require_command ps
|
||||
require_command nohup
|
||||
|
||||
release_archive=$(find_release_archive)
|
||||
WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/deploy_from_package.XXXXXX")
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
extract_release_package "$release_archive" "$WORK_DIR/package"
|
||||
|
||||
backend_jar_source=$(assert_single_jar "$WORK_DIR/package")
|
||||
frontend_dist_source=$(assert_single_dist_zip "$WORK_DIR/package")
|
||||
|
||||
backup_backend_jar
|
||||
backup_frontend_dist
|
||||
stop_backend
|
||||
deploy_backend_jar "$backend_jar_source"
|
||||
deploy_frontend_dist "$frontend_dist_source"
|
||||
start_backend
|
||||
|
||||
log_info "部署完成"
|
||||
log_info "后端 jar: $BACKEND_JAR_TARGET"
|
||||
log_info "前端目录: $FRONTEND_DIST_DIR"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
262
bin/prod/deploy_from_package_test.sh
Executable file
262
bin/prod/deploy_from_package_test.sh
Executable file
@@ -0,0 +1,262 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT_DIR=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
|
||||
SCRIPT_UNDER_TEST="$ROOT_DIR/bin/prod/deploy_from_package.sh"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_file_exists() {
|
||||
file_path="$1"
|
||||
[ -e "$file_path" ] || fail "expected file to exist: $file_path"
|
||||
}
|
||||
|
||||
assert_grep() {
|
||||
pattern="$1"
|
||||
target="$2"
|
||||
if ! grep -Eq "$pattern" "$target"; then
|
||||
fail "expected pattern [$pattern] in $target"
|
||||
fi
|
||||
}
|
||||
|
||||
create_fake_java() {
|
||||
fake_java="$1"
|
||||
|
||||
cat > "$fake_java" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
port=""
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--server.port=*)
|
||||
port=${arg#--server.port=}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$port" ]; then
|
||||
echo "missing port" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while :; do
|
||||
sleep 1
|
||||
done
|
||||
EOF
|
||||
|
||||
chmod +x "$fake_java"
|
||||
}
|
||||
|
||||
create_release_zip() {
|
||||
release_dir="$1"
|
||||
release_zip_name="$2"
|
||||
|
||||
mkdir -p "$release_dir/package/deploy" "$release_dir/package/__MACOSX/deploy"
|
||||
mkdir -p "$release_dir/package/frontend_payload/dist" "$release_dir/package/frontend_payload/__MACOSX/dist"
|
||||
printf 'new-jar\n' > "$release_dir/package/deploy/ruoyi-admin.jar"
|
||||
printf 'macos-meta\n' > "$release_dir/package/__MACOSX/deploy/._ruoyi-admin.jar"
|
||||
printf '<html>new</html>\n' > "$release_dir/package/frontend_payload/dist/index.html"
|
||||
printf 'macos-meta\n' > "$release_dir/package/frontend_payload/__MACOSX/dist/._index.html"
|
||||
(
|
||||
cd "$release_dir/package/frontend_payload"
|
||||
zip -qr "$release_dir/package/dist.zip" dist __MACOSX
|
||||
)
|
||||
mv "$release_dir/package/dist.zip" "$release_dir/package/deploy/dist.zip"
|
||||
(
|
||||
cd "$release_dir/package"
|
||||
zip -qr "$release_dir/$release_zip_name" deploy __MACOSX
|
||||
)
|
||||
}
|
||||
|
||||
find_free_port() {
|
||||
python3 - <<'PY'
|
||||
import socket
|
||||
|
||||
sock = socket.socket()
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
print(sock.getsockname()[1])
|
||||
sock.close()
|
||||
PY
|
||||
}
|
||||
|
||||
prepare_release_dir() {
|
||||
release_dir="$1"
|
||||
backend_port="$2"
|
||||
|
||||
mkdir -p "$release_dir/backend" "$release_dir/frontend" "$release_dir/fake-java-bin"
|
||||
printf 'old-jar\n' > "$release_dir/backend/ruoyi-admin.jar"
|
||||
mkdir -p "$release_dir/frontend/dist"
|
||||
printf '<html>old</html>\n' > "$release_dir/frontend/dist/index.html"
|
||||
|
||||
create_fake_java "$release_dir/fake-java-bin/java"
|
||||
create_release_zip "$release_dir" "deploy.zip"
|
||||
cp "$SCRIPT_UNDER_TEST" "$release_dir/deploy_from_package.sh"
|
||||
perl -0pi -e "s#JAVA_BIN=\"/home/webapp/env/java/bin/java\"#JAVA_BIN=\"$release_dir/fake-java-bin/java\"#" \
|
||||
"$release_dir/deploy_from_package.sh"
|
||||
perl -0pi -e "s/BACKEND_PORT=63310/BACKEND_PORT=$backend_port/" \
|
||||
"$release_dir/deploy_from_package.sh"
|
||||
chmod +x "$release_dir/deploy_from_package.sh"
|
||||
}
|
||||
|
||||
cleanup_release_dir() {
|
||||
release_dir="$1"
|
||||
|
||||
if [ -f "$release_dir/backend/backend.pid" ]; then
|
||||
backend_pid=$(cat "$release_dir/backend/backend.pid" 2>/dev/null || true)
|
||||
if [ -n "${backend_pid:-}" ]; then
|
||||
kill "$backend_pid" 2>/dev/null || true
|
||||
wait "$backend_pid" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "$release_dir"
|
||||
}
|
||||
|
||||
test_deploy_success() {
|
||||
release_dir=$(mktemp -d)
|
||||
backend_port=$(find_free_port)
|
||||
trap 'cleanup_release_dir "$release_dir"' EXIT INT TERM
|
||||
|
||||
prepare_release_dir "$release_dir" "$backend_port"
|
||||
(
|
||||
cd "$release_dir"
|
||||
./deploy_from_package.sh
|
||||
)
|
||||
|
||||
assert_file_exists "$release_dir/backend/ruoyi-admin.jar"
|
||||
assert_file_exists "$release_dir/frontend/dist.zip"
|
||||
assert_file_exists "$release_dir/frontend/dist/index.html"
|
||||
assert_file_exists "$release_dir/backend/backend.pid"
|
||||
assert_file_exists "$release_dir/backend/backend-console.log"
|
||||
assert_grep 'new' "$release_dir/frontend/dist/index.html"
|
||||
|
||||
backup_jar_count=$(find "$release_dir/backend" -maxdepth 1 -type f -name 'ruoyi-admin.jar.*.bak' | wc -l | tr -d ' ')
|
||||
[ "$backup_jar_count" -eq 1 ] || fail "expected one backup jar, got $backup_jar_count"
|
||||
|
||||
backup_dist_count=$(find "$release_dir/frontend" -maxdepth 1 -type d -name 'dist-*' | wc -l | tr -d ' ')
|
||||
[ "$backup_dist_count" -eq 1 ] || fail "expected one backup dist dir, got $backup_dist_count"
|
||||
|
||||
backend_pid=$(cat "$release_dir/backend/backend.pid")
|
||||
kill -0 "$backend_pid" 2>/dev/null || fail "expected backend pid to be running"
|
||||
|
||||
trap - EXIT INT TERM
|
||||
cleanup_release_dir "$release_dir"
|
||||
}
|
||||
|
||||
test_multiple_release_zip_should_fail() {
|
||||
release_dir=$(mktemp -d)
|
||||
backend_port=$(find_free_port)
|
||||
trap 'cleanup_release_dir "$release_dir"' EXIT INT TERM
|
||||
|
||||
prepare_release_dir "$release_dir" "$backend_port"
|
||||
cp "$release_dir/deploy.zip" "$release_dir/deploy-copy.zip"
|
||||
|
||||
if (
|
||||
cd "$release_dir"
|
||||
./deploy_from_package.sh >/tmp/deploy_from_package_test.stderr 2>&1
|
||||
); then
|
||||
fail "expected deploy_from_package.sh to fail when multiple release zips exist"
|
||||
fi
|
||||
|
||||
assert_file_exists /tmp/deploy_from_package_test.stderr
|
||||
assert_grep '发布 zip 数量不正确' /tmp/deploy_from_package_test.stderr
|
||||
|
||||
rm -f /tmp/deploy_from_package_test.stderr
|
||||
trap - EXIT INT TERM
|
||||
cleanup_release_dir "$release_dir"
|
||||
}
|
||||
|
||||
test_defunct_process_should_be_ignored() {
|
||||
release_dir=$(mktemp -d)
|
||||
backend_port=$(find_free_port)
|
||||
trap 'cleanup_release_dir "$release_dir"' EXIT INT TERM
|
||||
|
||||
prepare_release_dir "$release_dir" "$backend_port"
|
||||
mkdir -p "$release_dir/fake-ps-bin"
|
||||
cat > "$release_dir/fake-ps-bin/ps" <<EOF
|
||||
#!/bin/sh
|
||||
if [ "\$1" = "-ef" ]; then
|
||||
cat <<'PSOUT'
|
||||
UID PID PPID C STIME TTY TIME CMD
|
||||
root 99999 1 0 00:00 ? 00:00:00 [java] <defunct> -Dloan.pricing.home=$release_dir -jar $release_dir/backend/ruoyi-admin.jar
|
||||
PSOUT
|
||||
exit 0
|
||||
fi
|
||||
/bin/ps "\$@"
|
||||
EOF
|
||||
chmod +x "$release_dir/fake-ps-bin/ps"
|
||||
|
||||
(
|
||||
cd "$release_dir"
|
||||
PATH="$release_dir/fake-ps-bin:/usr/bin:/bin" ./deploy_from_package.sh
|
||||
)
|
||||
|
||||
backend_pid=$(cat "$release_dir/backend/backend.pid")
|
||||
kill -0 "$backend_pid" 2>/dev/null || fail "expected backend pid to be running when defunct process is ignored"
|
||||
|
||||
trap - EXIT INT TERM
|
||||
cleanup_release_dir "$release_dir"
|
||||
}
|
||||
|
||||
test_only_current_project_jar_should_match() {
|
||||
release_dir=$(mktemp -d)
|
||||
backend_port=$(find_free_port)
|
||||
trap 'cleanup_release_dir "$release_dir"' EXIT INT TERM
|
||||
|
||||
prepare_release_dir "$release_dir" "$backend_port"
|
||||
mkdir -p "$release_dir/fake-ps-bin"
|
||||
cat > "$release_dir/fake-ps-bin/ps" <<EOF
|
||||
#!/bin/sh
|
||||
if [ "\$1" = "-ef" ]; then
|
||||
cat <<'PSOUT'
|
||||
UID PID PPID C STIME TTY TIME CMD
|
||||
root 88888 1 0 00:00 ? 00:00:00 java -Dloan.pricing.home=$release_dir -jar $release_dir/backend/ruoyi-admin.jar.bak --spring.profiles.active=pro
|
||||
PSOUT
|
||||
exit 0
|
||||
fi
|
||||
/bin/ps "\$@"
|
||||
EOF
|
||||
chmod +x "$release_dir/fake-ps-bin/ps"
|
||||
|
||||
(
|
||||
cd "$release_dir"
|
||||
PATH="$release_dir/fake-ps-bin:/usr/bin:/bin" ./deploy_from_package.sh
|
||||
)
|
||||
|
||||
backend_pid=$(cat "$release_dir/backend/backend.pid")
|
||||
kill -0 "$backend_pid" 2>/dev/null || fail "expected backend pid to be running when non-target jar process is ignored"
|
||||
|
||||
trap - EXIT INT TERM
|
||||
cleanup_release_dir "$release_dir"
|
||||
}
|
||||
|
||||
test_should_use_ps_ef_for_process_detection() {
|
||||
if rg -n 'pgrep' "$SCRIPT_UNDER_TEST" >/dev/null 2>&1; then
|
||||
fail "expected deploy_from_package.sh not to depend on pgrep"
|
||||
fi
|
||||
|
||||
if ! rg -n 'ps -ef' "$SCRIPT_UNDER_TEST" >/dev/null 2>&1; then
|
||||
fail "expected deploy_from_package.sh to use ps -ef for process detection"
|
||||
fi
|
||||
|
||||
if rg -n '\b(ss|lsof|netstat|resolve_frontend_source_dir|is_port_listening)\b' "$SCRIPT_UNDER_TEST" >/dev/null 2>&1; then
|
||||
fail "expected deploy_from_package.sh to remove port detection and unzip compatibility helpers"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
[ -f "$SCRIPT_UNDER_TEST" ] || fail "script under test not found: $SCRIPT_UNDER_TEST"
|
||||
test_should_use_ps_ef_for_process_detection
|
||||
test_deploy_success
|
||||
test_multiple_release_zip_should_fail
|
||||
test_defunct_process_should_be_ignored
|
||||
test_only_current_project_jar_should_match
|
||||
printf 'PASS: deploy_from_package tests\n'
|
||||
}
|
||||
|
||||
main "$@"
|
||||
245
bin/prod/deploy_release.sh
Executable file
245
bin/prod/deploy_release.sh
Executable file
@@ -0,0 +1,245 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
WEBAPP_ROOT="/home/webapp"
|
||||
ENV_ROOT="$WEBAPP_ROOT/env"
|
||||
APP_ROOT="$WEBAPP_ROOT/loan-pricing"
|
||||
JAVA_HOME="$ENV_ROOT/java"
|
||||
NGINX_HOME="$ENV_ROOT/nginx"
|
||||
NGINX_CONF="$NGINX_HOME/conf/nginx.conf"
|
||||
BACKEND_DIR="$APP_ROOT/backend"
|
||||
FRONTEND_DIR="$APP_ROOT/frontend"
|
||||
FRONTEND_DIST_DIR="$FRONTEND_DIR/dist"
|
||||
BACKUP_DIR="$APP_ROOT/backup"
|
||||
LOG_DIR="$APP_ROOT/logs"
|
||||
RUN_DIR="$APP_ROOT/run"
|
||||
TMP_DIR="$APP_ROOT/tmp"
|
||||
BACKEND_JAR="$BACKEND_DIR/ruoyi-admin.jar"
|
||||
FRONTEND_PORT=63311
|
||||
JAVA_RESTART_SCRIPT="$WEBAPP_ROOT/restart_java.sh"
|
||||
|
||||
timestamp() {
|
||||
date "+%Y-%m-%d %H:%M:%S"
|
||||
}
|
||||
|
||||
log_info() {
|
||||
printf '[%s] %s\n' "$(timestamp)" "$1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
printf '[%s] %s\n' "$(timestamp)" "$1" >&2
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法:
|
||||
./bin/prod/deploy_release.sh <发布压缩包路径>
|
||||
EOF
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
log_error "请使用 root 用户执行部署脚本"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_command() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
log_error "缺少命令: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_runtime_dirs() {
|
||||
mkdir -p "$BACKEND_DIR" "$FRONTEND_DIR" "$BACKUP_DIR" "$LOG_DIR" "$RUN_DIR" "$TMP_DIR"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [ -n "${WORK_DIR:-}" ] && [ -d "$WORK_DIR" ]; then
|
||||
rm -rf "$WORK_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
extract_release_package() {
|
||||
release_archive="$1"
|
||||
release_extract_dir="$2"
|
||||
|
||||
mkdir -p "$release_extract_dir"
|
||||
|
||||
case "$release_archive" in
|
||||
*.zip)
|
||||
unzip -oq "$release_archive" -d "$release_extract_dir"
|
||||
;;
|
||||
*.tar.gz|*.tgz)
|
||||
tar -xzf "$release_archive" -C "$release_extract_dir"
|
||||
;;
|
||||
*.tar)
|
||||
tar -xf "$release_archive" -C "$release_extract_dir"
|
||||
;;
|
||||
*)
|
||||
log_error "不支持的发布包格式: $release_archive"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
assert_single_file() {
|
||||
search_dir="$1"
|
||||
file_name="$2"
|
||||
description="$3"
|
||||
count=$(find "$search_dir" -type f -name "$file_name" | wc -l | tr -d ' ')
|
||||
|
||||
if [ "$count" -ne 1 ]; then
|
||||
log_error "$description 数量不正确,期望 1 个,实际 $count 个"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find "$search_dir" -type f -name "$file_name" | head -n 1
|
||||
}
|
||||
|
||||
assert_single_jar() {
|
||||
search_dir="$1"
|
||||
count=$(find "$search_dir" -type f -name '*.jar' | wc -l | tr -d ' ')
|
||||
|
||||
if [ "$count" -ne 1 ]; then
|
||||
log_error "后端 jar 数量不正确,期望 1 个,实际 $count 个"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find "$search_dir" -type f -name '*.jar' | head -n 1
|
||||
}
|
||||
|
||||
backup_current_release() {
|
||||
backup_stamp=$(date "+%Y%m%d%H%M%S")
|
||||
CURRENT_BACKUP_DIR="$BACKUP_DIR/$backup_stamp"
|
||||
|
||||
mkdir -p "$CURRENT_BACKUP_DIR/backend" "$CURRENT_BACKUP_DIR/frontend"
|
||||
|
||||
if [ -f "$BACKEND_JAR" ]; then
|
||||
cp -a "$BACKEND_JAR" "$CURRENT_BACKUP_DIR/backend/"
|
||||
fi
|
||||
|
||||
if [ -d "$FRONTEND_DIST_DIR" ]; then
|
||||
cp -a "$FRONTEND_DIST_DIR" "$CURRENT_BACKUP_DIR/frontend/"
|
||||
fi
|
||||
|
||||
log_info "旧版本已备份到: $CURRENT_BACKUP_DIR"
|
||||
}
|
||||
|
||||
deploy_backend() {
|
||||
source_jar="$1"
|
||||
|
||||
rm -f "$BACKEND_DIR"/*.jar
|
||||
cp "$source_jar" "$BACKEND_JAR"
|
||||
}
|
||||
|
||||
resolve_frontend_source_dir() {
|
||||
unzip_dir="$1"
|
||||
|
||||
if [ -f "$unzip_dir/index.html" ]; then
|
||||
printf '%s\n' "$unzip_dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f "$unzip_dir/dist/index.html" ]; then
|
||||
printf '%s\n' "$unzip_dir/dist"
|
||||
return 0
|
||||
fi
|
||||
|
||||
candidate=$(find "$unzip_dir" -type f -name 'index.html' | head -n 1)
|
||||
if [ -z "${candidate:-}" ]; then
|
||||
log_error "dist.zip 解压后未找到 index.html"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dirname "$candidate"
|
||||
}
|
||||
|
||||
deploy_frontend() {
|
||||
dist_zip="$1"
|
||||
dist_unpack_dir="$WORK_DIR/frontend"
|
||||
|
||||
mkdir -p "$dist_unpack_dir"
|
||||
unzip -oq "$dist_zip" -d "$dist_unpack_dir"
|
||||
|
||||
frontend_source_dir=$(resolve_frontend_source_dir "$dist_unpack_dir")
|
||||
rm -rf "$FRONTEND_DIST_DIR"
|
||||
mkdir -p "$FRONTEND_DIST_DIR"
|
||||
cp -a "$frontend_source_dir"/. "$FRONTEND_DIST_DIR"/
|
||||
}
|
||||
|
||||
reload_nginx() {
|
||||
nginx_pid_file="$RUN_DIR/nginx.pid"
|
||||
|
||||
"$NGINX_HOME/sbin/nginx" -t -c "$NGINX_CONF"
|
||||
|
||||
if [ -f "$nginx_pid_file" ]; then
|
||||
nginx_pid=$(cat "$nginx_pid_file" 2>/dev/null || true)
|
||||
if [ -n "${nginx_pid:-}" ] && kill -0 "$nginx_pid" 2>/dev/null; then
|
||||
"$NGINX_HOME/sbin/nginx" -c "$NGINX_CONF" -s reload
|
||||
log_info "Nginx 已重载,前端端口: $FRONTEND_PORT"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
"$NGINX_HOME/sbin/nginx" -c "$NGINX_CONF"
|
||||
log_info "Nginx 已启动,前端端口: $FRONTEND_PORT"
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_root
|
||||
require_command tar
|
||||
require_command unzip
|
||||
require_command find
|
||||
|
||||
release_archive="$1"
|
||||
if [ ! -f "$release_archive" ]; then
|
||||
log_error "发布压缩包不存在: $release_archive"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVA_HOME/bin/java" ]; then
|
||||
log_error "未检测到 Java,请先执行 ./bin/prod/install_env.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$NGINX_HOME/sbin/nginx" ]; then
|
||||
log_error "未检测到 Nginx,请先执行 ./bin/prod/install_env.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVA_RESTART_SCRIPT" ]; then
|
||||
log_error "未检测到 Java 重启脚本: $JAVA_RESTART_SCRIPT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_runtime_dirs
|
||||
WORK_DIR=$(mktemp -d "$TMP_DIR/release.XXXXXX")
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
extract_release_package "$release_archive" "$WORK_DIR/package"
|
||||
|
||||
backend_jar_source=$(assert_single_jar "$WORK_DIR/package")
|
||||
frontend_dist_source=$(assert_single_file "$WORK_DIR/package" 'dist.zip' '前端 dist.zip')
|
||||
|
||||
backup_current_release
|
||||
"$JAVA_RESTART_SCRIPT" stop
|
||||
deploy_backend "$backend_jar_source"
|
||||
deploy_frontend "$frontend_dist_source"
|
||||
"$JAVA_RESTART_SCRIPT" start
|
||||
reload_nginx
|
||||
|
||||
log_info "部署完成"
|
||||
log_info "后端 jar: $BACKEND_JAR"
|
||||
log_info "前端目录: $FRONTEND_DIST_DIR"
|
||||
log_info "备份目录: $CURRENT_BACKUP_DIR"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
244
bin/prod/install_env.sh
Executable file
244
bin/prod/install_env.sh
Executable file
@@ -0,0 +1,244 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
WEBAPP_ROOT="/home/webapp"
|
||||
ENV_ROOT="$WEBAPP_ROOT/env"
|
||||
APP_ROOT="$WEBAPP_ROOT/loan-pricing"
|
||||
JAVA_HOME="$ENV_ROOT/java"
|
||||
NGINX_HOME="$ENV_ROOT/nginx"
|
||||
NGINX_CONF="$NGINX_HOME/conf/nginx.conf"
|
||||
BACKEND_DIR="$APP_ROOT/backend"
|
||||
FRONTEND_DIR="$APP_ROOT/frontend"
|
||||
BACKUP_DIR="$APP_ROOT/backup"
|
||||
LOG_DIR="$APP_ROOT/logs"
|
||||
RUN_DIR="$APP_ROOT/run"
|
||||
TMP_DIR="$APP_ROOT/tmp"
|
||||
BACKEND_PORT=63310
|
||||
FRONTEND_PORT=63311
|
||||
|
||||
timestamp() {
|
||||
date "+%Y-%m-%d %H:%M:%S"
|
||||
}
|
||||
|
||||
log_info() {
|
||||
printf '[%s] %s\n' "$(timestamp)" "$1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
printf '[%s] %s\n' "$(timestamp)" "$1" >&2
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
log_error "请使用 root 用户执行安装脚本"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_command() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
log_error "缺少命令: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_base_dirs() {
|
||||
mkdir -p "$ENV_ROOT" "$BACKEND_DIR" "$FRONTEND_DIR" "$BACKUP_DIR" "$LOG_DIR" "$RUN_DIR" "$TMP_DIR"
|
||||
}
|
||||
|
||||
find_archive() {
|
||||
search_kind="$1"
|
||||
found=""
|
||||
|
||||
case "$search_kind" in
|
||||
java)
|
||||
set -- "$WEBAPP_ROOT/openjdk" "$WEBAPP_ROOT"
|
||||
patterns='openjdk*.tar.gz openjdk*.tgz jdk*.tar.gz jdk*.tgz'
|
||||
;;
|
||||
nginx)
|
||||
set -- "$WEBAPP_ROOT/nginx" "$ENV_ROOT" "$WEBAPP_ROOT"
|
||||
patterns='nginx-*.tar.gz nginx-*.tgz'
|
||||
;;
|
||||
*)
|
||||
log_error "未知的安装包类型: $search_kind"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
for dir in "$@"; do
|
||||
if [ ! -d "$dir" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
for pattern in $patterns; do
|
||||
candidate=$(find "$dir" -maxdepth 1 -type f -name "$pattern" | sort | head -n 1)
|
||||
if [ -n "${candidate:-}" ]; then
|
||||
found="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "${found:-}" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "${found:-}" ]; then
|
||||
log_error "未找到 $search_kind 安装包"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$found"
|
||||
}
|
||||
|
||||
install_yum_dependencies() {
|
||||
require_command yum
|
||||
|
||||
log_info "安装系统依赖"
|
||||
yum install -y \
|
||||
gcc \
|
||||
make \
|
||||
pcre \
|
||||
pcre-devel \
|
||||
zlib \
|
||||
zlib-devel \
|
||||
openssl \
|
||||
openssl-devel \
|
||||
tar \
|
||||
gzip \
|
||||
unzip \
|
||||
which \
|
||||
findutils \
|
||||
procps-ng \
|
||||
iproute
|
||||
}
|
||||
|
||||
install_java() {
|
||||
java_archive="$1"
|
||||
|
||||
log_info "安装 Java: $java_archive"
|
||||
rm -rf "$JAVA_HOME"
|
||||
mkdir -p "$JAVA_HOME"
|
||||
tar -xzf "$java_archive" -C "$JAVA_HOME" --strip-components=1
|
||||
|
||||
if [ ! -x "$JAVA_HOME/bin/java" ]; then
|
||||
log_error "Java 安装失败,未找到 $JAVA_HOME/bin/java"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"$JAVA_HOME/bin/java" -version >/dev/null 2>&1
|
||||
}
|
||||
|
||||
install_nginx() {
|
||||
nginx_archive="$1"
|
||||
build_dir=$(mktemp -d "$ENV_ROOT/nginx-build.XXXXXX")
|
||||
|
||||
log_info "编译安装 Nginx: $nginx_archive"
|
||||
rm -rf "$NGINX_HOME"
|
||||
mkdir -p "$NGINX_HOME"
|
||||
|
||||
tar -xzf "$nginx_archive" -C "$build_dir"
|
||||
source_dir=$(find "$build_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)
|
||||
if [ -z "${source_dir:-}" ]; then
|
||||
rm -rf "$build_dir"
|
||||
log_error "Nginx 源码目录解析失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jobs=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
||||
(
|
||||
cd "$source_dir"
|
||||
./configure --prefix="$NGINX_HOME" --with-http_ssl_module
|
||||
make -j"$jobs"
|
||||
make install
|
||||
)
|
||||
|
||||
rm -rf "$build_dir"
|
||||
|
||||
if [ ! -x "$NGINX_HOME/sbin/nginx" ]; then
|
||||
log_error "Nginx 安装失败,未找到 $NGINX_HOME/sbin/nginx"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
write_nginx_conf() {
|
||||
log_info "写入 Nginx 配置: $NGINX_CONF"
|
||||
|
||||
mkdir -p "$NGINX_HOME/conf" "$NGINX_HOME/logs" "$FRONTEND_DIR/dist"
|
||||
|
||||
cat > "$NGINX_CONF" <<EOF
|
||||
user nobody;
|
||||
worker_processes 1;
|
||||
|
||||
error_log $LOG_DIR/nginx-error.log warn;
|
||||
pid $RUN_DIR/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include $NGINX_HOME/conf/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
|
||||
'\$status \$body_bytes_sent "\$http_referer" '
|
||||
'"\$http_user_agent" "\$http_x_forwarded_for"';
|
||||
|
||||
access_log $LOG_DIR/nginx-access.log main;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
client_max_body_size 100m;
|
||||
|
||||
server {
|
||||
listen $FRONTEND_PORT;
|
||||
server_name _;
|
||||
|
||||
root $FRONTEND_DIR/dist;
|
||||
index index.html;
|
||||
|
||||
location /prod-api/ {
|
||||
proxy_pass http://127.0.0.1:$BACKEND_PORT/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
"$NGINX_HOME/sbin/nginx" -t -c "$NGINX_CONF"
|
||||
}
|
||||
|
||||
main() {
|
||||
require_root
|
||||
require_command tar
|
||||
require_command find
|
||||
ensure_base_dirs
|
||||
install_yum_dependencies
|
||||
|
||||
java_archive=$(find_archive java)
|
||||
nginx_archive=$(find_archive nginx)
|
||||
|
||||
log_info "检测到 Java 安装包: $java_archive"
|
||||
log_info "检测到 Nginx 安装包: $nginx_archive"
|
||||
|
||||
install_java "$java_archive"
|
||||
install_nginx "$nginx_archive"
|
||||
write_nginx_conf
|
||||
|
||||
log_info "环境安装完成"
|
||||
log_info "JAVA_HOME=$JAVA_HOME"
|
||||
log_info "NGINX_HOME=$NGINX_HOME"
|
||||
log_info "前端端口=$FRONTEND_PORT,后端端口=$BACKEND_PORT"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
208
bin/prod/restart_java.sh
Executable file
208
bin/prod/restart_java.sh
Executable file
@@ -0,0 +1,208 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
WEBAPP_ROOT="/home/webapp"
|
||||
ENV_ROOT="$WEBAPP_ROOT/env"
|
||||
APP_ROOT="$WEBAPP_ROOT/loan-pricing"
|
||||
JAVA_HOME="$ENV_ROOT/jdk"
|
||||
BACKEND_DIR="$APP_ROOT/backend"
|
||||
LOG_DIR="$APP_ROOT/logs"
|
||||
RUN_DIR="$APP_ROOT/run"
|
||||
BACKEND_PID_FILE="$RUN_DIR/backend.pid"
|
||||
BACKEND_JAR="$BACKEND_DIR/ruoyi-admin.jar"
|
||||
BACKEND_CONSOLE_LOG="$LOG_DIR/backend-console.log"
|
||||
BACKEND_PORT=63310
|
||||
BACKEND_MARKER="-Dloan.pricing.home=$APP_ROOT"
|
||||
JAVA_OPTS="$BACKEND_MARKER -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"
|
||||
|
||||
timestamp() {
|
||||
date "+%Y-%m-%d %H:%M:%S"
|
||||
}
|
||||
|
||||
log_info() {
|
||||
printf '[%s] %s\n' "$(timestamp)" "$1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
printf '[%s] %s\n' "$(timestamp)" "$1" >&2
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法: ./restart_java.sh [start|stop|restart|status]
|
||||
|
||||
默认动作:
|
||||
restart 重启后端 Java 进程
|
||||
EOF
|
||||
}
|
||||
|
||||
ensure_runtime_dirs() {
|
||||
mkdir -p "$BACKEND_DIR" "$LOG_DIR" "$RUN_DIR"
|
||||
}
|
||||
|
||||
is_managed_backend_pid() {
|
||||
pid="$1"
|
||||
if [ -z "${pid:-}" ] || ! kill -0 "$pid" 2>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
args=$(ps -o args= -p "$pid" 2>/dev/null || true)
|
||||
if [ -z "${args:-}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$args" in
|
||||
*"$BACKEND_MARKER"*"$BACKEND_JAR"*|*"$BACKEND_JAR"*"$BACKEND_MARKER"*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
collect_backend_pids() {
|
||||
pids=""
|
||||
|
||||
if [ -f "$BACKEND_PID_FILE" ]; then
|
||||
file_pid=$(cat "$BACKEND_PID_FILE" 2>/dev/null || true)
|
||||
if [ -n "${file_pid:-}" ] && is_managed_backend_pid "$file_pid"; then
|
||||
pids="$pids $file_pid"
|
||||
fi
|
||||
fi
|
||||
|
||||
marker_pids=$(ps -ef | awk -v marker="$BACKEND_MARKER" -v jar="$BACKEND_JAR" '
|
||||
index($0, "<defunct>") == 0 && index($0, marker) > 0 {
|
||||
for (i = 1; i < NF; i++) {
|
||||
if ($i == "-jar" && $(i + 1) == jar) {
|
||||
print $2
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
' | xargs 2>/dev/null || true)
|
||||
if [ -n "${marker_pids:-}" ]; then
|
||||
for pid in $marker_pids; do
|
||||
if is_managed_backend_pid "$pid"; then
|
||||
pids="$pids $pid"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
printf '%s\n' "$(echo "$pids" | xargs 2>/dev/null || true)"
|
||||
}
|
||||
|
||||
stop_backend() {
|
||||
pids=$(collect_backend_pids)
|
||||
|
||||
if [ -z "${pids:-}" ]; then
|
||||
rm -f "$BACKEND_PID_FILE"
|
||||
log_info "未发现运行中的后端进程"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "停止后端进程: $pids"
|
||||
for pid in $pids; do
|
||||
kill -TERM "$pid" 2>/dev/null || true
|
||||
done
|
||||
|
||||
elapsed=0
|
||||
while [ "$elapsed" -lt 30 ]; do
|
||||
remaining=""
|
||||
for pid in $pids; do
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
remaining="$remaining $pid"
|
||||
fi
|
||||
done
|
||||
|
||||
remaining=$(echo "$remaining" | xargs 2>/dev/null || true)
|
||||
if [ -z "${remaining:-}" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
elapsed=$((elapsed + 1))
|
||||
done
|
||||
|
||||
if [ -n "${remaining:-}" ]; then
|
||||
log_info "执行强制停止: $remaining"
|
||||
for pid in $remaining; do
|
||||
kill -KILL "$pid" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
rm -f "$BACKEND_PID_FILE"
|
||||
}
|
||||
|
||||
start_backend() {
|
||||
ensure_runtime_dirs
|
||||
|
||||
if [ ! -x "$JAVA_HOME/bin/java" ]; then
|
||||
log_error "未检测到可执行 Java: $JAVA_HOME/bin/java"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$BACKEND_JAR" ]; then
|
||||
log_error "未找到后端 jar: $BACKEND_JAR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$(collect_backend_pids)" ]; then
|
||||
log_error "检测到后端已在运行,请先执行 stop 或 restart"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '\n===== %s restart =====\n' "$(timestamp)" >> "$BACKEND_CONSOLE_LOG"
|
||||
|
||||
(
|
||||
cd "$BACKEND_DIR"
|
||||
nohup "$JAVA_HOME/bin/java" $JAVA_OPTS -jar "$BACKEND_JAR" --spring.profiles.active=pro --server.port="$BACKEND_PORT" >> "$BACKEND_CONSOLE_LOG" 2>&1 &
|
||||
echo $! > "$BACKEND_PID_FILE"
|
||||
)
|
||||
|
||||
sleep 3
|
||||
|
||||
backend_pid=$(cat "$BACKEND_PID_FILE" 2>/dev/null || true)
|
||||
if [ -z "${backend_pid:-}" ] || ! kill -0 "$backend_pid" 2>/dev/null; then
|
||||
log_error "后端启动失败,请检查日志: $BACKEND_CONSOLE_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "后端已启动,PID: $backend_pid"
|
||||
}
|
||||
|
||||
status_backend() {
|
||||
pids=$(collect_backend_pids)
|
||||
if [ -n "${pids:-}" ]; then
|
||||
log_info "后端正在运行,进程: $pids"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "后端未运行"
|
||||
}
|
||||
|
||||
main() {
|
||||
action="${1:-restart}"
|
||||
|
||||
case "$action" in
|
||||
start)
|
||||
start_backend
|
||||
;;
|
||||
stop)
|
||||
stop_backend
|
||||
;;
|
||||
restart)
|
||||
stop_backend
|
||||
start_backend
|
||||
;;
|
||||
status)
|
||||
status_backend
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
116
bin/prod/restart_java_test.sh
Normal file
116
bin/prod/restart_java_test.sh
Normal file
@@ -0,0 +1,116 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT_DIR=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
|
||||
SCRIPT_UNDER_TEST="$ROOT_DIR/bin/prod/restart_java.sh"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_grep() {
|
||||
pattern="$1"
|
||||
target="$2"
|
||||
if ! grep -Eq -- "$pattern" "$target"; then
|
||||
fail "expected pattern [$pattern] in $target"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_grep() {
|
||||
pattern="$1"
|
||||
target="$2"
|
||||
if grep -Eq -- "$pattern" "$target"; then
|
||||
fail "did not expect pattern [$pattern] in $target"
|
||||
fi
|
||||
}
|
||||
|
||||
create_fake_java() {
|
||||
fake_java="$1"
|
||||
|
||||
cat > "$fake_java" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
while :; do
|
||||
sleep 1
|
||||
done
|
||||
EOF
|
||||
|
||||
chmod +x "$fake_java"
|
||||
}
|
||||
|
||||
prepare_script_env() {
|
||||
work_dir="$1"
|
||||
|
||||
mkdir -p "$work_dir/env/jdk/bin" "$work_dir/loan-pricing/backend" "$work_dir/loan-pricing/logs" "$work_dir/loan-pricing/run"
|
||||
create_fake_java "$work_dir/env/jdk/bin/java"
|
||||
printf 'fake-jar\n' > "$work_dir/loan-pricing/backend/ruoyi-admin.jar"
|
||||
cp "$SCRIPT_UNDER_TEST" "$work_dir/restart_java.sh"
|
||||
perl -0pi -e "s#WEBAPP_ROOT=\"/home/webapp\"#WEBAPP_ROOT=\"$work_dir\"#" "$work_dir/restart_java.sh"
|
||||
chmod +x "$work_dir/restart_java.sh"
|
||||
}
|
||||
|
||||
cleanup_work_dir() {
|
||||
work_dir="$1"
|
||||
|
||||
if [ -f "$work_dir/loan-pricing/run/backend.pid" ]; then
|
||||
backend_pid=$(cat "$work_dir/loan-pricing/run/backend.pid" 2>/dev/null || true)
|
||||
if [ -n "${backend_pid:-}" ]; then
|
||||
kill "$backend_pid" 2>/dev/null || true
|
||||
wait "$backend_pid" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "$work_dir"
|
||||
}
|
||||
|
||||
test_script_contract() {
|
||||
assert_grep 'JAVA_HOME="\$ENV_ROOT/jdk"' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep '--spring\.profiles\.active=pro' "$SCRIPT_UNDER_TEST"
|
||||
assert_grep 'ps -ef' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep 'pgrep' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep 'mvn' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep 'require_root' "$SCRIPT_UNDER_TEST"
|
||||
assert_not_grep '\b(ss|lsof|netstat)\b' "$SCRIPT_UNDER_TEST"
|
||||
}
|
||||
|
||||
test_restart_flow() {
|
||||
work_dir=$(mktemp -d)
|
||||
trap 'cleanup_work_dir "$work_dir"' EXIT INT TERM
|
||||
|
||||
prepare_script_env "$work_dir"
|
||||
|
||||
"$work_dir/restart_java.sh" start
|
||||
if [ ! -f "$work_dir/loan-pricing/run/backend.pid" ]; then
|
||||
fail "expected backend pid file after start"
|
||||
fi
|
||||
|
||||
backend_pid=$(cat "$work_dir/loan-pricing/run/backend.pid")
|
||||
kill -0 "$backend_pid" 2>/dev/null || fail "expected backend process to be running after start"
|
||||
|
||||
status_output=$("$work_dir/restart_java.sh" status 2>&1 || true)
|
||||
printf '%s\n' "$status_output" | grep -q '后端正在运行' || fail "expected status output to show running"
|
||||
|
||||
"$work_dir/restart_java.sh" restart
|
||||
restarted_pid=$(cat "$work_dir/loan-pricing/run/backend.pid")
|
||||
kill -0 "$restarted_pid" 2>/dev/null || fail "expected backend process to be running after restart"
|
||||
|
||||
"$work_dir/restart_java.sh" stop
|
||||
if [ -f "$work_dir/loan-pricing/run/backend.pid" ]; then
|
||||
fail "expected backend pid file to be removed after stop"
|
||||
fi
|
||||
|
||||
trap - EXIT INT TERM
|
||||
cleanup_work_dir "$work_dir"
|
||||
}
|
||||
|
||||
main() {
|
||||
[ -f "$SCRIPT_UNDER_TEST" ] || fail "script under test not found: $SCRIPT_UNDER_TEST"
|
||||
test_script_contract
|
||||
test_restart_flow
|
||||
printf 'PASS: restart_java tests\n'
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user