# 用途: 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)"