# ============================================================ # Thing 构建发布脚本(目标:自建 Gitea release) # # 用法: # .\scripts\release.ps1 -Version 0.2.0 # 同步版本号 + 构建 + 发布 # .\scripts\release.ps1 -Version 0.2.0 -SkipBuild # 复用现有构建产物,直接发布 # .\scripts\release.ps1 -Version 0.2.0 -SkipPush # 只构建+整理产物,不上传 # # 发布产物(上传到 https://gitea.atie.fun/LFeng/Thing 的 v{Version} release): # thing_{v}_x64.exe 便携免安装版(无内核) # thing_{v}_x64.msi 安装版 MSI(去掉 tauri 默认的 _en-US 后缀) # thing_{v}_x64-setup.exe 安装版 NSIS # thing-hk_{v}.zip ThingHK 硬件监控内核(mihomo 继续走代理模块内置的 GitHub 下载) # # 前提: # - Gitea token 已配置为环境变量 GITEA_TOKEN(-SkipPush 时不需要) # - 可选 -NotesPath 指定 release notes 文件(Markdown 文本), # 缺省时使用 scripts/release-notes.md(若存在),否则用简单占位文本 # ============================================================ param( [string]$Version = '', [switch]$SkipBuild, [switch]$SkipPush, [string]$NotesPath = '' ) $ErrorActionPreference = 'Stop' $Root = Split-Path -Parent $PSScriptRoot $TargetDir = Join-Path $Root 'src-tauri\target\release' $BundleDir = Join-Path $TargetDir 'bundle' $RepoOwner = 'LFeng' $RepoName = 'Thing' $ApiBase = 'https://gitea.atie.fun/api/v1' $Product = 'thing' function Write-Step([string]$msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan } # ---------- 0. 版本号 ---------- if ([string]::IsNullOrWhiteSpace($Version)) { $conf = Get-Content (Join-Path $Root 'src-tauri\tauri.conf.json') -Raw | ConvertFrom-Json $Version = $conf.version Write-Step "未指定 -Version,沿用现有版本 $Version" } # 校验 X.Y.Z 格式 if ($Version -notmatch '^\d+\.\d+\.\d+$') { throw "版本号格式错误(应为 X.Y.Z):$Version" } Write-Step "发布版本:v$Version" # ---------- 1. 同步版本号到三处 ---------- $tauriConf = Join-Path $Root 'src-tauri\tauri.conf.json' $cargoToml = Join-Path $Root 'src-tauri\Cargo.toml' $pkgJson = Join-Path $Root 'package.json' $t = Get-Content $tauriConf -Raw $t = $t -replace '("version"\s*:\s*")[^"]*(")', "`${1}$Version`${2}" [System.IO.File]::WriteAllText($tauriConf, $t, (New-Object System.Text.UTF8Encoding($false))) $c = Get-Content $cargoToml -Raw $c = $c -replace '(?m)^(version\s*=\s*")[^"]*(")', "`${1}$Version`${2}" [System.IO.File]::WriteAllText($cargoToml, $c, (New-Object System.Text.UTF8Encoding($false))) $p = Get-Content $pkgJson -Raw $p = $p -replace '("version"\s*:\s*")[^"]*(")', "`${1}$Version`${2}" [System.IO.File]::WriteAllText($pkgJson, $p, (New-Object System.Text.UTF8Encoding($false))) Write-Step "版本号已同步:tauri.conf.json / Cargo.toml / package.json" # ---------- 2. 构建 ---------- if (-not $SkipBuild) { Write-Step '开始构建(bun run tauri build)...' Push-Location $Root try { bun run tauri build } finally { Pop-Location } if ($LASTEXITCODE -ne 0) { throw 'tauri build 失败' } } else { Write-Step '跳过构建,复用现有产物' } # ---------- 3. 整理产物 ---------- # 版本号此时已解析,再确定暂存目录 $StageDir = Join-Path $Root "release_stage\$Version" New-Item -ItemType Directory -Force -Path $StageDir | Out-Null $exeSrc = Join-Path $TargetDir "$Product.exe" $msiSrc = Join-Path $BundleDir "msi\${Product}_${Version}_x64_en-US.msi" $nsisSrc = Join-Path $BundleDir "nsis\${Product}_${Version}_x64-setup.exe" $hkSrc = Join-Path $Root 'src-tauri\binaries\ThingHK.exe' $exeOut = Join-Path $StageDir "${Product}_${Version}_x64.exe" $msiOut = Join-Path $StageDir "${Product}_${Version}_x64.msi" $nsisOut = Join-Path $StageDir "${Product}_${Version}_x64-setup.exe" $hkZip = Join-Path $StageDir "thing-hk_${Version}.zip" if (Test-Path $exeSrc) { Copy-Item $exeSrc $exeOut } else { Write-Warning "缺少便携版:$exeSrc" } if (Test-Path $msiSrc) { Copy-Item $msiSrc $msiOut } else { Write-Warning "缺少 MSI(已跳过重命名):$msiSrc" } if (Test-Path $nsisSrc) { Copy-Item $nsisSrc $nsisOut } else { Write-Warning "缺少 NSIS:$nsisSrc" } if (Test-Path $hkSrc) { $tmp = Join-Path $env:TEMP "thinghk_$([guid]::NewGuid().ToString('N'))" New-Item -ItemType Directory -Force -Path $tmp | Out-Null Copy-Item $hkSrc (Join-Path $tmp 'ThingHK.exe') Compress-Archive -Path (Join-Path $tmp 'ThingHK.exe') -DestinationPath $hkZip -Force Remove-Item -Recurse -Force $tmp Write-Step "ThingHK 内核包:$hkZip" } else { Write-Warning "缺少 ThingHK.exe:$hkSrc" } Write-Step "产物已整理到:$StageDir" Get-ChildItem $StageDir | Select-Object Name, @{n='Size(MB)';e={[math]::Round($_.Length/1MB,1)}} | Format-Table -AutoSize # ---------- 4. 上传到 Gitea ---------- if ($SkipPush) { Write-Step '已跳过上传(-SkipPush)' exit 0 } $token = $env:GITEA_TOKEN if ([string]::IsNullOrWhiteSpace($token)) { throw '未设置环境变量 GITEA_TOKEN,无法发布到 Gitea(或使用 -SkipPush 跳过上传)' } $auth = @{ Authorization = "token $token" } # 4.1 release notes if (-not [string]::IsNullOrWhiteSpace($NotesPath)) { $body = Get-Content $NotesPath -Raw } elseif (Test-Path (Join-Path $PSScriptRoot 'release-notes.md')) { $body = Get-Content (Join-Path $PSScriptRoot 'release-notes.md') -Raw } else { $body = "Thing v$Version" } # 4.2 创建 release(已存在同名 tag 则复用) Write-Step "创建 release v$Version ..." $releaseUrl = "$ApiBase/repos/$RepoOwner/$RepoName/releases" $releasePayload = @{ tag_name = "v$Version" name = "Thing v$Version" body = $body draft = $false prerelease = $false } | ConvertTo-Json try { $release = Invoke-RestMethod -Method Post -Uri $releaseUrl -Headers $auth -ContentType 'application/json' -Body $releasePayload } catch { # tag 已存在:尝试用该 tag 查找现有 release,后续资产上传会追加 Write-Warning "创建 release 失败,尝试复用已有 release:$_" $release = Invoke-RestMethod -Method Get -Uri "$releaseUrl/tags/v$Version" -Headers $auth } $releaseId = $release.id Write-Step "release id=$releaseId" # 4.3 逐个上传资产 $assets = @($exeOut, $msiOut, $nsisOut, $hkZip) | Where-Object { Test-Path $_ } foreach ($file in $assets) { $name = Split-Path $file -Leaf Write-Step "上传 $name ..." $assetUrl = "$releaseUrl/$releaseId/assets?name=$([uri]::EscapeDataString($name))" $resp = Invoke-RestMethod -Method Post -Uri $assetUrl -Headers $auth -ContentType 'application/octet-stream' -InFile $file Write-Host " -> $($resp.browser_download_url)" -ForegroundColor Green } Write-Step "发布完成:https://gitea.atie.fun/$RepoOwner/$RepoName/releases/tag/v$Version"