95 lines
5.1 KiB
PowerShell
95 lines
5.1 KiB
PowerShell
# ============================================================
|
||||
|
|
# MyBlog 一键更新脚本(在 Windows 上运行)
|
|||
|
|
# 作用:打包本地代码 -> 上传到服务器 -> 服务器自动 备份/迁移/重启/验证
|
|||
|
|
# 前置条件:
|
|||
|
|
# 1. 已配置 SSH 免密登录(首次需执行:ssh-keygen 后 ssh-copy-id 服务器)
|
|||
|
|
# 2. 服务器上已按 deploy/DEPLOY.md 完成首次部署(myblog 服务存在)
|
|||
|
|
# 用法(在项目根目录 D:\MyBlog 打开 PowerShell):
|
|||
|
|
# powershell -ExecutionPolicy Bypass -File .\deploy\update.ps1
|
|||
|
|
# 或直接指定服务器:.\deploy\update.ps1 -Server root@8.145.36.108
|
|||
|
|
# 常用参数:
|
|||
|
|
# -Server 服务器地址,默认读取 deploy/deploy.conf(缺省用 root@8.145.36.108)
|
|||
|
|
# -IncludeUploads 同时同步本地 uploads/(首次部署或新增静态资源时用)
|
|||
|
|
# 安全说明:
|
|||
|
|
# - 永远不打包 .env 与 backend/blog.db(密钥与数据库只在服务器上)
|
|||
|
|
# - 默认排除 uploads/:网页上传的文件只在服务器,本地打包不覆盖
|
|||
|
|
# ============================================================
|
|||
|
|
|
|||
|
|
param(
|
|||
|
|
[string]$Server = "",
|
|||
|
|
[switch]$IncludeUploads
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
$ErrorActionPreference = "Stop"
|
|||
|
|
$ScriptDir = $PSScriptRoot # deploy 目录
|
|||
|
|
$ProjectRoot = Split-Path -Parent $ScriptDir # 项目根目录
|
|||
|
|
$PackageName = "myblog.tar.gz"
|
|||
|
|
$RemoteScript = "update_server.sh"
|
|||
|
|
|
|||
|
|
function Write-Step([string]$msg) { Write-Host ""; Write-Host "== $msg ==" -ForegroundColor Cyan }
|
|||
|
|
|
|||
|
|
# ---------- 1. 确定服务器地址(命令行参数 > deploy.conf > 默认值) ----------
|
|||
|
|
if (-not $Server -and (Test-Path (Join-Path $ScriptDir "deploy.conf"))) {
|
|||
|
|
$confLine = Get-Content (Join-Path $ScriptDir "deploy.conf") | Where-Object { $_ -match '^\s*SERVER\s*=' } | Select-Object -First 1
|
|||
|
|
if ($confLine) { $Server = ($confLine -split '=', 2)[1].Trim() }
|
|||
|
|
$domainLine = Get-Content (Join-Path $ScriptDir "deploy.conf") | Where-Object { $_ -match '^\s*DOMAIN\s*=' } | Select-Object -First 1
|
|||
|
|
if ($domainLine) { $Domain = ($domainLine -split '=', 2)[1].Trim() }
|
|||
|
|
}
|
|||
|
|
if (-not $Server) { $Server = "root@8.145.36.108" }
|
|||
|
|
Write-Host "目标服务器:$Server"
|
|||
|
|
if ($Domain) { Write-Host "验证域名:$Domain" }
|
|||
|
|
|
|||
|
|
# ---------- 2. 检查依赖工具 ----------
|
|||
|
|
foreach ($tool in @("tar", "scp", "ssh")) {
|
|||
|
|
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
|
|||
|
|
throw "缺少工具 $tool,请先安装 OpenSSH 客户端(Windows 设置 -> 可选功能)。"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ---------- 3. 打包本地代码(排除密钥/数据库/缓存/上传目录) ----------
|
|||
|
|
Write-Step "打包本地代码"
|
|||
|
|
$tmpPkg = Join-Path $env:TEMP $PackageName
|
|||
|
|
if (Test-Path $tmpPkg) { Remove-Item -LiteralPath $tmpPkg -Force }
|
|||
|
|
Push-Location $ProjectRoot
|
|||
|
|
try {
|
|||
|
|
# 排除本地敏感运行数据:数据库、密钥配置、IPWatch 家庭端密钥与日志
|
|||
|
|
$excludes = @("--exclude=backend/blog.db", "--exclude=backend/__pycache__",
|
|||
|
|
"--exclude=backend/routers/__pycache__",
|
|||
|
|
"--exclude=backend/services/__pycache__", "--exclude=venv",
|
|||
|
|
"--exclude=deploy/ipwatch/ipwatch.conf",
|
|||
|
|
"--exclude=deploy/ipwatch/lastip.txt",
|
|||
|
|
"--exclude=deploy/ipwatch/ipwatch.log")
|
|||
|
|
if (-not $IncludeUploads) { $excludes += "--exclude=uploads" }
|
|||
|
|
$targets = @("frontend", "backend", "deploy", "requirements.txt", ".env.example", "README.md")
|
|||
|
|
& tar -czf $tmpPkg @excludes @targets
|
|||
|
|
if ($LASTEXITCODE -ne 0) { throw "tar 打包失败" }
|
|||
|
|
} finally {
|
|||
|
|
Pop-Location
|
|||
|
|
}
|
|||
|
|
# 校验包内不含敏感文件(防止配置写错把密钥带上服务器)
|
|||
|
|
$bad = tar -tzf $tmpPkg | Select-String "(^|/)\.env$|blog\.db|__pycache__|^uploads/|ipwatch\.(conf|log)$|lastip\.txt" | Select-Object -First 5
|
|||
|
|
if ($bad) { throw "更新包内含不应上传的文件:$($bad -join ', ')" }
|
|||
|
|
Write-Host ("更新包已生成:{0}({1:N0} KB)" -f $tmpPkg, ((Get-Item $tmpPkg).Length / 1KB))
|
|||
|
|
|
|||
|
|
# ---------- 4. 上传到服务器 ----------
|
|||
|
|
Write-Step "上传更新包与服务器脚本"
|
|||
|
|
& scp -o BatchMode=yes -o StrictHostKeyChecking=accept-new $tmpPkg "$($Server):/tmp/$PackageName"
|
|||
|
|
if ($LASTEXITCODE -ne 0) { throw "上传失败:请确认已配置 SSH 免密登录" }
|
|||
|
|
& scp -o BatchMode=yes (Join-Path $ScriptDir $RemoteScript) "$($Server):/tmp/$RemoteScript"
|
|||
|
|
if ($LASTEXITCODE -ne 0) { throw "上传服务器脚本失败" }
|
|||
|
|
Write-Host "上传完成"
|
|||
|
|
|
|||
|
|
# ---------- 5. 执行服务器端更新(备份/解压/迁移/重启/验证) ----------
|
|||
|
|
Write-Step "执行服务器端更新(备份、迁移、重启、验证)"
|
|||
|
|
$domainArg = if ($Domain) { " '$Domain'" } else { "" }
|
|||
|
|
# 服务器端脚本若带 Windows 换行(CRLF)会导致 bash 解析失败,先统一转成 LF 再执行
|
|||
|
|
& ssh -o BatchMode=yes $Server "sed -i 's/\r`$//' /tmp/$RemoteScript && bash /tmp/$RemoteScript$domainArg"
|
|||
|
|
if ($LASTEXITCODE -ne 0) {
|
|||
|
|
Write-Host "服务器端更新失败,请检查上方输出。可用备份回滚:/root/blog-backups/" -ForegroundColor Red
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Write-Host ""
|
|||
|
|
Write-Host "✅ 更新完成!请打开 https://$Domain 验证。" -ForegroundColor Green
|
|||
|
|
|