#!/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}[INFO]${COLOR_RESET} $*" } log_warn() { echo -e "${COLOR_WARN}[WARN]${COLOR_RESET} $*" } log_error() { echo -e "${COLOR_ERROR}[ERROR]${COLOR_RESET} $*" } log_success() { echo -e "${COLOR_SUCCESS}[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 "$@"