#!/usr/bin/env pwsh # ============================================================== # sync.ps1 — 将 game-docker 同步到服务器 # # 替代 scp -r 的智能上传脚本: # - config 模式:只传 Docker 配置 + 脚本,~几 KB,秒级完成 # - app 模式:传应用代码,排除静态三方库(默认) # - full 模式:全量同步,适合首次部署 # # 用法: # .\sync.ps1 # app 模式(默认) # .\sync.ps1 -Mode config # 只传 docker/ 配置文件 # .\sync.ps1 -Mode full # 全量(首次部署) # .\sync.ps1 -Mode app -DryRun # 预览 tar 内容,不实际上传 # ============================================================== param( [ValidateSet('config', 'app', 'full')] [string]$Mode = 'app', [string]$Server = 'root@47.98.203.17', [string]$RemotePath = '/opt/youle/game-docker', [switch]$DryRun ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $LocalDir = Split-Path -Parent $MyInvocation.MyCommand.Path $TmpTar = Join-Path $env:TEMP 'game-docker-sync.tar.gz' # ===================== 排除规则 ===================== # 任何模式都不上传 $AlwaysExclude = @( './.env' # 密钥,绝不上传 './.git' './*.bak' './game' # 原始未修改代码备份,仅本地保留 ) # app / config 模式额外排除的静态三方库(几乎不变动,Docker build 也不需要) # 这些目录已在 .dockerignore 中排除,服务器上不需要它们 $VendorExclude = @( './api/document' # 接口文档,228 files / 2.07 MB './api/sample' # 示例代码,72 files / 3.18 MB './api/tests' # 单元测试 # dlweb/api/third(阿里云短信SDK)已从项目中删除,见 .dockerignore ) # ===================== config 模式:精确文件列表 ===================== $ConfigFiles = @( 'docker-compose.yml' '.env.example' 'deploy.sh' 'init-ssl.sh' 'env_config.php' 'README.md' 'sync.ps1' 'docker' # 目录 ) # ===================== 辅助函数 ===================== function Write-Step([string]$msg) { Write-Host "[sync] $msg" -ForegroundColor Cyan } function Write-Ok([string]$msg) { Write-Host "[OK] $msg" -ForegroundColor Green } function Write-Warn([string]$msg) { Write-Host "[!!] $msg" -ForegroundColor Yellow } # ===================== 主逻辑 ===================== try { if ($Mode -eq 'config') { # ── Config 模式:用 scp 精确传输少量关键文件(最快) ────────────── Write-Step 'Config mode: uploading docker configs only...' $missing = @() foreach ($f in $ConfigFiles) { $src = Join-Path $LocalDir $f if (-not (Test-Path $src)) { $missing += $f; continue } if ($DryRun) { Write-Host " [dry] would scp: $f" continue } if (Test-Path $src -PathType Container) { scp -r -O $src "${Server}:${RemotePath}/" } else { scp -O $src "${Server}:${RemotePath}/$f" } } if ($missing) { Write-Warn "Skipped (not found): $($missing -join ', ')" } if (-not $DryRun) { Write-Ok 'Config files uploaded.' } } else { # ── App / Full 模式:tar + ssh,单连接传全部文件 ──────────────────── # # 性能对比(约 5000 文件): # scp -r : ~5000 次 SSH 握手,极慢 # tar+ssh: 1 次 SSH 连接 + gzip 压缩,快 10-50x # $excludeArgs = [System.Collections.Generic.List[string]]::new() foreach ($e in $AlwaysExclude) { $excludeArgs.Add("--exclude=$e") } if ($Mode -eq 'app') { foreach ($e in $VendorExclude) { $excludeArgs.Add("--exclude=$e") } } if ($DryRun) { Write-Step "DryRun ($Mode): listing archive contents..." Write-Host " tar args: $($excludeArgs -join ' ')" & tar -tvf - @excludeArgs -C $LocalDir . 2>&1 | Select-Object -First 60 Write-Warn "(dry-run, nothing uploaded)" return } # 1. 在本地打包(gzip 压缩减小传输量) Write-Step "$Mode mode: creating archive (this may take a few seconds)..." & tar -czf $TmpTar @excludeArgs -C $LocalDir . $sizeMB = [math]::Round((Get-Item $TmpTar).Length / 1MB, 2) Write-Ok "Archive created: $sizeMB MB" # 2. 上传单个 tar 文件(单连接,比 scp -r 快得多) Write-Step "Uploading $sizeMB MB to $Server ..." scp -O $TmpTar "${Server}:/tmp/game-docker-sync.tar.gz" # 3. 服务器端解压(先建目录,保留已有文件,覆盖更新内容) Write-Step 'Extracting on server...' ssh $Server "mkdir -p ${RemotePath} && tar xzf /tmp/game-docker-sync.tar.gz -C ${RemotePath} --overwrite && rm /tmp/game-docker-sync.tar.gz" Write-Ok "Sync complete ($Mode mode, $sizeMB MB transferred)." Write-Host "" Write-Host " Next steps (if needed):" -ForegroundColor DarkGray Write-Host " docker compose up -d --build api # 重建应用镜像" -ForegroundColor DarkGray Write-Host " docker compose up -d --build dlweb # 重建 dlweb" -ForegroundColor DarkGray Write-Host " docker compose restart nginx # 重载配置" -ForegroundColor DarkGray } } finally { if (Test-Path $TmpTar) { Remove-Item $TmpTar -Force } }