# ============================================================ # 本机一键打包脚本(Windows PowerShell) # 流程: go build(linux/amd64) -> web/frontend 两个 vite build # -> 渲染 web/nginx.k8s.conf(去 BOM) -> docker build -> push Harbor # # 用法: # .\deploy\build.ps1 # 打包当前代码并推送 (tag = commit sha + latest) # .\deploy\build.ps1 -Tag v1.0.2 # 使用指定 tag # .\deploy\build.ps1 -NoPush # 只构建不推送 # # 依赖: go / node(corepack pnpm) / docker(Harbor 登录态已存于 Docker Desktop 凭据库) # ============================================================ param( [string]$Tag = "", [switch]$NoPush ) $ErrorActionPreference = 'Stop' # 脚本所在目录的上一级即项目根目录,任何 cwd 下运行都正确 $Root = Split-Path -Parent $PSScriptRoot $Registry = 'harbor.fuhua91.com' $Project = 'live' function Invoke-Step { param([string]$Name, [scriptblock]$Block) Write-Host "==> $Name" -ForegroundColor Cyan & $Block if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) { Write-Host "错误: $Name 失败 (exit $LASTEXITCODE)" -ForegroundColor Red exit $LASTEXITCODE } } # 前端包管理器: 本机 pnpm 通常不在 PATH,回退 corepack pnpm function Invoke-Pnpm { param([string]$Dir, [string[]]$PnpmArgs) Push-Location (Join-Path $Root $Dir) try { if (Get-Command pnpm -ErrorAction SilentlyContinue) { & pnpm @PnpmArgs } else { & corepack pnpm @PnpmArgs } if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } finally { Pop-Location } } function Build-Frontend { param([string]$Dir) if (-not (Test-Path (Join-Path $Root "$Dir\node_modules"))) { Invoke-Step "[$Dir] pnpm install" { Invoke-Pnpm -Dir $Dir -PnpmArgs @('install', '--frozen-lockfile') } } Invoke-Step "[$Dir] vite build" { Invoke-Pnpm -Dir $Dir -PnpmArgs @('build') } } Set-Location $Root # ---------- 0. 版本号 ---------- if (-not $Tag) { $Tag = (& git rev-parse --short=12 HEAD).Trim() if ($LASTEXITCODE -ne 0) { Write-Host '错误: 无法获取 git commit sha' -ForegroundColor Red; exit 1 } } $BackendImage = "$Registry/$Project/backend:$Tag" $FrontendImage = "$Registry/$Project/frontend:$Tag" $LivewebImage = "$Registry/$Project/live-web:$Tag" Write-Host "==> 打包版本: $Tag" # ---------- 1. 后端二进制 (必须 linux/amd64,否则容器内 Exec format error) ---------- Invoke-Step 'go build (GOOS=linux GOARCH=amd64)' { $env:GOOS = 'linux'; $env:GOARCH = 'amd64'; $env:CGO_ENABLED = '0' go build -ldflags "-X main.Version=$Tag" -o ./bin/kratos-admin ./cmd } # ---------- 2. 两个前端 ---------- Build-Frontend 'web' # admin 管理后台 Build-Frontend 'frontend' # live 用户端(PC 直播页) # ---------- 3. 渲染 nginx.k8s.conf ---------- # PowerShell Set-Content 会写 UTF-8 BOM,导致 nginx 报 unknown directive; # 必须用 WriteAllBytes 写无 BOM 内容。KRA_APP=live -> proxy_pass http://live-backend:8000 Invoke-Step '渲染 web/nginx.k8s.conf (KRA_APP=live, 去 BOM)' { $src = Get-Content -Raw (Join-Path $Root 'web\.docker-compose\nginx\conf.d\my.conf') $rendered = $src -replace '\$\{KRA_APP\}', $Project $dst = Join-Path $Root 'web\nginx.k8s.conf' [System.IO.File]::WriteAllBytes($dst, [System.Text.Encoding]::UTF8.GetBytes($rendered)) if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } # 校验文件头非 EF BB BF $head = [System.IO.File]::ReadAllBytes($dst)[0..1] if ($head[0] -eq 0xEF -and $head[1] -eq 0xBB) { Write-Host '错误: nginx.k8s.conf 仍含 BOM' -ForegroundColor Red exit 1 } } # ---------- 4. docker build ---------- Invoke-Step "docker build $BackendImage" { docker build -t $BackendImage -t "$Registry/$Project/backend:latest" $Root } Invoke-Step "docker build $FrontendImage" { docker build -t $FrontendImage -t "$Registry/$Project/frontend:latest" (Join-Path $Root 'web') } Invoke-Step "docker build $LivewebImage" { docker build -t $LivewebImage -t "$Registry/$Project/live-web:latest" (Join-Path $Root 'frontend') } # ---------- 5. docker push ---------- if (-not $NoPush) { foreach ($img in @($BackendImage, $FrontendImage, $LivewebImage)) { Invoke-Step "docker push ${img}:$Tag + latest" { docker push $img docker push ($img -replace ":[^:]+$", ':latest') } } Write-Host "" Write-Host "完成。服务器上执行: ./deploy.sh $Tag (或不带参数拉 latest)" -ForegroundColor Green } else { Write-Host "" Write-Host "构建完成 (-NoPush 未推送)。镜像: $BackendImage / $FrontendImage / $LivewebImage" -ForegroundColor Green }