- Directory.Build.props: 生成 XML 注释供 Swagger 展示中文说明 - AdminHost/FishGameHost: 接入 Swashbuckle,路由 adminswagger / roomswagger - 后台前端 config.js 改为同源相对地址,适配反向代理 - socket.html(SignalR 协议文档)纳入仓库 - deploy/: 本机构建(容器内编译,规避杀软)、服务器替换脚本、Nginx 配置
158 lines
7.4 KiB
PowerShell
158 lines
7.4 KiB
PowerShell
<#
|
||
本机构建镜像并导出 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
|