xykj-plus/claude-ecc/admin-xy/deploy.sh
2026-06-13 13:48:51 +08:00

229 lines
8.7 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# deploy.sh — admin-xy 生产发布脚本
#
# 用法:
# ./deploy.sh # 构建镜像并部署(首次或更新)
# ./deploy.sh --no-cache # 强制全量重新构建镜像
# ./deploy.sh --stop # 停止并移除 admin-xy 前后端容器(保留数据和配置)
# ./deploy.sh --restart # 重启 admin-xy 前后端容器(不重新构建)
#
# 前提:
# - Docker 及 Docker Compose 已安装
# - /opt/admin-xy/.env 已配置(首次运行时自动生成模板)
set -euo pipefail
# ── 常量 ────────────────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RELEASE_DIR="/opt/admin-xy"
ENV_FILE="${RELEASE_DIR}/.env"
COMPOSE_FILE="${RELEASE_DIR}/docker-compose.yml"
GIT_CONFIG_DIR="${RELEASE_DIR}/git"
ADMIN_SERVICES=(server web)
# ── 辅助函数 ─────────────────────────────────────────────────────────────────
log() { echo "[$(date '+%H:%M:%S')] $*"; }
err() { echo "[ERROR] $*" >&2; exit 1; }
warn() { echo "[WARN] $*" >&2; }
require_root() {
if [[ $EUID -ne 0 ]]; then
err "该脚本需要 root 权限(请使用 sudo ./deploy.sh"
fi
}
load_env_file() {
local line key value
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ "$line" == *"="* ]] || continue
key="${line%%=*}"
value="${line#*=}"
key="${key#"${key%%[![:space:]]*}"}"
key="${key%"${key##*[![:space:]]}"}"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
if [[ "$value" != \"*\" && "$value" != \'*\' ]]; then
value="${value%%[[:space:]]#*}"
fi
[[ "$value" =~ ^\"(.*)\"$ ]] && value="${BASH_REMATCH[1]}"
[[ "$value" =~ ^\'(.*)\'$ ]] && value="${BASH_REMATCH[1]}"
export "${key}=${value}"
done < "${ENV_FILE}"
}
write_git_config_files() {
local git_name git_email credentials_file
git_name="${GIT_AUTHOR_NAME:-dev-assistant}"
git_email="${GIT_AUTHOR_EMAIL:-dev-assistant@example.com}"
credentials_file="${GIT_CONFIG_DIR}/.git-credentials"
mkdir -p "${GIT_CONFIG_DIR}"
chmod 700 "${GIT_CONFIG_DIR}"
cat > "${GIT_CONFIG_DIR}/.gitconfig" <<EOF
[user]
name = ${git_name}
email = ${git_email}
[credential]
helper = store
[safe]
directory = /workspace
EOF
: > "${credentials_file}"
if [[ -n "${GITEE_USERNAME:-}" && -n "${GITEE_TOKEN:-}" ]]; then
printf 'https://%s:%s@gitee.com\n' "${GITEE_USERNAME}" "${GITEE_TOKEN}" > "${credentials_file}"
else
warn "未配置 GITEE_USERNAME/GITEE_TOKEN容器内 git push 可能需要重新认证"
fi
chmod 600 "${GIT_CONFIG_DIR}/.gitconfig" "${credentials_file}"
}
# ── 参数解析 ──────────────────────────────────────────────────────────────────
ACTION="deploy"
NO_CACHE=""
for arg in "$@"; do
case "$arg" in
--no-cache) NO_CACHE="--no-cache" ;;
--stop) ACTION="stop" ;;
--restart) ACTION="restart" ;;
--help|-h)
sed -n '2,13p' "$0" | sed 's/^# \?//'
exit 0
;;
*) err "未知参数:$arg(使用 --help 查看用法)" ;;
esac
done
# ── 发布目录初始化 ────────────────────────────────────────────────────────────
init_release_dir() {
log "初始化发布目录 ${RELEASE_DIR} ..."
mkdir -p "${RELEASE_DIR}"
# 写入 .env 模板(仅首次,避免覆盖已有配置)
if [[ ! -f "${ENV_FILE}" ]]; then
log "生成 .env 配置模板 → ${ENV_FILE}"
cat > "${ENV_FILE}" <<'EOF'
# ── 数据库连接(必填)────────────────────────────────────────────────────────
# JDBC URL 示例jdbc:mysql://127.0.0.1:3306/adminxy?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia%2FShanghai
SPRING_DATASOURCE_ADMIN_URL=jdbc:mysql://127.0.0.1:3306/adminxy?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia%2FShanghai
SPRING_DATASOURCE_ADMIN_USERNAME=adminxy
SPRING_DATASOURCE_ADMIN_PASSWORD=changeme
# ── JWT 密钥(必填,至少 32 个字符)─────────────────────────────────────────
JWT_SECRET=change-me-in-production-32bytes-min-secret-key-please
# ── 容器内 Git 身份与 Gitee HTTPS 凭据(可选,用于 Agent 提交/推送)────────
GIT_AUTHOR_NAME=dev-assistant
GIT_AUTHOR_EMAIL=dev-assistant@example.com
GITEE_USERNAME=
GITEE_TOKEN=
EOF
warn "已生成 .env 模板,请编辑后再次运行:${ENV_FILE}"
exit 0
fi
# 校验必填变量
local missing=()
load_env_file
[[ -z "${SPRING_DATASOURCE_ADMIN_URL:-}" ]] && missing+=("SPRING_DATASOURCE_ADMIN_URL")
[[ -z "${SPRING_DATASOURCE_ADMIN_USERNAME:-}" ]] && missing+=("SPRING_DATASOURCE_ADMIN_USERNAME")
[[ -z "${SPRING_DATASOURCE_ADMIN_PASSWORD:-}" ]] && missing+=("SPRING_DATASOURCE_ADMIN_PASSWORD")
[[ -z "${JWT_SECRET:-}" ]] && missing+=("JWT_SECRET")
if [[ ${#missing[@]} -gt 0 ]]; then
err "以下必填项未配置:${missing[*]}(请检查 ${ENV_FILE}"
fi
write_git_config_files
# 同步 docker-compose.yml
log "同步 docker-compose.yml → ${COMPOSE_FILE}"
cp "${SCRIPT_DIR}/docker-compose.yml" "${COMPOSE_FILE}"
}
# ── 构建镜像 ──────────────────────────────────────────────────────────────────
build_images() {
log "构建 Docker 镜像 ..."
"${SCRIPT_DIR}/build-images.sh" ${NO_CACHE}
}
# ── 部署 ─────────────────────────────────────────────────────────────────────
deploy() {
log "启动 admin-xy 前后端服务 ..."
docker compose \
--project-directory "${RELEASE_DIR}" \
--file "${COMPOSE_FILE}" \
--env-file "${ENV_FILE}" \
up -d "${ADMIN_SERVICES[@]}"
log "当前 admin-xy 前后端容器状态:"
docker compose \
--project-directory "${RELEASE_DIR}" \
--file "${COMPOSE_FILE}" \
ps "${ADMIN_SERVICES[@]}"
}
# ── 停止 ─────────────────────────────────────────────────────────────────────
stop_services() {
if [[ ! -f "${COMPOSE_FILE}" ]]; then
err "未找到 ${COMPOSE_FILE},无法停止(是否已部署?)"
fi
log "停止并移除 admin-xy 前后端容器 ..."
docker compose \
--project-directory "${RELEASE_DIR}" \
--file "${COMPOSE_FILE}" \
stop "${ADMIN_SERVICES[@]}"
docker compose \
--project-directory "${RELEASE_DIR}" \
--file "${COMPOSE_FILE}" \
rm -f "${ADMIN_SERVICES[@]}"
log "admin-xy 前后端容器已停止并移除。"
}
# ── 重启 ─────────────────────────────────────────────────────────────────────
restart_services() {
if [[ ! -f "${COMPOSE_FILE}" ]]; then
err "未找到 ${COMPOSE_FILE},请先执行完整部署:./deploy.sh"
fi
log "重启 admin-xy 前后端服务 ..."
docker compose \
--project-directory "${RELEASE_DIR}" \
--file "${COMPOSE_FILE}" \
--env-file "${ENV_FILE}" \
restart "${ADMIN_SERVICES[@]}"
log "当前 admin-xy 前后端容器状态:"
docker compose \
--project-directory "${RELEASE_DIR}" \
--file "${COMPOSE_FILE}" \
ps "${ADMIN_SERVICES[@]}"
}
# ── 主流程 ────────────────────────────────────────────────────────────────────
require_root
case "${ACTION}" in
deploy)
log "=== admin-xy 生产部署开始 ==="
init_release_dir
build_images
deploy
log "=== 部署完成 ==="
log "前端http://<server-ip>:9001"
log "后端http://<server-ip>:9010"
;;
stop)
stop_services
;;
restart)
restart_services
;;
esac