Files
schoolNews/build.sh
2025-11-26 14:13:17 +08:00

457 lines
13 KiB
Bash
Executable File
Raw 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.

#!/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_update_if_needed() {
# Git更新可选
if [ "${DO_BUILD}" != true ]; then
return
fi
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 ""
}
run_compile() {
# 执行编译
if [ "${DO_BUILD}" != true ]; then
return
fi
case ${BUILD_TARGET} in
serv)
compile_serv
;;
web)
compile_web
;;
all)
compile_serv
compile_web
;;
esac
}
run_build_images() {
# 执行镜像构建
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
}
show_images_info() {
# 查看镜像信息
log_info "Docker镜像列表:"
docker images | grep -E "school-news-" | head -15
echo ""
}
print_summary() {
# 构建完成
echo "=========================================="
log_info "✅ 构建完成!"
echo "=========================================="
log_info "构建目标: ${BUILD_TARGET}"
log_info "构建版本: ${IMAGE_VERSION}"
log_info "输出目录: ${BUILD_OUTPUT}"
echo "=========================================="
}
main() {
git_update_if_needed
run_compile
run_build_images
show_images_info
# 保存镜像(可选)
if [ "${DO_SAVE}" = true ]; then
save_images
fi
print_summary
}
main