389 lines
12 KiB
Bash
389 lines
12 KiB
Bash
#!/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 "$@"
|