新增: 应用内版本更新与自动升级全链路

后端 update.rs: app_check_update(拉 latest.json+ed25519 签名校验) /
app_download_install_update / prefs 读写 / mark_checked / mark_notified 五命令,
lib.rs 注册;tauri.conf updater 段(endpoints=app.1216.top/download/devflow/latest.json
+ pubkey + createUpdaterArtifacts) + tauri-plugin-updater 依赖 + capabilities 权限。

前端 UpdateBadge.vue(顶栏徽章+下拉) / useUpdateCheck.ts(检查/下载/安装状态机) /
AboutPanel.vue(设置页关于面板) + AppLayout 接线 + SettingsNav/searchIndex 索引 +
i18n zh/en settings 键。

运维 scripts/publish-release.ps1(发版) + swap-install.ps1(换装校验) +
设计文档 版本更新与自动升级-设计-2026-10-09.md。

验收: cargo check -p devflow EXIT 0 + vue-tsc EXIT 0 + build EXIT 0。
This commit is contained in:
lxy
2026-10-09 22:53:14 +08:00
parent c70faa7bb8
commit a8c92e9619
27 changed files with 2021 additions and 24 deletions
+121
View File
@@ -0,0 +1,121 @@
# 用途: 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),复用已有产物"
}
# 定位产物:裸 exe / .nsis.zip / .sig
$setupExe = Get-ChildItem "$nsisDir\devflow_${version}_x64-setup.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
$zipFile = Get-ChildItem "$nsisDir\devflow_${version}_x64-setup.nsis.zip" -ErrorAction SilentlyContinue | Select-Object -First 1
$sigFile = Get-ChildItem "$nsisDir\devflow_${version}_x64-setup.nsis.zip.sig" -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $zipFile) { Write-Host "[失败] 未找到 .nsis.zip 产物(确认 createUpdaterArtifacts=true)"; exit 1 }
if (-not $sigFile) { Write-Host "[失败] 未找到 .sig 签名文件"; exit 1 }
Write-Host " 产物: $($zipFile.Name) ($([math]::Round($zipFile.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 = (Get-FileHash $zipFile.FullName -Algorithm SHA256).Hash.ToLower()
$latestJson = [ordered]@{
version = $version
notes = $Notes
pub_date = $pubDate
platforms = [ordered]@{
"windows-x86_64" = [ordered]@{
signature = $sig
url = "$baseUrl/devflow_${version}_x64-setup.nsis.zip"
}
}
} | ConvertTo-Json -Depth 5
$latestPath = "$repo\target\release\bundle\nsis\latest.json"
$latestJson | Out-File $latestPath -Encoding utf8 -NoNewline
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, $zipFile.FullName, $sigFile.FullName)
if ($setupExe) { $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)"
+43
View File
@@ -0,0 +1,43 @@
# DevFlow 换装脚本:kill 旧进程 -> 静默安装 -> 启动验证
# 独立后台执行,避免中断当前会话导致换装半途而废
$ErrorActionPreference = 'Continue'
$log = 'E:/wk-lab/devflow/swap.log'
function Log($m) { "$(Get-Date -Format 'HH:mm:ss') $m" | Tee-Object -FilePath $log -Append }
Log "=== 换装开始 ==="
# 1. kill 旧进程
$procs = Get-Process devflow -ErrorAction SilentlyContinue
if ($procs) {
Log "发现运行中 devflow 进程: $($procs.Id -join ','),正在结束..."
$procs | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
$still = Get-Process devflow -ErrorAction SilentlyContinue
if ($still) { Log "警告: 仍有残留进程 $($still.Id -join ',')"; $still | Stop-Process -Force -ErrorAction SilentlyContinue; Start-Sleep -Seconds 2 }
Log "旧进程已结束"
} else {
Log "无运行中 devflow 进程"
}
# 2. 静默安装新包
$setup = 'E:/wk-lab/devflow/target/release/bundle/nsis/devflow_0.1.1_x64-setup.exe'
if (-not (Test-Path $setup)) { Log "错误: 安装包不存在 $setup"; exit 1 }
Log "静默安装: $setup"
$p = Start-Process -FilePath $setup -ArgumentList '/S' -PassThru -Wait
Log "安装器退出码: $($p.ExitCode)"
Start-Sleep -Seconds 3
# 3. 启动验证
$installed = 'C:\Users\23780\AppData\Local\devflow\devflow.exe'
if (Test-Path $installed) {
$ver = (Get-Item $installed).VersionInfo.FileVersion
Log "已安装二进制: $installed (FileVersion=$ver)"
Start-Process -FilePath $installed
Start-Sleep -Seconds 5
$new = Get-Process devflow -ErrorAction SilentlyContinue
if ($new) { Log "启动成功, PID=$($new.Id -join ',')" } else { Log "警告: 启动后未检测到进程" }
} else {
Log "警告: 未找到安装后二进制 $installed"
}
Log "=== 换装结束 ==="