新增生产一键部署脚本
This commit is contained in:
352
bin/prod/deploy_from_package.sh
Executable file
352
bin/prod/deploy_from_package.sh
Executable file
@@ -0,0 +1,352 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
JAVA_BIN="/home/webapp/env/java/bin/java"
|
||||
BACKEND_PORT=63310
|
||||
SPRING_PROFILE="pro"
|
||||
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
|
||||
}
|
||||
|
||||
require_port_command() {
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v lsof >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_error "缺少端口检测命令: ss 或 lsof"
|
||||
exit 1
|
||||
}
|
||||
|
||||
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' | 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
|
||||
}
|
||||
|
||||
assert_single_dist_zip() {
|
||||
search_dir="$1"
|
||||
count=$(find "$search_dir" -type f -name 'dist.zip' | 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' | 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_archive() {
|
||||
source_dist_zip="$1"
|
||||
rm -f "$FRONTEND_DIST_ARCHIVE"
|
||||
mv "$source_dist_zip" "$FRONTEND_DIST_ARCHIVE"
|
||||
}
|
||||
|
||||
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() {
|
||||
dist_unpack_dir="$WORK_DIR/frontend"
|
||||
|
||||
mkdir -p "$dist_unpack_dir"
|
||||
unzip -oq "$FRONTEND_DIST_ARCHIVE" -d "$dist_unpack_dir"
|
||||
|
||||
rm -rf "$FRONTEND_DIST_DIR"
|
||||
mkdir -p "$FRONTEND_DIST_DIR"
|
||||
cp -a "$(resolve_frontend_source_dir "$dist_unpack_dir")"/. "$FRONTEND_DIST_DIR"/
|
||||
}
|
||||
|
||||
is_port_listening() {
|
||||
port="$1"
|
||||
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -lnt 2>/dev/null | grep -q ":$port "
|
||||
return $?
|
||||
fi
|
||||
|
||||
lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1
|
||||
}
|
||||
|
||||
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_TARGET"*|*"$BACKEND_JAR_TARGET"*"$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=$(pgrep -f "$BACKEND_MARKER" 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
|
||||
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
|
||||
|
||||
wait_seconds=0
|
||||
while [ "$wait_seconds" -lt 30 ]; do
|
||||
if is_port_listening "$BACKEND_PORT"; then
|
||||
log_info "后端已监听端口: $BACKEND_PORT"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
wait_seconds=$((wait_seconds + 1))
|
||||
done
|
||||
|
||||
log_error "后端未在预期时间内监听端口 $BACKEND_PORT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
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 pgrep
|
||||
require_command ps
|
||||
require_command nohup
|
||||
require_port_command
|
||||
|
||||
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_archive "$frontend_dist_source"
|
||||
deploy_frontend_dist
|
||||
start_backend
|
||||
|
||||
log_info "部署完成"
|
||||
log_info "后端 jar: $BACKEND_JAR_TARGET"
|
||||
log_info "前端目录: $FRONTEND_DIST_DIR"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user