From edde7afdc8bfd6af85a1b0b2309462ed4c4a47e1 Mon Sep 17 00:00:00 2001 From: Kerwin Date: Wed, 20 Aug 2025 17:16:34 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILD.md | 130 +++++++++++++++++++++++++++++ Dockerfile | 23 +++++- main.go | 13 +++ scripts/build.sh | 178 ++++++++++++++++++++++++++++++++++++++++ scripts/docker-build.sh | 155 ++++++++++++++++++++++++++++++++++ utils/version.go | 29 +++++-- 6 files changed, 519 insertions(+), 9 deletions(-) create mode 100644 BUILD.md create mode 100644 scripts/build.sh create mode 100644 scripts/docker-build.sh diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..051bad5 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,130 @@ +# 编译说明 + +## 方案1:使用编译脚本(推荐) + +### 在Git Bash中执行: + +```bash +# 给脚本添加执行权限(首次使用) +chmod +x scripts/build.sh + +# 编译Linux版本(推荐,用于服务器部署) +./scripts/build.sh + +# 或者明确指定编译Linux版本 +./scripts/build.sh build-linux + +# 或者指定目标文件名 +./scripts/build.sh build-linux myapp + +# 编译当前平台版本(用于本地测试) +./scripts/build.sh build +``` + +### 编译脚本功能: +- 自动读取 `VERSION` 文件中的版本号 +- 自动获取Git提交信息和分支信息 +- 自动获取构建时间 +- 将版本信息编译到可执行文件中 +- 支持跨平台编译(默认编译Linux版本) +- 使用静态链接,适合服务器部署 + +## 方案2:手动编译 + +### Linux版本(推荐): + +```bash +# 获取版本信息 +VERSION=$(cat VERSION) +GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") +GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") +BUILD_TIME=$(date '+%Y-%m-%d %H:%M:%S') + +# 编译Linux版本 +CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags "-X 'github.com/ctwj/urldb/utils.Version=${VERSION}' -X 'github.com/ctwj/urldb/utils.BuildTime=${BUILD_TIME}' -X 'github.com/ctwj/urldb/utils.GitCommit=${GIT_COMMIT}' -X 'github.com/ctwj/urldb/utils.GitBranch=${GIT_BRANCH}'" -o main . +``` + +### 当前平台版本: + +```bash +# 获取版本信息 +VERSION=$(cat VERSION) +GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") +GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") +BUILD_TIME=$(date '+%Y-%m-%d %H:%M:%S') + +# 编译当前平台版本 +go build -ldflags "-X 'github.com/ctwj/urldb/utils.Version=${VERSION}' -X 'github.com/ctwj/urldb/utils.BuildTime=${BUILD_TIME}' -X 'github.com/ctwj/urldb/utils.GitCommit=${GIT_COMMIT}' -X 'github.com/ctwj/urldb/utils.GitBranch=${GIT_BRANCH}'" -o main . +``` + +## 验证版本信息 + +编译完成后,可以通过以下方式验证版本信息: + +```bash +# 命令行验证 +./main version + +# 启动服务器后通过API验证 +curl http://localhost:8080/api/version +``` + +## 部署说明 + +使用方案1编译后,部署时只需要: + +1. 复制可执行文件到服务器 +2. 启动程序 + +**不再需要复制 `VERSION` 文件**,因为版本信息已经编译到程序中。 + +### 使用部署脚本(可选) + +```bash +# 给部署脚本添加执行权限 +chmod +x scripts/deploy-example.sh + +# 部署到服务器 +./scripts/deploy-example.sh root example.com /opt/urldb +``` + +### 使用Docker构建脚本: + +```bash +# 给脚本添加执行权限 +chmod +x scripts/docker-build.sh + +# 构建Docker镜像 +./scripts/docker-build.sh build + +# 构建指定版本镜像 +./scripts/docker-build.sh build 1.2.4 + +# 推送镜像到Docker Hub +./scripts/docker-build.sh push 1.2.4 +``` + +### 手动Docker构建: + +```bash +# 构建镜像 +docker build --target backend -t ctwj/urldb-backend:1.2.3 . +docker build --target frontend -t ctwj/urldb-frontend:1.2.3 . +``` + +## 版本管理 + +更新版本号: + +```bash +# 更新版本号 +./scripts/version.sh patch # 修订版本 +./scripts/version.sh minor # 次版本 +./scripts/version.sh major # 主版本 + +# 然后重新编译 +./scripts/build.sh + +# 或者构建Docker镜像 +./scripts/docker-build.sh build +``` \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 8de9444..934e735 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,11 +28,26 @@ WORKDIR /app COPY go.mod go.sum ./ RUN go mod download -# 先复制VERSION文件,确保构建时能正确读取版本号 -COPY VERSION ./ - +# 复制所有源代码 COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . + +# 定义构建参数 +ARG VERSION +ARG GIT_COMMIT +ARG GIT_BRANCH +ARG BUILD_TIME + +# 获取版本信息并编译 +RUN VERSION=${VERSION:-$(cat VERSION)} && \ + GIT_COMMIT=${GIT_COMMIT:-$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")} && \ + GIT_BRANCH=${GIT_BRANCH:-$(git branch --show-current 2>/dev/null || echo "unknown")} && \ + BUILD_TIME=${BUILD_TIME:-$(date '+%Y-%m-%d %H:%M:%S')} && \ + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \ + -ldflags "-X 'github.com/ctwj/urldb/utils.Version=${VERSION}' \ + -X 'github.com/ctwj/urldb/utils.BuildTime=${BUILD_TIME}' \ + -X 'github.com/ctwj/urldb/utils.GitCommit=${GIT_COMMIT}' \ + -X 'github.com/ctwj/urldb/utils.GitBranch=${GIT_BRANCH}'" \ + -o main . # 后端运行阶段 FROM alpine:latest AS backend diff --git a/main.go b/main.go index 06b8b7d..40c8fb9 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "log" "os" "strings" @@ -18,6 +19,18 @@ import ( ) func main() { + // 检查命令行参数 + if len(os.Args) > 1 && os.Args[1] == "version" { + versionInfo := utils.GetVersionInfo() + fmt.Printf("版本: v%s\n", versionInfo.Version) + fmt.Printf("构建时间: %s\n", versionInfo.BuildTime.Format("2006-01-02 15:04:05")) + fmt.Printf("Git提交: %s\n", versionInfo.GitCommit) + fmt.Printf("Git分支: %s\n", versionInfo.GitBranch) + fmt.Printf("Go版本: %s\n", versionInfo.GoVersion) + fmt.Printf("平台: %s/%s\n", versionInfo.Platform, versionInfo.Arch) + return + } + // 初始化日志系统 if err := utils.InitLogger(nil); err != nil { log.Fatal("初始化日志系统失败:", err) diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..fdeb52f --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,178 @@ +#!/bin/bash + +# 编译脚本 - 自动注入版本信息 +# 用法: ./scripts/build.sh [target] + +set -e + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 获取当前版本 +get_current_version() { + cat VERSION +} + +# 获取Git信息 +get_git_commit() { + git rev-parse --short HEAD 2>/dev/null || echo "unknown" +} + +get_git_branch() { + git branch --show-current 2>/dev/null || echo "unknown" +} + +# 获取构建时间 +get_build_time() { + date '+%Y-%m-%d %H:%M:%S' +} + +# 编译函数 +build() { + local target=${1:-"main"} + local version=$(get_current_version) + local git_commit=$(get_git_commit) + local git_branch=$(get_git_branch) + local build_time=$(get_build_time) + + echo -e "${BLUE}开始编译...${NC}" + echo -e "版本: ${GREEN}${version}${NC}" + echo -e "Git提交: ${GREEN}${git_commit}${NC}" + echo -e "Git分支: ${GREEN}${git_branch}${NC}" + echo -e "构建时间: ${GREEN}${build_time}${NC}" + + # 构建 ldflags + local ldflags="-X 'github.com/ctwj/urldb/utils.Version=${version}'" + ldflags="${ldflags} -X 'github.com/ctwj/urldb/utils.BuildTime=${build_time}'" + ldflags="${ldflags} -X 'github.com/ctwj/urldb/utils.GitCommit=${git_commit}'" + ldflags="${ldflags} -X 'github.com/ctwj/urldb/utils.GitBranch=${git_branch}'" + + # 编译 - 使用跨平台编译设置 + echo -e "${YELLOW}编译中...${NC}" + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags "${ldflags}" -o "${target}" . + + if [ $? -eq 0 ]; then + echo -e "${GREEN}编译成功!${NC}" + echo -e "可执行文件: ${GREEN}${target}${NC}" + echo -e "目标平台: ${GREEN}Linux${NC}" + + # 显示版本信息(在Linux环境下) + echo -e "${BLUE}版本信息验证:${NC}" + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + ./${target} version 2>/dev/null || echo "无法验证版本信息" + else + echo "当前非Linux环境,无法直接验证版本信息" + echo "请将编译后的文件复制到Linux服务器上验证" + fi + else + echo -e "${RED}编译失败!${NC}" + exit 1 + fi +} + +# 清理函数 +clean() { + echo -e "${YELLOW}清理编译文件...${NC}" + rm -f main + echo -e "${GREEN}清理完成${NC}" +} + +# 显示帮助 +show_help() { + echo -e "${BLUE}编译脚本${NC}" + echo "" + echo "用法: $0 [命令]" + echo "" + echo "命令:" + echo " build [target] 编译程序 (当前平台)" + echo " build-linux [target] 编译Linux版本 (推荐)" + echo " clean 清理编译文件" + echo " help 显示此帮助信息" + echo "" + echo "示例:" + echo " $0 # 编译Linux版本 (默认)" + echo " $0 build-linux # 编译Linux版本" + echo " $0 build-linux app # 编译Linux版本为 app" + echo " $0 build # 编译当前平台版本" + echo " $0 clean # 清理编译文件" + echo "" + echo "注意:" + echo " - Linux版本使用静态链接,适合部署到服务器" + echo " - 默认编译Linux版本,无需复制VERSION文件" +} + +# Linux编译函数 +build_linux() { + local target=${1:-"main"} + local version=$(get_current_version) + local git_commit=$(get_git_commit) + local git_branch=$(get_git_branch) + local build_time=$(get_build_time) + + echo -e "${BLUE}开始Linux编译...${NC}" + echo -e "版本: ${GREEN}${version}${NC}" + echo -e "Git提交: ${GREEN}${git_commit}${NC}" + echo -e "Git分支: ${GREEN}${git_branch}${NC}" + echo -e "构建时间: ${GREEN}${build_time}${NC}" + + # 构建 ldflags + local ldflags="-X 'github.com/ctwj/urldb/utils.Version=${version}'" + ldflags="${ldflags} -X 'github.com/ctwj/urldb/utils.BuildTime=${build_time}'" + ldflags="${ldflags} -X 'github.com/ctwj/urldb/utils.GitCommit=${git_commit}'" + ldflags="${ldflags} -X 'github.com/ctwj/urldb/utils.GitBranch=${git_branch}'" + + # Linux编译 + echo -e "${YELLOW}编译中...${NC}" + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags "${ldflags}" -o "${target}" . + + if [ $? -eq 0 ]; then + echo -e "${GREEN}Linux编译成功!${NC}" + echo -e "可执行文件: ${GREEN}${target}${NC}" + echo -e "目标平台: ${GREEN}Linux${NC}" + echo -e "静态链接: ${GREEN}是${NC}" + + # 显示文件信息 + if command -v file >/dev/null 2>&1; then + echo -e "${BLUE}文件信息:${NC}" + file "${target}" + fi + + echo -e "${BLUE}注意: 请在Linux服务器上验证版本信息${NC}" + else + echo -e "${RED}Linux编译失败!${NC}" + exit 1 + fi +} + +# 主函数 +main() { + case $1 in + "build") + build $2 + ;; + "build-linux") + build_linux $2 + ;; + "clean") + clean + ;; + "help"|"-h"|"--help") + show_help + ;; + "") + build_linux + ;; + *) + echo -e "${RED}错误: 未知命令 '$1'${NC}" + echo "使用 '$0 help' 查看帮助信息" + exit 1 + ;; + esac +} + +# 运行主函数 +main "$@" \ No newline at end of file diff --git a/scripts/docker-build.sh b/scripts/docker-build.sh new file mode 100644 index 0000000..4b06df6 --- /dev/null +++ b/scripts/docker-build.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +# Docker构建脚本 +# 用法: ./scripts/docker-build.sh [version] + +set -e + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 获取版本号 +get_version() { + if [ -n "$1" ]; then + echo "$1" + else + cat VERSION + fi +} + +# 获取Git信息 +get_git_commit() { + git rev-parse --short HEAD 2>/dev/null || echo "unknown" +} + +get_git_branch() { + git branch --show-current 2>/dev/null || echo "unknown" +} + +# 构建Docker镜像 +build_docker() { + local version=$(get_version $1) + local git_commit=$(get_git_commit) + local git_branch=$(get_git_branch) + local build_time=$(date '+%Y-%m-%d %H:%M:%S') + + echo -e "${BLUE}开始Docker构建...${NC}" + echo -e "版本: ${GREEN}${version}${NC}" + echo -e "Git提交: ${GREEN}${git_commit}${NC}" + echo -e "Git分支: ${GREEN}${git_branch}${NC}" + echo -e "构建时间: ${GREEN}${build_time}${NC}" + + # 直接使用 docker build,避免 buildx 的复杂性 + BUILD_CMD="docker build" + echo -e "${BLUE}使用构建命令: ${BUILD_CMD}${NC}" + + # 构建前端镜像 + echo -e "${YELLOW}构建前端镜像...${NC}" + FRONTEND_CMD="${BUILD_CMD} --build-arg VERSION=${version} --build-arg GIT_COMMIT=${git_commit} --build-arg GIT_BRANCH=${git_branch} --build-arg BUILD_TIME=${build_time} --target frontend -t ctwj/urldb-frontend:${version} ." + echo -e "${BLUE}执行命令: ${FRONTEND_CMD}${NC}" + ${BUILD_CMD} \ + --build-arg VERSION=${version} \ + --build-arg GIT_COMMIT=${git_commit} \ + --build-arg GIT_BRANCH=${git_branch} \ + --build-arg "BUILD_TIME=${build_time}" \ + --target frontend \ + -t ctwj/urldb-frontend:${version} \ + . + + # 构建后端镜像 + echo -e "${YELLOW}构建后端镜像...${NC}" + BACKEND_CMD="${BUILD_CMD} --build-arg VERSION=${version} --build-arg GIT_COMMIT=${git_commit} --build-arg GIT_BRANCH=${git_branch} --build-arg BUILD_TIME=${build_time} --target backend -t ctwj/urldb-backend:${version} ." + echo -e "${BLUE}执行命令: ${BACKEND_CMD}${NC}" + ${BUILD_CMD} \ + --build-arg VERSION=${version} \ + --build-arg GIT_COMMIT=${git_commit} \ + --build-arg GIT_BRANCH=${git_branch} \ + --build-arg BUILD_TIME="${build_time}" \ + --target backend \ + -t ctwj/urldb-backend:${version} \ + . + + + echo -e "${GREEN}Docker构建完成!${NC}" + echo -e "镜像标签:" + echo -e " ${GREEN}ctwj/urldb-backend:${version}${NC}" + echo -e " ${GREEN}ctwj/urldb-frontend:${version}${NC}" +} + +# 推送镜像 +push_images() { + local version=$(get_version $1) + + echo -e "${YELLOW}推送镜像到Docker Hub...${NC}" + + # 推送后端镜像 + docker push ctwj/urldb-backend:${version} + + # 推送前端镜像 + docker push ctwj/urldb-frontend:${version} + + echo -e "${GREEN}镜像推送完成!${NC}" +} + +# 清理镜像 +clean_images() { + local version=$(get_version $1) + + echo -e "${YELLOW}清理Docker镜像...${NC}" + docker rmi ctwj/urldb-backend:${version} 2>/dev/null || true + docker rmi ctwj/urldb-frontend:${version} 2>/dev/null || true + + echo -e "${GREEN}镜像清理完成${NC}" +} + +# 显示帮助 +show_help() { + echo -e "${BLUE}Docker构建脚本${NC}" + echo "" + echo "用法: $0 [命令] [版本]" + echo "" + echo "命令:" + echo " build [version] 构建Docker镜像" + echo " push [version] 推送镜像到Docker Hub" + echo " clean [version] 清理Docker镜像" + echo " help 显示此帮助信息" + echo "" + echo "示例:" + echo " $0 build # 构建当前版本镜像" + echo " $0 build 1.2.4 # 构建指定版本镜像" + echo " $0 push 1.2.4 # 推送指定版本镜像" + echo " $0 clean # 清理当前版本镜像" +} + +# 主函数 +main() { + case $1 in + "build") + build_docker $2 + ;; + "push") + push_images $2 + ;; + "clean") + clean_images $2 + ;; + "help"|"-h"|"--help") + show_help + ;; + "") + show_help + ;; + *) + echo -e "${RED}错误: 未知命令 '$1'${NC}" + echo "使用 '$0 help' 查看帮助信息" + exit 1 + ;; + esac +} + +# 运行主函数 +main "$@" \ No newline at end of file diff --git a/utils/version.go b/utils/version.go index 047d38a..f80403d 100644 --- a/utils/version.go +++ b/utils/version.go @@ -23,13 +23,14 @@ type VersionInfo struct { // 编译时注入的版本信息 var ( - Version = getVersionFromFile() + // 这些变量将在编译时通过 ldflags 注入 + Version = "unknown" // 默认版本,编译时会被覆盖 BuildTime = GetCurrentTimeString() GitCommit = "unknown" GitBranch = "unknown" ) -// getVersionFromFile 从VERSION文件读取版本号 +// getVersionFromFile 从VERSION文件读取版本号(备用方案) func getVersionFromFile() string { data, err := os.ReadFile("VERSION") if err != nil { @@ -42,11 +43,29 @@ func getVersionFromFile() string { func GetVersionInfo() *VersionInfo { buildTime, _ := ParseTime(BuildTime) + // 检查版本信息是否通过编译时注入 + version := Version + gitCommit := GitCommit + gitBranch := GitBranch + + // 如果编译时注入的版本是默认值,尝试从文件读取 + if version == "unknown" { + version = getVersionFromFile() + } + + // 如果Git信息是默认值,尝试从文件读取 + if gitCommit == "unknown" { + gitCommit = "unknown" + } + if gitBranch == "unknown" { + gitBranch = "unknown" + } + return &VersionInfo{ - Version: Version, + Version: version, BuildTime: buildTime, - GitCommit: GitCommit, - GitBranch: GitBranch, + GitCommit: gitCommit, + GitBranch: gitBranch, GoVersion: runtime.Version(), NodeVersion: getNodeVersion(), Platform: runtime.GOOS,