新增springboot应用自动部署脚本
This commit is contained in:
396
deploy/springboot/deploy.sh
Normal file
396
deploy/springboot/deploy.sh
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Spring Boot 应用部署脚本
|
||||||
|
#
|
||||||
|
# 主要功能:
|
||||||
|
# 1. 初始化部署变量(JDK 路径、运行目录、日志目录、外置配置目录、JVM 参数等)
|
||||||
|
# 2. 自动创建部署相关目录(如不存在)
|
||||||
|
# 3. 基于 Jar 名称进行进程检查(支持指定 Jar 或自动发现 Jar)
|
||||||
|
# 4. 停止同名应用进程(优先 kill -15,超时后 kill -9)
|
||||||
|
# 5. 使用 nohup 启动应用并进行启动结果校验
|
||||||
|
#
|
||||||
|
# 使用说明:
|
||||||
|
# 1. 首次使用请根据实际环境修改“变量初始化区”
|
||||||
|
# 2. 脚本支持通过命令行覆盖部分变量,示例:
|
||||||
|
# ./deploy.sh --jar-name app.jar --profile prod --debug true
|
||||||
|
# 3. 日志输出为中文,便于定位部署阶段和问题
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# 一、变量初始化区(请按需修改)
|
||||||
|
############################################
|
||||||
|
|
||||||
|
# JDK 安装目录(必须能找到 bin/java)
|
||||||
|
JDK_HOME="/usr/local/jdk"
|
||||||
|
|
||||||
|
# Spring Boot Jar 运行目录(放置 jar 包的位置)
|
||||||
|
APP_HOME="/opt/apps/springboot"
|
||||||
|
|
||||||
|
# 应用日志输出目录(脚本会自动创建)
|
||||||
|
LOG_DIR="/opt/logs/springboot"
|
||||||
|
|
||||||
|
# 外置 Spring Boot 配置文件目录(脚本会自动创建)
|
||||||
|
# 建议在该目录中放置 application-<profile>.yml 或 application.yml
|
||||||
|
CONFIG_DIR="/opt/config/springboot"
|
||||||
|
|
||||||
|
# 指定 Jar 包名称(例如 demo.jar)
|
||||||
|
# 若留空则自动从 APP_HOME 中寻找第一个 *.jar 作为候选
|
||||||
|
JAR_NAME=""
|
||||||
|
|
||||||
|
# JVM 参数(可按机器资源调整)
|
||||||
|
JVM_OPTS="-Xms512m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR}/heapdump.hprof -Dfile.encoding=UTF-8"
|
||||||
|
|
||||||
|
# Spring Boot Profile(dev/test/prod 等)
|
||||||
|
SPRING_PROFILE="prod"
|
||||||
|
|
||||||
|
# Java 启动附加参数(例如时区等系统属性)
|
||||||
|
JAVA_SYS_PROPS="-Duser.timezone=Asia/Shanghai"
|
||||||
|
|
||||||
|
# Debug 开关:true/false
|
||||||
|
DEBUG_ENABLED="false"
|
||||||
|
|
||||||
|
# Debug 端口和监听地址(仅在 DEBUG_ENABLED=true 时生效)
|
||||||
|
DEBUG_PORT="5005"
|
||||||
|
DEBUG_SUSPEND="n" # y: 启动时等待调试器连接;n: 不等待
|
||||||
|
DEBUG_ADDRESS="0.0.0.0"
|
||||||
|
|
||||||
|
# 停止进程等待超时时间(秒)
|
||||||
|
STOP_TIMEOUT="30"
|
||||||
|
|
||||||
|
# 启动后健康检查等待时间(秒)
|
||||||
|
START_WAIT="8"
|
||||||
|
|
||||||
|
# 启动日志文件
|
||||||
|
CONSOLE_LOG_FILE="${LOG_DIR}/console.out"
|
||||||
|
|
||||||
|
# 运行用户(用于日志提示,不强制切换用户)
|
||||||
|
RUN_USER="$(whoami)"
|
||||||
|
|
||||||
|
# 额外 Spring 参数(可按需增加,例如 --server.port=8080)
|
||||||
|
SPRING_EXTRA_ARGS=""
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# 二、内部变量与通用函数
|
||||||
|
############################################
|
||||||
|
|
||||||
|
SCRIPT_NAME="$(basename "$0")"
|
||||||
|
CURRENT_TIME="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
|
||||||
|
# 彩色输出(若终端不支持可改为空)
|
||||||
|
COLOR_RESET="\033[0m"
|
||||||
|
COLOR_INFO="\033[1;34m"
|
||||||
|
COLOR_WARN="\033[1;33m"
|
||||||
|
COLOR_ERROR="\033[1;31m"
|
||||||
|
COLOR_SUCCESS="\033[1;32m"
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${COLOR_INFO}[信息][$(date '+%Y-%m-%d %H:%M:%S')]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${COLOR_WARN}[警告][$(date '+%Y-%m-%d %H:%M:%S')]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${COLOR_ERROR}[错误][$(date '+%Y-%m-%d %H:%M:%S')]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${COLOR_SUCCESS}[成功][$(date '+%Y-%m-%d %H:%M:%S')]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_header() {
|
||||||
|
echo "============================================================"
|
||||||
|
echo "脚本名称 : ${SCRIPT_NAME}"
|
||||||
|
echo "执行时间 : ${CURRENT_TIME}"
|
||||||
|
echo "执行用户 : ${RUN_USER}"
|
||||||
|
echo "============================================================"
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
用法:
|
||||||
|
${SCRIPT_NAME} [可选参数]
|
||||||
|
|
||||||
|
可选参数:
|
||||||
|
--jdk-home <path> 指定 JDK_HOME
|
||||||
|
--app-home <path> 指定 APP_HOME
|
||||||
|
--log-dir <path> 指定 LOG_DIR
|
||||||
|
--config-dir <path> 指定 CONFIG_DIR
|
||||||
|
--jar-name <name> 指定 Jar 包名称(如 demo.jar)
|
||||||
|
--profile <name> 指定 Spring Profile(如 dev/test/prod)
|
||||||
|
--debug <true|false> 是否开启远程调试
|
||||||
|
--debug-port <port> 调试端口(默认 5005)
|
||||||
|
--stop-timeout <sec> 停止超时时间(秒)
|
||||||
|
--start-wait <sec> 启动后等待检查时间(秒)
|
||||||
|
--help 显示帮助
|
||||||
|
|
||||||
|
示例:
|
||||||
|
${SCRIPT_NAME} --jar-name demo.jar --profile prod --debug false
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# 参数解析:允许在不改脚本文件的情况下按需覆盖变量
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--jdk-home)
|
||||||
|
JDK_HOME="$2"; shift 2 ;;
|
||||||
|
--app-home)
|
||||||
|
APP_HOME="$2"; shift 2 ;;
|
||||||
|
--log-dir)
|
||||||
|
LOG_DIR="$2"; shift 2 ;;
|
||||||
|
--config-dir)
|
||||||
|
CONFIG_DIR="$2"; shift 2 ;;
|
||||||
|
--jar-name)
|
||||||
|
JAR_NAME="$2"; shift 2 ;;
|
||||||
|
--profile)
|
||||||
|
SPRING_PROFILE="$2"; shift 2 ;;
|
||||||
|
--debug)
|
||||||
|
DEBUG_ENABLED="$2"; shift 2 ;;
|
||||||
|
--debug-port)
|
||||||
|
DEBUG_PORT="$2"; shift 2 ;;
|
||||||
|
--stop-timeout)
|
||||||
|
STOP_TIMEOUT="$2"; shift 2 ;;
|
||||||
|
--start-wait)
|
||||||
|
START_WAIT="$2"; shift 2 ;;
|
||||||
|
--help)
|
||||||
|
usage; exit 0 ;;
|
||||||
|
*)
|
||||||
|
log_error "未知参数:$1"
|
||||||
|
usage
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 基础前置校验,避免关键路径或命令不存在导致后续异常
|
||||||
|
pre_check() {
|
||||||
|
log_info "开始执行部署前检查..."
|
||||||
|
|
||||||
|
if [[ ! -d "${JDK_HOME}" ]]; then
|
||||||
|
log_error "JDK_HOME 不存在:${JDK_HOME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -x "${JDK_HOME}/bin/java" ]]; then
|
||||||
|
log_error "未找到可执行 Java:${JDK_HOME}/bin/java"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# APP_HOME 不强制提前存在,后续会自动创建
|
||||||
|
if ! command -v nohup >/dev/null 2>&1; then
|
||||||
|
log_error "系统未找到 nohup 命令,请先安装 coreutils 或确认环境。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "部署前检查通过。"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建目录:若不存在则创建,存在则提示复用
|
||||||
|
ensure_dirs() {
|
||||||
|
log_info "开始检查并创建部署目录..."
|
||||||
|
|
||||||
|
for d in "${APP_HOME}" "${LOG_DIR}" "${CONFIG_DIR}"; do
|
||||||
|
if [[ ! -d "$d" ]]; then
|
||||||
|
mkdir -p "$d"
|
||||||
|
log_info "目录不存在,已创建:$d"
|
||||||
|
else
|
||||||
|
log_info "目录已存在,跳过创建:$d"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_success "目录检查/创建完成。"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 自动发现 Jar:当未手工指定 JAR_NAME 时,从 APP_HOME 取第一个 jar
|
||||||
|
auto_select_jar() {
|
||||||
|
if [[ -n "${JAR_NAME}" ]]; then
|
||||||
|
log_info "已指定 Jar 包名称:${JAR_NAME}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "未指定 Jar 包名称,开始从运行目录自动发现:${APP_HOME}"
|
||||||
|
|
||||||
|
# 使用 find 获取第一个 jar,排序后取第一项可提升稳定性
|
||||||
|
local found
|
||||||
|
found="$(find "${APP_HOME}" -maxdepth 1 -type f -name '*.jar' | sort | head -n 1)"
|
||||||
|
|
||||||
|
if [[ -z "${found}" ]]; then
|
||||||
|
log_error "在 ${APP_HOME} 未发现任何 jar 包,请先上传应用 Jar。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
JAR_NAME="$(basename "${found}")"
|
||||||
|
log_success "自动选定 Jar 包:${JAR_NAME}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 返回匹配 Jar 名称的进程 PID(可能多个)
|
||||||
|
get_pids_by_jar() {
|
||||||
|
local jar="$1"
|
||||||
|
|
||||||
|
# 过滤掉 grep 本身,匹配 java 命令行中包含目标 jar 的进程
|
||||||
|
ps -ef | grep java | grep "${jar}" | grep -v grep | awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 优雅停止应用:优先 kill -15,超时后 kill -9
|
||||||
|
stop_app_if_running() {
|
||||||
|
log_info "开始检查同名进程(基于 Jar 名称):${JAR_NAME}"
|
||||||
|
|
||||||
|
local pids
|
||||||
|
pids="$(get_pids_by_jar "${JAR_NAME}" || true)"
|
||||||
|
|
||||||
|
if [[ -z "${pids}" ]]; then
|
||||||
|
log_info "未发现运行中的同名进程,无需停止。"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_warn "发现运行中进程 PID:${pids}"
|
||||||
|
log_info "优先执行 kill -15 进行优雅停止..."
|
||||||
|
|
||||||
|
for pid in ${pids}; do
|
||||||
|
kill -15 "${pid}" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
local waited=0
|
||||||
|
while [[ ${waited} -lt ${STOP_TIMEOUT} ]]; do
|
||||||
|
sleep 1
|
||||||
|
waited=$((waited + 1))
|
||||||
|
|
||||||
|
local remaining
|
||||||
|
remaining="$(get_pids_by_jar "${JAR_NAME}" || true)"
|
||||||
|
if [[ -z "${remaining}" ]]; then
|
||||||
|
log_success "应用已在 ${waited} 秒内优雅停止。"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_warn "等待 ${STOP_TIMEOUT} 秒后进程仍存在,执行 kill -9 强制停止..."
|
||||||
|
|
||||||
|
local force_pids
|
||||||
|
force_pids="$(get_pids_by_jar "${JAR_NAME}" || true)"
|
||||||
|
for pid in ${force_pids}; do
|
||||||
|
kill -9 "${pid}" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
local final_check
|
||||||
|
final_check="$(get_pids_by_jar "${JAR_NAME}" || true)"
|
||||||
|
if [[ -n "${final_check}" ]]; then
|
||||||
|
log_error "强制停止后仍检测到进程:${final_check},请人工排查。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "应用已强制停止。"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 组装 debug 参数
|
||||||
|
build_debug_opts() {
|
||||||
|
if [[ "${DEBUG_ENABLED}" == "true" ]]; then
|
||||||
|
echo "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${DEBUG_SUSPEND},address=${DEBUG_ADDRESS}:${DEBUG_PORT}"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 启动应用并校验进程是否存在
|
||||||
|
start_app() {
|
||||||
|
local jar_path="${APP_HOME}/${JAR_NAME}"
|
||||||
|
|
||||||
|
if [[ ! -f "${jar_path}" ]]; then
|
||||||
|
log_error "目标 Jar 不存在:${jar_path}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local debug_opts
|
||||||
|
debug_opts="$(build_debug_opts)"
|
||||||
|
|
||||||
|
log_info "开始启动应用..."
|
||||||
|
log_info "JDK 目录:${JDK_HOME}"
|
||||||
|
log_info "运行目录:${APP_HOME}"
|
||||||
|
log_info "日志目录:${LOG_DIR}"
|
||||||
|
log_info "配置目录:${CONFIG_DIR}"
|
||||||
|
log_info "Spring Profile:${SPRING_PROFILE}"
|
||||||
|
log_info "Debug 开关:${DEBUG_ENABLED}"
|
||||||
|
|
||||||
|
# 启动命令说明:
|
||||||
|
# 1. 使用 nohup 后台启动,避免会话退出导致进程结束
|
||||||
|
# 2. --spring.config.location 指向外置配置目录
|
||||||
|
# 3. 标准输出与错误输出统一重定向到 console.out
|
||||||
|
nohup "${JDK_HOME}/bin/java" \
|
||||||
|
${JVM_OPTS} \
|
||||||
|
${JAVA_SYS_PROPS} \
|
||||||
|
${debug_opts} \
|
||||||
|
-jar "${jar_path}" \
|
||||||
|
--spring.profiles.active="${SPRING_PROFILE}" \
|
||||||
|
--spring.config.location="${CONFIG_DIR}/" \
|
||||||
|
${SPRING_EXTRA_ARGS} \
|
||||||
|
>> "${CONSOLE_LOG_FILE}" 2>&1 &
|
||||||
|
|
||||||
|
local new_pid=$!
|
||||||
|
log_info "启动命令已提交,后台进程 PID(提交时): ${new_pid}"
|
||||||
|
|
||||||
|
log_info "等待 ${START_WAIT} 秒后进行启动结果检查..."
|
||||||
|
sleep "${START_WAIT}"
|
||||||
|
|
||||||
|
local started_pids
|
||||||
|
started_pids="$(get_pids_by_jar "${JAR_NAME}" || true)"
|
||||||
|
|
||||||
|
if [[ -n "${started_pids}" ]]; then
|
||||||
|
log_success "应用启动成功,当前 PID:${started_pids}"
|
||||||
|
log_info "启动日志文件:${CONSOLE_LOG_FILE}"
|
||||||
|
else
|
||||||
|
log_error "应用启动失败,未检测到进程。请检查日志:${CONSOLE_LOG_FILE}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打印当前配置,便于部署时核对
|
||||||
|
print_config() {
|
||||||
|
echo "---------------- 当前部署配置 ----------------"
|
||||||
|
echo "JDK_HOME = ${JDK_HOME}"
|
||||||
|
echo "APP_HOME = ${APP_HOME}"
|
||||||
|
echo "LOG_DIR = ${LOG_DIR}"
|
||||||
|
echo "CONFIG_DIR = ${CONFIG_DIR}"
|
||||||
|
echo "JAR_NAME = ${JAR_NAME:-<自动发现>}"
|
||||||
|
echo "SPRING_PROFILE = ${SPRING_PROFILE}"
|
||||||
|
echo "DEBUG_ENABLED = ${DEBUG_ENABLED}"
|
||||||
|
echo "DEBUG_PORT = ${DEBUG_PORT}"
|
||||||
|
echo "STOP_TIMEOUT(s) = ${STOP_TIMEOUT}"
|
||||||
|
echo "START_WAIT(s) = ${START_WAIT}"
|
||||||
|
echo "CONSOLE_LOG_FILE = ${CONSOLE_LOG_FILE}"
|
||||||
|
echo "------------------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
print_header
|
||||||
|
|
||||||
|
# 1) 解析参数
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
# 2) 打印配置
|
||||||
|
print_config
|
||||||
|
|
||||||
|
# 3) 前置检查
|
||||||
|
pre_check
|
||||||
|
|
||||||
|
# 4) 创建目录
|
||||||
|
ensure_dirs
|
||||||
|
|
||||||
|
# 5) 选择 Jar 包(指定或自动发现)
|
||||||
|
auto_select_jar
|
||||||
|
|
||||||
|
# 6) 每次启动前都进行同名进程检查并执行停止
|
||||||
|
stop_app_if_running
|
||||||
|
|
||||||
|
# 7) 启动应用并检查结果
|
||||||
|
start_app
|
||||||
|
|
||||||
|
log_success "部署流程执行完成。"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
388
deploy/springboot/generator.sh
Normal file
388
deploy/springboot/generator.sh
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Spring Boot 部署脚本生成器(交互式)
|
||||||
|
#
|
||||||
|
# 功能说明:
|
||||||
|
# 1. 交互式采集部署参数,提供默认值和选择项
|
||||||
|
# 2. 汇总展示参数,支持按编号二次修改
|
||||||
|
# 3. 用户确认后,以 deploy.sh 为模板生成 deploy-xxx.sh
|
||||||
|
# 4. 生成脚本中的参数会被固化为本次采集结果
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
TEMPLATE_FILE="${BASE_DIR}/deploy.sh"
|
||||||
|
|
||||||
|
COLOR_RESET="\033[0m"
|
||||||
|
COLOR_INFO="\033[1;34m"
|
||||||
|
COLOR_WARN="\033[1;33m"
|
||||||
|
COLOR_ERROR="\033[1;31m"
|
||||||
|
COLOR_SUCCESS="\033[1;32m"
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${COLOR_INFO}[信息]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${COLOR_WARN}[警告]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${COLOR_ERROR}[错误]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${COLOR_SUCCESS}[成功]${COLOR_RESET} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
用法:
|
||||||
|
$(basename "$0")
|
||||||
|
|
||||||
|
说明:
|
||||||
|
该脚本会基于同目录 deploy.sh 模板,交互式生成 deploy-xxx.sh。
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# 读取用户输入,支持默认值
|
||||||
|
ask_input() {
|
||||||
|
local prompt="$1"
|
||||||
|
local default_value="${2:-}"
|
||||||
|
local input
|
||||||
|
|
||||||
|
if [[ -n "${default_value}" ]]; then
|
||||||
|
read -r -p "${prompt} [默认: ${default_value}]: " input
|
||||||
|
if [[ -z "${input}" ]]; then
|
||||||
|
input="${default_value}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
read -r -p "${prompt}: " input
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${input}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 选择输入(数字菜单)
|
||||||
|
ask_choice() {
|
||||||
|
local title="$1"
|
||||||
|
local default_index="$2"
|
||||||
|
shift 2
|
||||||
|
local options=("$@")
|
||||||
|
local i
|
||||||
|
|
||||||
|
echo >&2
|
||||||
|
echo "${title}" >&2
|
||||||
|
for i in "${!options[@]}"; do
|
||||||
|
printf " %d) %s\n" "$((i + 1))" "${options[$i]}" >&2
|
||||||
|
done
|
||||||
|
|
||||||
|
local selected
|
||||||
|
while true; do
|
||||||
|
read -r -p "请输入选项序号 [默认: ${default_index}]: " selected
|
||||||
|
if [[ -z "${selected}" ]]; then
|
||||||
|
selected="${default_index}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${selected}" =~ ^[0-9]+$ ]] && (( selected >= 1 && selected <= ${#options[@]} )); then
|
||||||
|
echo "${options[$((selected - 1))]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "输入无效,请输入 1-${#options[@]} 的数字。" >&2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 是否确认(y/n)
|
||||||
|
ask_confirm() {
|
||||||
|
local prompt="$1"
|
||||||
|
local default_yes="${2:-y}"
|
||||||
|
local input
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if [[ "${default_yes}" == "y" ]]; then
|
||||||
|
read -r -p "${prompt} [Y/n]: " input
|
||||||
|
input="${input:-y}"
|
||||||
|
else
|
||||||
|
read -r -p "${prompt} [y/N]: " input
|
||||||
|
input="${input:-n}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${input}" in
|
||||||
|
y|Y) return 0 ;;
|
||||||
|
n|N) return 1 ;;
|
||||||
|
*) log_warn "请输入 y 或 n。" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 转义用于 sed 替换的值
|
||||||
|
escape_sed_replacement() {
|
||||||
|
echo "$1" | sed -e 's/[\\&|]/\\&/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 替换形如 VAR="..." 的变量定义行
|
||||||
|
replace_var_in_file() {
|
||||||
|
local file="$1"
|
||||||
|
local var_name="$2"
|
||||||
|
local var_value="$3"
|
||||||
|
local escaped
|
||||||
|
|
||||||
|
escaped="$(escape_sed_replacement "${var_value}")"
|
||||||
|
sed -i "s|^${var_name}=.*$|${var_name}=\"${escaped}\"|" "${file}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 展示当前采集到的配置
|
||||||
|
print_config_summary() {
|
||||||
|
echo
|
||||||
|
echo "================ 已采集参数(可二次修改) ================"
|
||||||
|
echo " 1) 应用标识(APP_KEY) : ${APP_KEY}"
|
||||||
|
echo " 2) 目标脚本路径(TARGET) : ${TARGET_SCRIPT}"
|
||||||
|
echo " 3) JDK_HOME : ${JDK_HOME}"
|
||||||
|
echo " 4) APP_HOME : ${APP_HOME}"
|
||||||
|
echo " 5) LOG_DIR : ${LOG_DIR}"
|
||||||
|
echo " 6) CONFIG_DIR : ${CONFIG_DIR}"
|
||||||
|
echo " 7) JAR_NAME : ${JAR_NAME:-<自动发现>}"
|
||||||
|
echo " 8) SPRING_PROFILE : ${SPRING_PROFILE}"
|
||||||
|
echo " 9) JVM_OPTS : ${JVM_OPTS}"
|
||||||
|
echo " 10) JAVA_SYS_PROPS : ${JAVA_SYS_PROPS}"
|
||||||
|
echo " 11) DEBUG_ENABLED : ${DEBUG_ENABLED}"
|
||||||
|
echo " 12) DEBUG_PORT : ${DEBUG_PORT}"
|
||||||
|
echo " 13) DEBUG_SUSPEND : ${DEBUG_SUSPEND}"
|
||||||
|
echo " 14) DEBUG_ADDRESS : ${DEBUG_ADDRESS}"
|
||||||
|
echo " 15) STOP_TIMEOUT : ${STOP_TIMEOUT}"
|
||||||
|
echo " 16) START_WAIT : ${START_WAIT}"
|
||||||
|
echo " 17) SPRING_EXTRA_ARGS : ${SPRING_EXTRA_ARGS}"
|
||||||
|
echo "=========================================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
modify_one_field() {
|
||||||
|
local idx="$1"
|
||||||
|
case "${idx}" in
|
||||||
|
1)
|
||||||
|
APP_KEY="$(ask_input "请输入应用标识(仅用于脚本命名,如 order、pay)" "${APP_KEY}")"
|
||||||
|
APP_KEY="$(echo "${APP_KEY}" | tr ' ' '-' | tr -cd '[:alnum:]_-')"
|
||||||
|
if [[ -z "${APP_KEY}" ]]; then
|
||||||
|
APP_KEY="app"
|
||||||
|
fi
|
||||||
|
TARGET_SCRIPT="${BASE_DIR}/deploy-${APP_KEY}.sh"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
TARGET_SCRIPT="$(ask_input "请输入目标脚本完整路径" "${TARGET_SCRIPT}")"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
JDK_HOME="$(ask_input "请输入 JDK_HOME" "${JDK_HOME}")"
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
APP_HOME="$(ask_input "请输入 APP_HOME(Jar 运行目录)" "${APP_HOME}")"
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
LOG_DIR="$(ask_input "请输入 LOG_DIR(日志目录)" "${LOG_DIR}")"
|
||||||
|
;;
|
||||||
|
6)
|
||||||
|
CONFIG_DIR="$(ask_input "请输入 CONFIG_DIR(外置配置目录)" "${CONFIG_DIR}")"
|
||||||
|
;;
|
||||||
|
7)
|
||||||
|
local jar_mode
|
||||||
|
jar_mode="$(ask_choice "请选择 Jar 名称模式" "1" "自动发现(JAR_NAME 留空)" "手动指定 Jar 名称")"
|
||||||
|
if [[ "${jar_mode}" == "自动发现(JAR_NAME 留空)" ]]; then
|
||||||
|
JAR_NAME=""
|
||||||
|
else
|
||||||
|
JAR_NAME="$(ask_input "请输入 Jar 名称(如 demo.jar)" "${JAR_NAME}")"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
8)
|
||||||
|
local profile_choice
|
||||||
|
profile_choice="$(ask_choice "请选择 Spring Profile" "4" "dev" "test" "stage" "prod" "custom")"
|
||||||
|
if [[ "${profile_choice}" == "custom" ]]; then
|
||||||
|
SPRING_PROFILE="$(ask_input "请输入自定义 profile" "${SPRING_PROFILE}")"
|
||||||
|
else
|
||||||
|
SPRING_PROFILE="${profile_choice}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
9)
|
||||||
|
JVM_OPTS="$(ask_input "请输入 JVM_OPTS" "${JVM_OPTS}")"
|
||||||
|
;;
|
||||||
|
10)
|
||||||
|
JAVA_SYS_PROPS="$(ask_input "请输入 JAVA_SYS_PROPS" "${JAVA_SYS_PROPS}")"
|
||||||
|
;;
|
||||||
|
11)
|
||||||
|
local debug_choice
|
||||||
|
debug_choice="$(ask_choice "是否启用 Debug" "1" "false" "true")"
|
||||||
|
DEBUG_ENABLED="${debug_choice}"
|
||||||
|
;;
|
||||||
|
12)
|
||||||
|
DEBUG_PORT="$(ask_input "请输入 DEBUG_PORT" "${DEBUG_PORT}")"
|
||||||
|
;;
|
||||||
|
13)
|
||||||
|
DEBUG_SUSPEND="$(ask_choice "DEBUG_SUSPEND 选择" "2" "y" "n")"
|
||||||
|
;;
|
||||||
|
14)
|
||||||
|
DEBUG_ADDRESS="$(ask_input "请输入 DEBUG_ADDRESS" "${DEBUG_ADDRESS}")"
|
||||||
|
;;
|
||||||
|
15)
|
||||||
|
STOP_TIMEOUT="$(ask_input "请输入 STOP_TIMEOUT(秒)" "${STOP_TIMEOUT}")"
|
||||||
|
;;
|
||||||
|
16)
|
||||||
|
START_WAIT="$(ask_input "请输入 START_WAIT(秒)" "${START_WAIT}")"
|
||||||
|
;;
|
||||||
|
17)
|
||||||
|
SPRING_EXTRA_ARGS="$(ask_input "请输入 SPRING_EXTRA_ARGS(可留空)" "${SPRING_EXTRA_ARGS}")"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_warn "忽略无效编号:${idx}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_inputs() {
|
||||||
|
echo "============================================================"
|
||||||
|
echo "Spring Boot 部署脚本生成器"
|
||||||
|
echo "模板文件:${TEMPLATE_FILE}"
|
||||||
|
echo "============================================================"
|
||||||
|
|
||||||
|
if [[ ! -f "${TEMPLATE_FILE}" ]]; then
|
||||||
|
log_error "模板文件不存在:${TEMPLATE_FILE}"
|
||||||
|
log_error "请先确认同目录下存在 deploy.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
APP_KEY="$(ask_input "请输入应用标识(用于生成 deploy-xxx.sh)" "app")"
|
||||||
|
APP_KEY="$(echo "${APP_KEY}" | tr ' ' '-' | tr -cd '[:alnum:]_-')"
|
||||||
|
if [[ -z "${APP_KEY}" ]]; then
|
||||||
|
APP_KEY="app"
|
||||||
|
fi
|
||||||
|
|
||||||
|
TARGET_SCRIPT="${BASE_DIR}/deploy-${APP_KEY}.sh"
|
||||||
|
|
||||||
|
JDK_HOME="$(ask_input "请输入 JDK_HOME" "/usr/local/jdk")"
|
||||||
|
APP_HOME="$(ask_input "请输入 APP_HOME(Jar 运行目录)" "/opt/apps/springboot")"
|
||||||
|
LOG_DIR="$(ask_input "请输入 LOG_DIR(日志目录)" "/opt/logs/springboot")"
|
||||||
|
CONFIG_DIR="$(ask_input "请输入 CONFIG_DIR(外置配置目录)" "/opt/config/springboot")"
|
||||||
|
|
||||||
|
local jar_mode
|
||||||
|
jar_mode="$(ask_choice "请选择 Jar 名称模式" "1" "自动发现(JAR_NAME 留空)" "手动指定 Jar 名称")"
|
||||||
|
if [[ "${jar_mode}" == "自动发现(JAR_NAME 留空)" ]]; then
|
||||||
|
JAR_NAME=""
|
||||||
|
else
|
||||||
|
JAR_NAME="$(ask_input "请输入 Jar 名称(如 demo.jar)" "")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local profile_choice
|
||||||
|
profile_choice="$(ask_choice "请选择 Spring Profile" "4" "dev" "test" "stage" "prod" "custom")"
|
||||||
|
if [[ "${profile_choice}" == "custom" ]]; then
|
||||||
|
SPRING_PROFILE="$(ask_input "请输入自定义 profile" "prod")"
|
||||||
|
else
|
||||||
|
SPRING_PROFILE="${profile_choice}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
JVM_OPTS="$(ask_input "请输入 JVM_OPTS" "-Xms512m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR}/heapdump.hprof -Dfile.encoding=UTF-8")"
|
||||||
|
JAVA_SYS_PROPS="$(ask_input "请输入 JAVA_SYS_PROPS" "-Duser.timezone=Asia/Shanghai")"
|
||||||
|
|
||||||
|
local debug_choice
|
||||||
|
debug_choice="$(ask_choice "是否启用 Debug" "1" "false" "true")"
|
||||||
|
DEBUG_ENABLED="${debug_choice}"
|
||||||
|
|
||||||
|
DEBUG_PORT="5005"
|
||||||
|
DEBUG_SUSPEND="n"
|
||||||
|
DEBUG_ADDRESS="0.0.0.0"
|
||||||
|
if [[ "${DEBUG_ENABLED}" == "true" ]]; then
|
||||||
|
DEBUG_PORT="$(ask_input "请输入 DEBUG_PORT" "5005")"
|
||||||
|
DEBUG_SUSPEND="$(ask_choice "DEBUG_SUSPEND 选择" "2" "y" "n")"
|
||||||
|
DEBUG_ADDRESS="$(ask_input "请输入 DEBUG_ADDRESS" "0.0.0.0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
STOP_TIMEOUT="$(ask_input "请输入 STOP_TIMEOUT(秒)" "30")"
|
||||||
|
START_WAIT="$(ask_input "请输入 START_WAIT(秒)" "8")"
|
||||||
|
SPRING_EXTRA_ARGS="$(ask_input "请输入 SPRING_EXTRA_ARGS(可留空)" "")"
|
||||||
|
}
|
||||||
|
|
||||||
|
review_and_modify_loop() {
|
||||||
|
while true; do
|
||||||
|
print_config_summary
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "如需修改参数,请输入编号(支持多个,逗号分隔,例如 3,4,7)。"
|
||||||
|
echo "直接回车表示不修改。输入 q 退出生成。"
|
||||||
|
local selection
|
||||||
|
read -r -p "请输入: " selection
|
||||||
|
|
||||||
|
if [[ "${selection}" == "q" ]]; then
|
||||||
|
log_warn "已取消生成。"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${selection}" ]]; then
|
||||||
|
# 将逗号转换为空格后逐个处理
|
||||||
|
local token
|
||||||
|
for token in ${selection//,/ }; do
|
||||||
|
if [[ "${token}" =~ ^[0-9]+$ ]]; then
|
||||||
|
modify_one_field "${token}"
|
||||||
|
else
|
||||||
|
log_warn "忽略无效输入:${token}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ask_confirm "确认以上配置并生成脚本吗?" "y"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_script() {
|
||||||
|
local target_dir
|
||||||
|
target_dir="$(dirname "${TARGET_SCRIPT}")"
|
||||||
|
|
||||||
|
if [[ ! -d "${target_dir}" ]]; then
|
||||||
|
log_info "目标目录不存在,正在创建:${target_dir}"
|
||||||
|
mkdir -p "${target_dir}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "${TARGET_SCRIPT}" ]]; then
|
||||||
|
if ! ask_confirm "目标脚本已存在,是否覆盖?" "n"; then
|
||||||
|
log_warn "用户取消覆盖,生成中止。"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "${TEMPLATE_FILE}" "${TARGET_SCRIPT}"
|
||||||
|
|
||||||
|
# 将模板中的顶部参数固化为本次采集值
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "JDK_HOME" "${JDK_HOME}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "APP_HOME" "${APP_HOME}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "LOG_DIR" "${LOG_DIR}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "CONFIG_DIR" "${CONFIG_DIR}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "JAR_NAME" "${JAR_NAME}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "JVM_OPTS" "${JVM_OPTS}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "SPRING_PROFILE" "${SPRING_PROFILE}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "JAVA_SYS_PROPS" "${JAVA_SYS_PROPS}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "DEBUG_ENABLED" "${DEBUG_ENABLED}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "DEBUG_PORT" "${DEBUG_PORT}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "DEBUG_SUSPEND" "${DEBUG_SUSPEND}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "DEBUG_ADDRESS" "${DEBUG_ADDRESS}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "STOP_TIMEOUT" "${STOP_TIMEOUT}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "START_WAIT" "${START_WAIT}"
|
||||||
|
replace_var_in_file "${TARGET_SCRIPT}" "SPRING_EXTRA_ARGS" "${SPRING_EXTRA_ARGS}"
|
||||||
|
|
||||||
|
chmod +x "${TARGET_SCRIPT}" 2>/dev/null || true
|
||||||
|
|
||||||
|
log_success "生成完成:${TARGET_SCRIPT}"
|
||||||
|
log_info "可以直接执行:${TARGET_SCRIPT}"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if [[ "${1:-}" == "--help" ]]; then
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
collect_inputs
|
||||||
|
review_and_modify_loop
|
||||||
|
generate_script
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user