429 lines
13 KiB
Bash
Executable File
429 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
##############################################
|
||
# 校园新闻管理系统 - 构建和打包脚本
|
||
# 功能:
|
||
# 1. 从git拉取最新代码
|
||
# 2. 编译后端jar包
|
||
# 3. 构建前端dist
|
||
# 4. 制作Docker镜像(支持基础镜像和应用镜像)
|
||
# 5. 保存镜像到文件(用于离线部署)
|
||
#
|
||
# 使用方法:
|
||
# ./build.sh [target] [options] [version]
|
||
#
|
||
# 目标(target):
|
||
# base-serv - 构建后端基础镜像(包含Python依赖)
|
||
# base-web - 构建前端基础镜像(Node)
|
||
# base-all - 构建所有基础镜像
|
||
# serv - 构建后端服务镜像
|
||
# web - 构建前端服务镜像(Node + Vite Preview)
|
||
# mysql - 构建MySQL镜像
|
||
# all - 构建所有应用镜像(默认)
|
||
#
|
||
#
|
||
# 选项(options):
|
||
# build - 编译代码(serv和web需要)
|
||
# save - 保存镜像到tar文件(配合build自动保存构建版本)
|
||
# save=YYYYMMDD_HHMMSS - 保存指定版本的镜像
|
||
#
|
||
# 保存规则:
|
||
# - build save → 自动保存刚构建的版本(推荐)
|
||
# - save=20251124_xxx → 保存指定版本(必须已存在)
|
||
# - 只使用save → 报错,必须指定版本
|
||
#
|
||
# 示例:
|
||
# ./build.sh base-all # 构建所有基础镜像
|
||
# ./build.sh serv build save # 编译+构建+保存(自动使用构建版本)✅
|
||
# ./build.sh all build save # 完整构建流程
|
||
# ./build.sh serv save=20251124_143025 # 保存已存在的指定版本
|
||
# ./build.sh serv save # ❌ 错误:必须指定版本
|
||
##############################################
|
||
|
||
set -e # 遇到错误立即退出
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 日志函数
|
||
log_info() {
|
||
echo -e "${GREEN}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_warn() {
|
||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
log_step() {
|
||
echo -e "${BLUE}[STEP]${NC} $1"
|
||
}
|
||
|
||
# 项目路径
|
||
PROJECT_ROOT=$(cd "$(dirname "$0")" && pwd)
|
||
SERV_PATH="${PROJECT_ROOT}/schoolNewsServ"
|
||
WEB_PATH="${PROJECT_ROOT}/schoolNewsWeb"
|
||
DOCKER_PATH="${PROJECT_ROOT}/docker"
|
||
BUILD_OUTPUT="${PROJECT_ROOT}/build_output"
|
||
IMAGE_VERSION=$(date +%Y%m%d_%H%M%S)
|
||
|
||
# 解析命令行参数
|
||
BUILD_TARGET="${1:-all}" # 默认构建所有应用镜像
|
||
shift || true
|
||
DO_BUILD=false
|
||
DO_SAVE=false
|
||
SAVE_VERSION="" # 要保存的版本号
|
||
|
||
# 解析选项
|
||
for arg in "$@"; do
|
||
case $arg in
|
||
build)
|
||
DO_BUILD=true
|
||
;;
|
||
save)
|
||
DO_SAVE=true
|
||
# 如果不指定版本,使用当前构建的版本
|
||
;;
|
||
save=*)
|
||
DO_SAVE=true
|
||
SAVE_VERSION="${arg#save=}"
|
||
;;
|
||
*)
|
||
log_warn "未知选项: $arg"
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 确定保存版本
|
||
if [ "${DO_SAVE}" = true ]; then
|
||
if [ -z "${SAVE_VERSION}" ]; then
|
||
if [ "${DO_BUILD}" = true ]; then
|
||
# 如果有build参数,保存刚构建的版本
|
||
SAVE_VERSION="${IMAGE_VERSION}"
|
||
else
|
||
# 如果没有build参数且未指定版本,提示错误
|
||
echo ""
|
||
log_error "❌ 错误:只使用save时必须指定版本"
|
||
echo ""
|
||
echo "正确用法:"
|
||
echo " 1. 保存指定版本: ./build.sh ${BUILD_TARGET} save=20251124_143025"
|
||
echo " 2. 构建并保存: ./build.sh ${BUILD_TARGET} build save"
|
||
echo ""
|
||
echo "查看可用版本:"
|
||
echo " docker images | grep school-news"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
echo "=========================================="
|
||
echo "校园新闻管理系统 - 构建脚本"
|
||
echo "=========================================="
|
||
log_info "构建目标: ${BUILD_TARGET}"
|
||
log_info "构建版本: ${IMAGE_VERSION}"
|
||
log_info "编译代码: ${DO_BUILD}"
|
||
log_info "保存镜像: ${DO_SAVE}"
|
||
if [ "${DO_SAVE}" = true ]; then
|
||
if [ "${DO_BUILD}" = true ]; then
|
||
log_info "保存版本: ${SAVE_VERSION} (自动使用构建版本)"
|
||
else
|
||
log_info "保存版本: ${SAVE_VERSION} (指定版本)"
|
||
fi
|
||
fi
|
||
log_info "项目路径: ${PROJECT_ROOT}"
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
# 创建输出目录
|
||
mkdir -p "${BUILD_OUTPUT}"
|
||
|
||
# ================================================
|
||
# 构建函数
|
||
# ================================================
|
||
|
||
# 构建后端基础镜像
|
||
build_base_serv() {
|
||
log_step "构建后端基础镜像(Base Serv)"
|
||
cd "${PROJECT_ROOT}"
|
||
|
||
log_info "执行: docker build -t school-news-base-serv:${IMAGE_VERSION} -f docker/Dockerfile.base-serv ."
|
||
if docker build -t school-news-base-serv:${IMAGE_VERSION} -f docker/Dockerfile.base-serv .; then
|
||
docker tag school-news-base-serv:${IMAGE_VERSION} school-news-base-serv:latest
|
||
log_info "✅ 后端基础镜像构建成功"
|
||
log_info " 镜像标签: school-news-base-serv:${IMAGE_VERSION}"
|
||
log_info " 镜像标签: school-news-base-serv:latest"
|
||
else
|
||
log_error "❌ 后端基础镜像构建失败"
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
# 构建MySQL镜像
|
||
build_mysql() {
|
||
log_step "构建MySQL镜像"
|
||
cd "${PROJECT_ROOT}"
|
||
|
||
log_info "执行: docker build -t school-news-mysql:${IMAGE_VERSION} -f docker/Dockerfile.mysql ."
|
||
if docker build -t school-news-mysql:${IMAGE_VERSION} -f docker/Dockerfile.mysql .; then
|
||
docker tag school-news-mysql:${IMAGE_VERSION} school-news-mysql:latest
|
||
log_info "✅ MySQL镜像构建成功"
|
||
log_info " 镜像标签: school-news-mysql:${IMAGE_VERSION}"
|
||
log_info " 镜像标签: school-news-mysql:latest"
|
||
else
|
||
log_error "❌ MySQL镜像构建失败"
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
# 构建后端服务镜像
|
||
build_serv() {
|
||
log_step "构建后端服务镜像(Serv)"
|
||
cd "${PROJECT_ROOT}"
|
||
|
||
# 检查基础镜像
|
||
if ! docker images | grep -q "school-news-base-serv.*latest"; then
|
||
log_error "基础镜像不存在: school-news-base-serv:latest"
|
||
log_error "请先构建基础镜像:./build.sh base-serv"
|
||
exit 1
|
||
fi
|
||
|
||
# 检查jar包
|
||
if [ ! -f "${SERV_PATH}/admin/target/admin-1.0.0.jar" ]; then
|
||
log_error "jar包不存在: ${SERV_PATH}/admin/target/admin-1.0.0.jar"
|
||
log_error "请先编译后端项目:./build.sh serv build"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "执行: docker build -t school-news-serv:${IMAGE_VERSION} -f docker/Dockerfile.serv ."
|
||
if docker build -t school-news-serv:${IMAGE_VERSION} -f docker/Dockerfile.serv .; then
|
||
docker tag school-news-serv:${IMAGE_VERSION} school-news-serv:latest
|
||
log_info "✅ 后端服务镜像构建成功"
|
||
log_info " 镜像标签: school-news-serv:${IMAGE_VERSION}"
|
||
log_info " 镜像标签: school-news-serv:latest"
|
||
else
|
||
log_error "❌ 后端服务镜像构建失败"
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
# 构建前端服务镜像
|
||
build_web() {
|
||
log_step "构建前端服务镜像(Web)"
|
||
cd "${PROJECT_ROOT}"
|
||
|
||
# 检查dist目录
|
||
if [ ! -d "${WEB_PATH}/dist" ]; then
|
||
log_error "dist目录不存在: ${WEB_PATH}/dist"
|
||
log_error "请先构建前端项目:./build.sh web build"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "执行: docker build -t school-news-web:${IMAGE_VERSION} -f docker/Dockerfile.web ."
|
||
if docker build -t school-news-web:${IMAGE_VERSION} -f docker/Dockerfile.web .; then
|
||
docker tag school-news-web:${IMAGE_VERSION} school-news-web:latest
|
||
log_info "✅ 前端服务镜像构建成功"
|
||
log_info " 镜像标签: school-news-web:${IMAGE_VERSION}"
|
||
log_info " 镜像标签: school-news-web:latest"
|
||
else
|
||
log_error "❌ 前端服务镜像构建失败"
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
# 编译后端
|
||
compile_serv() {
|
||
log_step "编译后端jar包"
|
||
cd "${SERV_PATH}"
|
||
|
||
log_info "清理旧的构建..."
|
||
mvn clean -q
|
||
|
||
log_info "开始编译后端项目..."
|
||
if mvn package -DskipTests -pl admin -am; then
|
||
JAR_FILE="${SERV_PATH}/admin/target/admin-1.0.0.jar"
|
||
JAR_SIZE=$(du -h "${JAR_FILE}" | cut -f1)
|
||
log_info "✅ 后端编译成功: ${JAR_SIZE}"
|
||
else
|
||
log_error "❌ 后端编译失败"
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
# 构建前端
|
||
compile_web() {
|
||
log_step "构建前端dist"
|
||
cd "${WEB_PATH}"
|
||
|
||
log_info "清理旧的构建..."
|
||
rm -rf dist
|
||
|
||
log_info "开始构建前端项目..."
|
||
if npm run build; then
|
||
FILE_COUNT=$(find dist -type f | wc -l)
|
||
DIST_SIZE=$(du -sh dist | cut -f1)
|
||
log_info "✅ 前端构建成功: ${FILE_COUNT}个文件, ${DIST_SIZE}"
|
||
else
|
||
log_error "❌ 前端构建失败"
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
# 保存镜像
|
||
save_images() {
|
||
log_step "保存Docker镜像"
|
||
cd "${BUILD_OUTPUT}"
|
||
|
||
case ${BUILD_TARGET} in
|
||
base-serv)
|
||
save_image "school-news-base-serv"
|
||
;;
|
||
mysql)
|
||
save_image "school-news-mysql"
|
||
;;
|
||
serv)
|
||
save_image "school-news-serv"
|
||
;;
|
||
web)
|
||
save_image "school-news-web"
|
||
;;
|
||
all)
|
||
save_image "school-news-mysql"
|
||
save_image "school-news-serv"
|
||
save_image "school-news-web"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# 保存单个镜像(使用指定版本或当前构建版本)
|
||
save_image() {
|
||
local IMAGE_NAME=$1
|
||
local VERSION="${SAVE_VERSION}"
|
||
local IMAGE_FILE="${IMAGE_NAME}_${VERSION}.tar"
|
||
|
||
# 检查镜像是否存在
|
||
if ! docker images | grep -q "${IMAGE_NAME}.*${VERSION}"; then
|
||
log_error "❌ 镜像不存在: ${IMAGE_NAME}:${VERSION}"
|
||
log_error " 请先构建该版本的镜像,或使用 docker images 查看可用版本"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "保存${IMAGE_NAME}:${VERSION}镜像..."
|
||
if docker save -o "${IMAGE_FILE}" ${IMAGE_NAME}:${VERSION}; then
|
||
IMAGE_SIZE=$(du -h "${IMAGE_FILE}" | cut -f1)
|
||
log_info "✅ 镜像已保存: ${IMAGE_SIZE}"
|
||
log_info " 版本: ${IMAGE_NAME}:${VERSION}"
|
||
log_info " 文件: ${BUILD_OUTPUT}/${IMAGE_FILE}"
|
||
else
|
||
log_error "❌ 镜像保存失败: ${IMAGE_NAME}:${VERSION}"
|
||
exit 1
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
# ================================================
|
||
# 主流程控制
|
||
# ================================================
|
||
|
||
# Git更新(可选)
|
||
if [ "${DO_BUILD}" = true ]; then
|
||
log_step "Git Pull"
|
||
cd "${PROJECT_ROOT}"
|
||
|
||
if [[ $(git status --porcelain) ]]; then
|
||
log_warn "检测到未提交的更改"
|
||
read -p "是否继续拉取代码?(y/n): " -n 1 -r
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
log_warn "跳过git pull"
|
||
else
|
||
git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true
|
||
fi
|
||
else
|
||
git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
# 执行编译
|
||
if [ "${DO_BUILD}" = true ]; then
|
||
case ${BUILD_TARGET} in
|
||
serv|all)
|
||
compile_serv
|
||
;& # fallthrough
|
||
web|all)
|
||
compile_web
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
# 执行镜像构建
|
||
case ${BUILD_TARGET} in
|
||
base-serv)
|
||
build_base_serv
|
||
;;
|
||
mysql)
|
||
build_mysql
|
||
;;
|
||
serv)
|
||
build_serv
|
||
;;
|
||
web)
|
||
build_web
|
||
;;
|
||
all)
|
||
build_mysql
|
||
build_serv
|
||
build_web
|
||
;;
|
||
*)
|
||
log_error "未知的构建目标: ${BUILD_TARGET}"
|
||
echo ""
|
||
echo "可用的构建目标:"
|
||
echo " base-serv - 构建后端基础镜像(包含Python依赖)"
|
||
echo " mysql - 构建MySQL镜像"
|
||
echo " serv - 构建后端服务镜像"
|
||
echo " web - 构建前端服务镜像(基于node:20-alpine)"
|
||
echo " all - 构建所有应用镜像(默认)"
|
||
echo ""
|
||
echo "注意:"
|
||
echo " - Nginx使用官方镜像 nginx:alpine,无需构建"
|
||
echo " - Web基于官方Node镜像 node:20-alpine,无需base-web"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
# 查看镜像信息
|
||
log_info "Docker镜像列表:"
|
||
docker images | grep -E "school-news-" | head -15
|
||
echo ""
|
||
|
||
# 保存镜像
|
||
if [ "${DO_SAVE}" = true ]; then
|
||
save_images
|
||
fi
|
||
|
||
# ================================================
|
||
# 构建完成
|
||
# ================================================
|
||
echo "=========================================="
|
||
log_info "✅ 构建完成!"
|
||
echo "=========================================="
|
||
log_info "构建目标: ${BUILD_TARGET}"
|
||
log_info "构建版本: ${IMAGE_VERSION}"
|
||
log_info "输出目录: ${BUILD_OUTPUT}"
|
||
echo "=========================================="
|