commit aac94ddf4a1114631b666ae781685a833a1fe8c1 Author: hanxuanyu <2252193204@qq.com> Date: Thu Mar 5 11:06:02 2026 +0800 新增springboot应用自动部署脚本 diff --git a/deploy/springboot/deploy.sh b/deploy/springboot/deploy.sh new file mode 100644 index 0000000..b9dbfd3 --- /dev/null +++ b/deploy/springboot/deploy.sh @@ -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-.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 < 指定 JDK_HOME + --app-home 指定 APP_HOME + --log-dir 指定 LOG_DIR + --config-dir 指定 CONFIG_DIR + --jar-name 指定 Jar 包名称(如 demo.jar) + --profile 指定 Spring Profile(如 dev/test/prod) + --debug 是否开启远程调试 + --debug-port 调试端口(默认 5005) + --stop-timeout 停止超时时间(秒) + --start-wait 启动后等待检查时间(秒) + --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 "$@" diff --git a/deploy/springboot/generator.sh b/deploy/springboot/generator.sh new file mode 100644 index 0000000..7e43304 --- /dev/null +++ b/deploy/springboot/generator.sh @@ -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 <&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 "$@"