<# .SYNOPSIS 统一构建脚本 - 符合大厂标准的微服务构建流程 .DESCRIPTION 功能: 1. 加载环境配置文件 2. 参数验证与安全检查 3. 同步Maven项目版本与镜像Tag 4. 更新服务版本配置 5. 并行构建后端服务 6. 构建并推送Docker镜像 .PARAMETER Services 指定要构建的服务列表,默认全部构建 .PARAMETER SkipTests 是否跳过测试 .PARAMETER Push 是否推送镜像 .PARAMETER Env 目标环境(dev/test/prod),默认从配置文件读取 .PARAMETER Registry 镜像仓库地址,默认从配置文件读取 .PARAMETER Tag 版本号标签(如 v1.18.0) .EXAMPLE # 构建所有服务并推送 .\scripts\build-all.ps1 -Push -Tag "v1.18.0" -Registry "registry.fengzhongxing.com:5000/fzx" -Env "dev" # 指定环境构建 .\scripts\build-all.ps1 -Env prod -Push -Tag "v1.18.0" -Registry "registry.fengzhongxing.com:5000/fzx" # 只构建特定服务 .\scripts\build-all.ps1 -Services auth-service,order-service -SkipTests -Tag "v1.18.0" -Registry "registry.fengzhongxing.com:5000/fzx" -Env "dev" #> param( [string[]]$Services = @("api-gateway", "auth-service", "user-service", "order-service", "product-service", "merchant-service", "rider-service", "map-service", "marketing-service", "upload-service"), [switch]$SkipTests = $false, [switch]$Push = $false, [string]$Env = $null, [string]$Registry = $null, [string]$Tag = $null ) #region === 初始化配置 === $scriptPath = $MyInvocation.MyCommand.Definition $projectRoot = Split-Path (Split-Path $scriptPath -Parent) -Parent Set-Location $projectRoot $envConfigPath = "$projectRoot\infrastructure\compose\.env.aliyun-dev" $logPath = "$projectRoot\logs\build" New-Item -ItemType Directory -Path $logPath -Force | Out-Null $buildLogFile = "$logPath\build-$(Get-Date -Format 'yyyyMMdd-HHmmss').log" $errorLogFile = "$logPath\build-error-$(Get-Date -Format 'yyyyMMdd-HHmmss').log" # 自定义日志函数 function Write-Log { param( [string]$Message, [string]$Level = "INFO", [ConsoleColor]$Color = [ConsoleColor]::White ) $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $logMessage = "[$timestamp] [$Level] $Message" Add-Content -Path $buildLogFile -Value $logMessage Write-Host $logMessage -ForegroundColor $Color } function Write-ErrorLog { param([string]$Message) $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $logMessage = "[$timestamp] [ERROR] $Message" Add-Content -Path $errorLogFile -Value $logMessage Write-Host $logMessage -ForegroundColor Red } #endregion #region === 参数验证 === Write-Log "======================================" "INFO" Cyan Write-Log "🔍 参数验证与配置加载" "INFO" Cyan Write-Log "======================================" "INFO" Cyan # 验证必要工具是否存在 $requiredTools = @("podman", "mvn", "git") foreach ($tool in $requiredTools) { if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) { Write-ErrorLog "必需工具 $tool 未安装或不在PATH中" exit 1 } } Write-Log "✅ 所有必需工具已就绪" "INFO" Green # 加载环境配置 if (Test-Path $envConfigPath) { Write-Log "📄 加载环境配置: $envConfigPath" "INFO" Cyan $configContent = Get-Content $envConfigPath -Raw $configContent | Select-String -Pattern '^[A-Z_]+=' | ForEach-Object { $line = $_.Line.Trim() if ($line -match '^([A-Z_]+)=(.*)$') { $name = $matches[1] $value = $matches[2] -replace '"', '' if (-not (Get-Variable -Name "env:$name" -ErrorAction SilentlyContinue)) { Set-Variable -Name "env:$name" -Value $value } } } } # 配置优先级:命令行参数 > 环境变量 > 默认值 $IMAGE_REGISTRY = if ($Registry) { $Registry } elseif ($env:IMAGE_REGISTRY) { $env:IMAGE_REGISTRY } else { "registry.fengzhongxing.com:5000/fzx" } $IMAGE_ENV = if ($Env) { $Env } elseif ($env:IMAGE_ENV) { $env:IMAGE_ENV } else { "dev" } $IMAGE_TAG_PREFIX = $env:IMAGE_TAG_PREFIX ? $env:IMAGE_TAG_PREFIX : "v" Write-Log "📦 镜像仓库: $IMAGE_REGISTRY" "INFO" Cyan Write-Log "🌍 目标环境: $IMAGE_ENV" "INFO" Cyan # 验证服务列表 $validServices = Get-ChildItem -Path "$projectRoot\services" -Directory | Select-Object -ExpandProperty Name foreach ($service in $Services) { if ($service -notin $validServices) { Write-ErrorLog "无效服务: $service,可选服务: $($validServices -join ', ')" exit 1 } } Write-Log "✅ 服务列表验证通过: $($Services -join ', ')" "INFO" Green #endregion #region === 版本计算 === Write-Log "" "INFO" Write-Log "======================================" "INFO" Cyan Write-Log "📊 版本号确定" "INFO" Cyan Write-Log "======================================" "INFO" Cyan if ($Tag) { # 使用命令行指定的版本号 if ($Tag -match '^v?\d+\.\d+\.\d+$') { $VERSION = $Tag -replace '^v', '' $IMAGE_TAG = $Tag Write-Log "🎯 使用命令行指定版本: v$VERSION" "INFO" Green } else { Write-ErrorLog "无效的版本号格式: $Tag,格式应为 vX.Y.Z 或 X.Y.Z" exit 1 } } else { # 自动计算版本号 try { $versionOutput = & "$projectRoot\scripts\auto-version.ps1" 2>&1 if ($LASTEXITCODE -ne 0) { throw "版本计算脚本执行失败: $versionOutput" } $VERSION = $versionOutput | Where-Object { $_ -match '^\d+\.\d+\.\d+$' } if (-not $VERSION) { $VERSION = "1.16.0" Write-Log "⚠️ 获取版本失败,使用默认版本 v$VERSION" "WARN" Yellow } else { Write-Log "🎯 自动计算版本: v$VERSION" "INFO" Green } $IMAGE_TAG = "${IMAGE_TAG_PREFIX}${VERSION}" } catch { Write-ErrorLog "版本计算失败: $_" exit 1 } } #endregion #region === 更新版本配置(同步Maven版本与镜像Tag)=== Write-Log "" "INFO" Write-Log "======================================" "INFO" Cyan Write-Log "📝 同步更新Maven项目版本" "INFO" Cyan Write-Log "======================================" "INFO" Cyan foreach ($service in $Services) { $servicePath = "$projectRoot\services\$service" $pomPath = "$servicePath\pom.xml" if (Test-Path $pomPath) { try { Write-Log "🔄 同步 $service pom.xml 版本为 $VERSION..." "INFO" Cyan # 使用Maven versions插件更新版本号(大厂标准做法) Push-Location $servicePath mvn versions:set -DnewVersion=$VERSION -DprocessAllModules=true -DgenerateBackupPoms=false -q if ($LASTEXITCODE -ne 0) { Write-ErrorLog "更新 $service pom.xml 版本失败" Pop-Location exit 1 } Pop-Location Write-Log "✅ $service Maven版本已更新为 $VERSION" "INFO" Green } catch { Write-ErrorLog "更新 $service pom.xml 失败: $_" exit 1 } } } #endregion #region === 更新Compose配置 === Write-Log "" "INFO" Write-Log "======================================" "INFO" Cyan Write-Log "📝 更新Compose文件版本号" "INFO" Cyan Write-Log "======================================" "INFO" Cyan $composePath = "$projectRoot\infrastructure\compose\podman-compose.aliyun-dev.yml" if (Test-Path $composePath) { try { Write-Log "🔄 更新 podman-compose.aliyun-dev.yml..." "INFO" Cyan $content = Get-Content $composePath -Raw -ErrorAction Stop $content = $content -replace "IMAGE_TAG=v\d+\.\d+\.\d+", "IMAGE_TAG=$IMAGE_TAG" $content | Set-Content $composePath -NoNewline -ErrorAction Stop Write-Log "✅ Compose文件版本已更新为 $IMAGE_TAG" "INFO" Green } catch { Write-ErrorLog "更新Compose文件失败: $_" exit 1 } } # 更新环境变量文件 try { Write-Log "🔄 更新 .env.aliyun 版本号..." "INFO" Cyan $content = Get-Content $envConfigPath -Raw -ErrorAction Stop $content = $content -replace "IMAGE_TAG=v\d+\.\d+\.\d+", "IMAGE_TAG=$IMAGE_TAG" $content | Set-Content $envConfigPath -NoNewline -ErrorAction Stop Write-Log "✅ .env.aliyun 版本已更新" "INFO" Green } catch { Write-ErrorLog "更新环境配置文件失败: $_" exit 1 } #endregion #region === 构建后端服务 === Write-Log "" "INFO" Write-Log "======================================" "INFO" Cyan Write-Log "🔨 构建后端服务" "INFO" Cyan Write-Log "======================================" "INFO" Cyan $testArg = if ($SkipTests) { "-DskipTests" } else { "" } $failedServices = @() foreach ($service in $Services) { $servicePath = "$projectRoot\services\$service" if (Test-Path $servicePath) { Write-Log "🔧 构建 $service..." "INFO" Cyan Push-Location $servicePath mvn clean package $testArg -q if ($LASTEXITCODE -ne 0) { Write-ErrorLog "$service 构建失败!" $failedServices += $service } else { Write-Log "✅ $service 构建成功" "INFO" Green } Pop-Location } } Set-Location $projectRoot if ($failedServices.Count -gt 0) { Write-ErrorLog "以下服务构建失败: $($failedServices -join ', ')" exit 1 } #endregion #region === 构建Docker镜像 === Write-Log "" "INFO" Write-Log "======================================" "INFO" Cyan Write-Log "🐳 构建Docker镜像" "INFO" Cyan Write-Log "======================================" "INFO" Cyan $failedImages = @() foreach ($service in $Services) { $servicePath = "$projectRoot\services\$service" if (Test-Path $servicePath) { $imageTag = "${IMAGE_REGISTRY}/${service}-${IMAGE_ENV}:${IMAGE_TAG}" Write-Log "🔧 构建 $service 镜像: $imageTag" "INFO" Cyan podman build -t $imageTag $servicePath if ($LASTEXITCODE -ne 0) { Write-ErrorLog "$service 镜像构建失败!" $failedImages += $service } else { Write-Log "✅ $service 镜像构建成功" "INFO" Green } } } if ($failedImages.Count -gt 0) { Write-ErrorLog "以下镜像构建失败: $($failedImages -join ', ')" exit 1 } #endregion #region === 推送镜像 === if ($Push) { Write-Log "" "INFO" Write-Log "======================================" "INFO" Cyan Write-Log "📤 推送镜像到仓库" "INFO" Cyan Write-Log "======================================" "INFO" Cyan # 确保已登录 Write-Log "🔐 检查仓库登录状态..." "INFO" Cyan podman login --tls-verify=false -u $env:REGISTRY_USERNAME -p $env:REGISTRY_PASSWORD $env:REGISTRY_HOST 2>&1 | Out-Null if ($LASTEXITCODE -ne 0) { Write-ErrorLog "仓库登录失败,请检查凭证配置" exit 1 } Write-Log "✅ 仓库登录成功" "INFO" Green $failedPushes = @() foreach ($service in $Services) { $imageTag = "${IMAGE_REGISTRY}/${service}-${IMAGE_ENV}:${IMAGE_TAG}" Write-Log "📤 推送 $service..." "INFO" Cyan podman push --tls-verify=false $imageTag if ($LASTEXITCODE -ne 0) { Write-ErrorLog "$service 推送失败!" $failedPushes += $service } else { Write-Log "✅ $service 推送成功" "INFO" Green } } if ($failedPushes.Count -gt 0) { Write-ErrorLog "以下镜像推送失败: $($failedPushes -join ', ')" exit 1 } } #endregion #region === 构建完成 === Write-Log "" "INFO" Write-Log "======================================" "INFO" Green Write-Log "🎉 构建完成!" "INFO" Green Write-Log "版本: $IMAGE_TAG" "INFO" Green Write-Log "环境: $IMAGE_ENV" "INFO" Green Write-Log "构建服务: $($Services -join ', ')" "INFO" Green Write-Log "镜像仓库: $IMAGE_REGISTRY" "INFO" Green Write-Log "日志文件: $buildLogFile" "INFO" Green Write-Log "=======================================" "INFO" Green #endregion