← 返回首页

项目实现Shell脚本部署

项目实现Shell脚本部署

1、common.sh :部署脚本的公共工具库

#!/usr/bin/env bash
# 公共函数:被其他脚本 source

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

load_env() {
  local env_file="${SCRIPT_DIR}/.env"
  local example_file="${SCRIPT_DIR}/env.example"
  # Windows 上传的 CRLF 会导致 source 报 $'\r',先规范化到临时文件
  normalize_and_source() {
    local src="$1"
    local tmp
    tmp="$(mktemp)"
    tr -d '\r' < "${src}" > "${tmp}"
    # shellcheck disable=SC1090
    set -a
    # shellcheck disable=SC1090
    source "${tmp}"
    set +a
    rm -f "${tmp}"
  }

  if [[ -f "${env_file}" ]]; then
    normalize_and_source "${env_file}"
  else
    echo "[WARN] 未找到 ${env_file},使用 env.example 默认值"
    normalize_and_source "${example_file}"
  fi

  APP_HOME="${APP_HOME:-${PROJECT_ROOT}}"
  APP_NAME="${APP_NAME:-linkey-website}"
  JAR_NAME="${JAR_NAME:-website-0.0.1-SNAPSHOT.jar}"
  SERVER_PORT="${SERVER_PORT:-8898}"
  SPRING_PROFILES_ACTIVE="${SPRING_PROFILES_ACTIVE:-prod}"
  JAVA_OPTS="${JAVA_OPTS:--Xms256m -Xmx512m -Dfile.encoding=UTF-8}"
  DB_HOST="${DB_HOST:-127.0.0.1}"
  DB_PORT="${DB_PORT:-3306}"
  DB_NAME="${DB_NAME:-website}"
  DB_USER="${DB_USER:-root}"
  DB_PASSWORD="${DB_PASSWORD:-root}"
  HEALTH_URL="${HEALTH_URL:-http://127.0.0.1:${SERVER_PORT}/}"
  HEALTH_RETRIES="${HEALTH_RETRIES:-30}"
  HEALTH_INTERVAL="${HEALTH_INTERVAL:-2}"
  SKIP_TESTS="${SKIP_TESTS:-true}"

  if [[ -n "${JAVA_HOME:-}" && -z "${JAVA_BIN:-}" ]]; then
    JAVA_BIN="${JAVA_HOME}/bin/java"
  fi
  JAVA_BIN="${JAVA_BIN:-java}"
  MVN_BIN="${MVN_BIN:-mvn}"

  RUN_DIR="${APP_HOME}/deploy/run"
  LOG_DIR="${APP_HOME}/logs"
  BACKUP_DIR="${APP_HOME}/deploy/backup"
  PID_FILE="${RUN_DIR}/${APP_NAME}.pid"
  JAR_PATH="${APP_HOME}/target/${JAR_NAME}"
  UPLOAD_DIR="${APP_HOME}/src/main/resources/static/img"
}

ensure_dirs() {
  mkdir -p "${RUN_DIR}" "${LOG_DIR}" "${BACKUP_DIR}" "${UPLOAD_DIR}"
}

require_cmd() {
  local cmd="$1"
  if ! command -v "${cmd}" >/dev/null 2>&1 && [[ ! -x "${cmd}" ]]; then
    echo "[ERROR] 找不到命令: ${cmd}"
    exit 1
  fi
}

is_running() {
  if [[ -f "${PID_FILE}" ]]; then
    local pid
    pid="$(cat "${PID_FILE}")"
    if [[ -n "${pid}" ]] && kill -0 "${pid}" >/dev/null 2>&1; then
      return 0
    fi
  fi
  # 兜底:按 jar 名查进程
  if pgrep -f "${JAR_NAME}" >/dev/null 2>&1; then
    return 0
  fi
  return 1
}

get_pid() {
  if [[ -f "${PID_FILE}" ]]; then
    local pid
    pid="$(cat "${PID_FILE}")"
    if [[ -n "${pid}" ]] && kill -0 "${pid}" >/dev/null 2>&1; then
      echo "${pid}"
      return 0
    fi
  fi
  pgrep -f "${JAR_NAME}" | head -n 1 || true
}

wait_health() {
  local i=1
  echo "[INFO] 健康检查: ${HEALTH_URL}"
  while (( i <= HEALTH_RETRIES )); do
    if curl -fsS --max-time 3 "${HEALTH_URL}" >/dev/null 2>&1; then
      echo "[OK] 服务已就绪 (${i}/${HEALTH_RETRIES})"
      return 0
    fi
    echo "[INFO] 等待启动... (${i}/${HEALTH_RETRIES})"
    sleep "${HEALTH_INTERVAL}"
    ((i++))
  done
  echo "[ERROR] 健康检查失败,请查看 ${LOG_DIR}/app.log"
  return 1
}

print_banner() {
  echo "========================================"
  echo "  ${APP_NAME} 部署工具"
  echo "  APP_HOME : ${APP_HOME}"
  echo "  PORT     : ${SERVER_PORT}"
  echo "  PROFILE  : ${SPRING_PROFILES_ACTIVE}"
  echo "========================================"
}

 

2、deploy.sh:一键部署入口实现构建 → 备份旧包 → 停服 → 启服 → 健康检查

#!/usr/bin/env bash
# 一键部署:构建 → 备份旧包 → 停服 → 启服 → 健康检查
#
# 用法:
#   bash deploy/deploy.sh
#   bash deploy/deploy.sh --skip-build   # 跳过 Maven 构建,仅重启

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "${SCRIPT_DIR}/common.sh"
load_env
ensure_dirs
print_banner

SKIP_BUILD=false
for arg in "$@"; do
  case "${arg}" in
    --skip-build) SKIP_BUILD=true ;;
    -h|--help)
      echo "Usage: bash deploy/deploy.sh [--skip-build]"
      exit 0
      ;;
  esac
done

cd "${APP_HOME}"

# 构建前备份旧 jar,便于回滚
if [[ -f "${JAR_PATH}" ]]; then
  TS="$(date +%Y%m%d%H%M%S)"
  cp -f "${JAR_PATH}" "${BACKUP_DIR}/${JAR_NAME}.${TS}.bak"
  echo "[INFO] 已备份旧包 → ${BACKUP_DIR}/${JAR_NAME}.${TS}.bak"
  ls -1t "${BACKUP_DIR}/${JAR_NAME}".*.bak 2>/dev/null | tail -n +6 | xargs -r rm -f
fi

if [[ "${SKIP_BUILD}" != "true" ]]; then
  require_cmd "${MVN_BIN}"
  echo "[INFO] Maven 构建中..."
  if [[ "${SKIP_TESTS}" == "true" ]]; then
    "${MVN_BIN}" clean package -DskipTests
  else
    "${MVN_BIN}" clean package
  fi

  if [[ ! -f "${JAR_PATH}" ]]; then
    echo "[ERROR] 构建完成但未找到 ${JAR_PATH}"
    exit 1
  fi
else
  echo "[INFO] 跳过构建 (--skip-build)"
  if [[ ! -f "${JAR_PATH}" ]]; then
    echo "[ERROR] 找不到 ${JAR_PATH},无法跳过构建"
    exit 1
  fi
fi

bash "${SCRIPT_DIR}/stop.sh"
bash "${SCRIPT_DIR}/start.sh"

echo "[DONE] 部署完成 → http://$(hostname -I 2>/dev/null | awk '{print $1}'):${SERVER_PORT}/"

 

 

标签: 项目