- 未设签名私钥时自动关 updater 产物, 免 tauri build 中断 - 发版(需签名)分流 publish-release.ps1, 本地换装/正式发布分离
132 lines
6.4 KiB
PowerShell
132 lines
6.4 KiB
PowerShell
# 用途: DevFlow 应用内升级发布通道(B4/L3)
|
|
# 构建 updater 产物(.nsis.zip + .sig) -> 生成 latest.json -> 上传 app.1216.top
|
|
# 设计: docs/02-架构设计/专项设计/版本更新与自动升级-设计-2026-10-09.md §四 L3
|
|
#
|
|
# 用法:
|
|
# .\scripts\publish-release.ps1 # 用 git 最近提交作 notes
|
|
# .\scripts\publish-release.ps1 -Notes "修复xx" # 手动指定更新说明
|
|
# .\scripts\publish-release.ps1 -SkipBuild # 复用已有产物(仅生成清单+上传)
|
|
#
|
|
# 前置:
|
|
# 1. tauri.conf.json 与 Cargo.toml 版本号一致(脚本第 1 步校验,防 D1 复发)
|
|
# 2. 签名私钥就位: $env:TAURI_SIGNING_PRIVATE_KEY = "$env:USERPROFILE\.tauri\devflow.key"
|
|
# (无密码密钥无需 TAURI_SIGNING_PRIVATE_KEY_PASSWORD)
|
|
# 3. ssh-proxy 已配 suke-dev(39.99.243.191),web 根 /var/www/app-site/
|
|
param(
|
|
[string]$Notes = "",
|
|
[switch]$SkipBuild
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$repo = "E:\wk-lab\devflow"
|
|
$site = "suke-dev"
|
|
$remoteDir = "/var/www/app-site/download/devflow"
|
|
$baseUrl = "https://app.1216.top/download/devflow"
|
|
|
|
Set-Location $repo
|
|
|
|
# ── 1. 版本双源校验(防 D1 复发)──
|
|
Write-Host "[1/6] 校验版本双源一致..."
|
|
$confVer = (Get-Content "$repo\src-tauri\tauri.conf.json" -Raw | ConvertFrom-Json).version
|
|
$cargoVer = (Select-String -Path "$repo\src-tauri\Cargo.toml" -Pattern '^version\s*=\s*"([^"]+)"').Matches[0].Groups[1].Value
|
|
if ($confVer -ne $cargoVer) {
|
|
Write-Host "[失败] 版本不一致: tauri.conf.json=$confVer, Cargo.toml=$cargoVer"
|
|
Write-Host " 请先对齐两处版本号再发布(设计 D1:双源必须同值)"
|
|
exit 1
|
|
}
|
|
$version = $confVer
|
|
Write-Host " 版本 v$version 双源一致,通过"
|
|
|
|
# ── 2. 签名私钥就位检查 ──
|
|
if (-not $env:TAURI_SIGNING_PRIVATE_KEY) {
|
|
$keyPath = "$env:USERPROFILE\.tauri\devflow.key"
|
|
if (Test-Path $keyPath) {
|
|
$env:TAURI_SIGNING_PRIVATE_KEY = $keyPath
|
|
Write-Host "[2/6] 签名私钥: $keyPath"
|
|
} else {
|
|
Write-Host "[失败] 未找到签名私钥($keyPath),且未设 TAURI_SIGNING_PRIVATE_KEY"
|
|
Write-Host " 先执行: cargo tauri signer generate -w `$env:USERPROFILE\.tauri\devflow.key"
|
|
exit 1
|
|
}
|
|
} else {
|
|
Write-Host "[2/6] 签名私钥: 取自环境变量"
|
|
}
|
|
|
|
# ── 3. 构建 updater 产物 ──
|
|
$nsisDir = "$repo\target\release\bundle\nsis"
|
|
if (-not $SkipBuild) {
|
|
Write-Host "[3/6] tauri build(含 createUpdaterArtifacts,可能 10 分钟级)..."
|
|
cmd /c "cargo tauri build 2>&1"
|
|
if ($LASTEXITCODE -ne 0) { Write-Host "[失败] tauri build"; exit 1 }
|
|
} else {
|
|
Write-Host "[3/6] 跳过构建(-SkipBuild),复用已有产物"
|
|
}
|
|
|
|
# 定位产物:Tauri v2 = setup.exe + .exe.sig(更新包即安装器本身);v1 遗留 = .nsis.zip + .nsis.zip.sig
|
|
$setupExe = Get-ChildItem "$nsisDir\devflow_${version}_x64-setup.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
$sigFile = Get-ChildItem "$nsisDir\devflow_${version}_x64-setup.exe.sig","$nsisDir\devflow_${version}_x64-setup.nsis.zip.sig" -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
$zipFile = Get-ChildItem "$nsisDir\devflow_${version}_x64-setup.nsis.zip" -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if (-not $sigFile) { Write-Host "[失败] 未找到 .sig 签名文件(确认 createUpdaterArtifacts=true 且 TAURI_SIGNING_PRIVATE_KEY 就位)"; exit 1 }
|
|
# 与 .sig 配对的更新产物:v2 签名(.exe.sig)配 setup.exe;v1 签名(.nsis.zip.sig)配 .nsis.zip
|
|
$artifact = if ($sigFile.Name.EndsWith('.exe.sig')) { $setupExe } else { $zipFile }
|
|
if (-not $artifact) { Write-Host "[失败] 未找到与 $($sigFile.Name) 配对的更新产物"; exit 1 }
|
|
Write-Host " 产物: $($artifact.Name) ($([math]::Round($artifact.Length/1MB,1))MB)"
|
|
|
|
# ── 4. 生成 latest.json ──
|
|
Write-Host "[4/6] 生成 latest.json..."
|
|
$sig = (Get-Content $sigFile.FullName -Raw).Trim()
|
|
if (-not $Notes) {
|
|
# 用 git 最近一条提交作 notes(无 CHANGELOG 时的兜底)
|
|
$Notes = (git log -1 --pretty=format:"%s" 2>&1) -join "`n"
|
|
if (-not $Notes) { $Notes = "DevFlow v$version" }
|
|
}
|
|
$pubDate = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
# SHA256:本机 PowerShell 5.1 实测缺 Get-FileHash(同 swap-install 兜底口径),异常走 certutil
|
|
function Get-Sha256Lower($path) {
|
|
try { return (Get-FileHash -Path $path -Algorithm SHA256 -ErrorAction Stop).Hash.ToLower() }
|
|
catch { return (((certutil -hashfile $path SHA256) | Select-Object -Index 1) -replace '\s', '').ToLower() }
|
|
}
|
|
$sha256 = Get-Sha256Lower $artifact.FullName
|
|
$latestJson = [ordered]@{
|
|
version = $version
|
|
notes = $Notes
|
|
pub_date = $pubDate
|
|
platforms = [ordered]@{
|
|
"windows-x86_64" = [ordered]@{
|
|
signature = $sig
|
|
url = "$baseUrl/$($artifact.Name)"
|
|
}
|
|
}
|
|
} | ConvertTo-Json -Depth 5
|
|
$latestPath = "$repo\target\release\bundle\nsis\latest.json"
|
|
# ⚠️ 必须写「UTF-8 无 BOM」:Windows PowerShell 5.1 的 `Out-File -Encoding utf8` 会写 BOM
|
|
# (EF BB BF),部分 JSON 解析路径会报 "expected value at line 1 column 1",
|
|
# 表现为 updater 拿到清单即失败(签名/包都没错,只多了头 3 字节)。2026-10-10 实证修正。
|
|
[System.IO.File]::WriteAllText($latestPath, $latestJson, (New-Object System.Text.UTF8Encoding($false)))
|
|
Write-Host " latest.json: version=$version, sha256=$($sha256.Substring(0,16))..."
|
|
|
|
# ── 5. 上传到 app.1216.top ──
|
|
Write-Host "[5/6] 上传到 $site`:$remoteDir ..."
|
|
$env:MSYS_NO_PATHCONV = "1"
|
|
# 确保远程目录存在
|
|
ssh-proxy exec -n $site -c "mkdir -p $remoteDir"
|
|
if ($LASTEXITCODE -ne 0) { Write-Host "[失败] 创建远程目录"; exit 1 }
|
|
|
|
$uploads = @($latestPath, $sigFile.FullName, $artifact.FullName)
|
|
if ($setupExe -and $setupExe.FullName -ne $artifact.FullName) { $uploads += $setupExe.FullName }
|
|
foreach ($f in $uploads) {
|
|
$name = Split-Path $f -Leaf
|
|
Write-Host " 上传 $name ..."
|
|
ssh-proxy upload --name $site --src $f --dst "$remoteDir/$name"
|
|
if ($LASTEXITCODE -ne 0) { Write-Host "[失败] 上传 $name"; exit 1 }
|
|
}
|
|
|
|
# ── 6. 验证 ──
|
|
Write-Host "[6/6] 验证远端清单..."
|
|
$remoteCheck = ssh-proxy exec -n $site -c "ls -la $remoteDir && echo '---' && cat $remoteDir/latest.json | head -5"
|
|
Write-Host $remoteCheck
|
|
Write-Host ""
|
|
Write-Host "[发布完成] v$version"
|
|
Write-Host " 清单地址: $baseUrl/latest.json"
|
|
Write-Host " 应用内检查更新即可发现新版本(app.1216.top 已配 no-cache,无需刷 CDN)"
|