Files
BuYuHouDuan/deploy/build-local.ps1
T

158 lines
7.4 KiB
PowerShell
Raw Normal View History

<#
本机构建镜像并导出 tar(编译在 Docker 容器内进行)
------------------------------------------------------------------
适用场景:服务器无法访问 mcr.microsoft.com;且本机杀软(如火绒)
会拦截/删除编译产物(AdminApi.dll 等)。
编译与镜像构建全部在容器内完成,Windows 杀软无法拦截中间产物。
用法:
# 1) 全量构建(7 个服务)
powershell -ExecutionPolicy Bypass -File deploy\build-local.ps1 -PublicHost game.xxx.com
# 2) 只构建/打包指定服务(多个逗号分隔)
powershell -ExecutionPolicy Bypass -File deploy\build-local.ps1 -PublicHost game.xxx.com -Services admin,fishgame
# 3) 按「改动的子项目」自动推导需要重建的服务(推荐,避免漏编)
powershell -ExecutionPolicy Bypass -File deploy\build-local.ps1 -PublicHost game.xxx.com -ForProject FishGameService
产物:deploy\dist\ 下的 tar(只含本次构建的镜像)
#>
param(
[string]$PublicHost = "CHANGE_ME",
[string]$Tag = (Get-Date -Format "yyyyMMdd-HHmm"),
[string[]]$Services = @(),
[string]$ForProject = "",
[switch]$DryRun
)
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent $PSScriptRoot
$Deploy = Join-Path $Root "deploy"
$Dist = Join-Path $Deploy "dist"
function Info($m) { Write-Host "[本地构建] $m" -ForegroundColor Green }
function Warn($m) { Write-Host "[警告] $m" -ForegroundColor Yellow }
# ---------- 服务定义:服务名 -> 项目文件 / 入口 dll / Host 项目名 ----------
$serviceMap = [ordered]@{
gateway = @{ proj = "Gateway/Gateway.csproj"; dll = "Gateway.dll"; host = "Gateway" }
admin = @{ proj = "AdminSrc/AdminHost/AdminHost.csproj"; dll = "AdminHost.dll"; host = "AdminHost" }
user = @{ proj = "UserSrc/UserHost/UserHost.csproj"; dll = "UserHost.dll"; host = "UserHost" }
customer = @{ proj = "CustomerSrc/CustomerHost/CustomerHost.csproj"; dll = "CustomerHost.dll"; host = "CustomerHost" }
chess = @{ proj = "ChessGameHost/ChessGameHost.csproj"; dll = "ChessGameHost.dll"; host = "ChessGameHost" }
fishroom = @{ proj = "FishGameSrc/FishRoomHost/FishRoomHost.csproj"; dll = "FishRoomHost.dll"; host = "FishRoomHost" }
fishgame = @{ proj = "FishGameSrc/FishGameHost/FishGameHost.csproj"; dll = "FishGameHost.dll"; host = "FishGameHost" }
}
# ---------- 依赖图:改动某项目 -> 需要重建的服务 ----------
function Get-AffectedServices([string]$changedProject) {
$refs = @{}
Get-ChildItem -Path $Root -Recurse -Filter *.csproj -ErrorAction SilentlyContinue | ForEach-Object {
$txt = [IO.File]::ReadAllText($_.FullName)
$deps = @()
foreach ($m in [regex]::Matches($txt, 'ProjectReference\s+Include="([^"]+)"')) {
$deps += [IO.Path]::GetFileNameWithoutExtension($m.Groups[1].Value)
}
$refs[$_.BaseName] = $deps
}
$result = @()
foreach ($name in $serviceMap.Keys) {
$hostName = $serviceMap[$name].host
if ($hostName -eq $changedProject) { $result += $name; continue }
$seen = New-Object System.Collections.Generic.HashSet[string]
$stack = New-Object System.Collections.Stack
foreach ($d in @($refs[$hostName])) { if ($d) { [void]$stack.Push($d) } }
while ($stack.Count -gt 0) {
$cur = $stack.Pop()
if (-not $seen.Add($cur)) { continue }
foreach ($d in @($refs[$cur])) { if ($d) { [void]$stack.Push($d) } }
}
if ($seen.Contains($changedProject)) { $result += $name }
}
return $result
}
# ---------- 1) 决定要构建哪些服务 ----------
if ($ForProject) {
$targets = @(Get-AffectedServices $ForProject)
if ($targets.Count -eq 0) {
throw "没有任何 Host 引用项目 '$ForProject'(项目名不带 .csproj)。请检查拼写。"
}
Info ("按项目 '$ForProject' 推导出需重建的服务: " + ($targets -join ', '))
} elseif ($Services.Count -gt 0) {
$svcList = @()
foreach ($s in $Services) { $svcList += ($s -split ',') }
$svcList = @($svcList | ForEach-Object { $_.Trim() } | Where-Object { $_ })
foreach ($s in $svcList) {
if (-not $serviceMap.Contains($s)) { throw "未知服务名 '$s'。可选: $($serviceMap.Keys -join ', ')" }
}
$targets = @($svcList)
Info ("指定构建服务: " + ($targets -join ', '))
} else {
$targets = @($serviceMap.Keys)
Info "全量构建 7 个服务"
}
if ($DryRun) {
Info ("[DryRun] 将构建的服务: " + ($targets -join ', '))
exit 0
}
# ---------- 2) 前置校验 + 准备基础镜像(经镜像站拉取并打回官方 tag)----------
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { throw "未找到 docker,请先安装并启动 Docker Desktop" }
$prevEap = $ErrorActionPreference
$ErrorActionPreference = "Continue"
docker info 2>&1 | Out-Null
$dockerOk = ($LASTEXITCODE -eq 0)
$ErrorActionPreference = $prevEap
if (-not $dockerOk) { throw "Docker 引擎未就绪,请先启动 Docker Desktop 并等托盘图标变绿" }
if ($PublicHost -eq "CHANGE_ME") {
Warn "未指定 -PublicHost,捕鱼对局层地址会被写成 CHANGE_ME,客户端将连不上"
}
$baseImages = [ordered]@{
"mcr.microsoft.com/dotnet/sdk:8.0" = "mcr.m.daocloud.io/dotnet/sdk:8.0"
"mcr.microsoft.com/dotnet/aspnet:8.0" = "mcr.m.daocloud.io/dotnet/aspnet:8.0"
}
foreach ($official in $baseImages.Keys) {
docker image inspect $official *> $null
if ($LASTEXITCODE -ne 0) {
Info "本地缺少 $official,经镜像站拉取 ..."
docker pull $baseImages[$official]
if ($LASTEXITCODE -ne 0) { throw "拉取 $official 失败" }
docker tag $baseImages[$official] $official
}
}
# ---------- 3) 构建镜像(编译在容器内)----------
foreach ($name in $targets) {
$s = $serviceMap[$name]
Info "构建镜像 0524$name ..."
docker build -f (Join-Path $Deploy "Dockerfile.build") `
--build-arg "PROJECT=$($s.proj)" `
--build-arg "SVC_NAME=$name" `
--build-arg "APP_DLL=$($s.dll)" `
--build-arg "PUBLIC_HOST=$PublicHost" `
-t "0524$name" $Root
if ($LASTEXITCODE -ne 0) { throw "构建 0524$name 失败" }
}
# ---------- 4) 导出 tar(只含本次构建的镜像)----------
New-Item -ItemType Directory -Path $Dist -Force | Out-Null
$isAll = ($targets.Count -eq $serviceMap.Count)
$suffix = if ($isAll) { "" } else { "-" + ($targets -join "-") }
$tar = Join-Path $Dist "buyu-images$suffix-$Tag.tar"
$tags = @($targets | ForEach-Object { "0524$_" })
Info ("导出镜像到 $tar ...(含: " + ($tags -join ', ') + ")")
docker save -o $tar @tags
if ($LASTEXITCODE -ne 0) { throw "导出镜像失败" }
$sizeMB = [math]::Round((Get-Item $tar).Length / 1MB, 1)
Write-Host ""
Info "完成!镜像包: $tar ($sizeMB MB)"
Write-Host "下一步 —— 在本机上传到服务器:" -ForegroundColor Cyan
Write-Host " ssh root@<服务器IP> `"mkdir -p /opt/buyu/deploy/dist`"" -ForegroundColor Cyan
Write-Host " scp `"$tar`" root@<服务器IP>:/opt/buyu/deploy/dist/" -ForegroundColor Cyan
Write-Host "再登录服务器执行(只重启本次涉及的服务):" -ForegroundColor Cyan
Write-Host " cd /opt/buyu" -ForegroundColor Cyan
Write-Host (" bash deploy/load-and-up.sh deploy/dist/" + (Split-Path $tar -Leaf) + " " + ($targets -join ' ')) -ForegroundColor Cyan