Files
mqttx/scripts/publish-gitea.ps1
T
JoeAllen 6ff05f7216 ci: 发布脚本自动创建 Release 并按 OTA 命名上传附件
应用内 OTA 只认 Gitea Release 附件(update.rs 查 /releases),此前脚本
只上传 Generic 包,Release 不存在时更新检查永远失败。现在:

- 自动查询/创建与版本同名 tag 的 Release,附件命名规范为
  mqttx-v<ver>-<triple>-mqttx[.exe](不符约定时重命名后上传),
  附带 .sha256 侧车,供 OTA sha256 校验
- 版本号统一剥离 v 前缀(git describe 与 Cargo.toml 两种来源一致)
- 平台三元组按运行环境推断(与 update.rs::platform_triple 一致),
  可用 GITEA_TRIPLE 覆盖
- owner/repo 均支持从 git remote 推断,可用 GITEA_REPO 参数覆盖
- ps1 附件上传走 .NET HttpClient multipart(PS 5.1 无 -Form)
2026-09-29 10:33:42 +08:00

228 lines
9.6 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<#
.SYNOPSIS
发布构建产物到 Gitea:Generic Package + Release 附件(OTA 更新源)。
.DESCRIPTION
1) Generic Package API 上传:
PUT {GITEA_URL}/api/packages/{owner}/generic/{pkg}/{version}/{filename}
2) Release 附件上传(应用内 OTA 只认 Release 附件):
POST {GITEA_URL}/api/v1/repos/{owner}/{repo}/releases/{id}/assets?name=...
命名约定 mqttx-v<ver>-<triple>-mqttx[.exe],含 .sha256 侧车。
认证:Authorization: token <GITEA_TOKEN>
同名包版本重复上传会得到 409,脚本按"已存在"处理。
.EXAMPLE
$env:GITEA_URL = "https://gitea.example.com"
$env:GITEA_TOKEN = "xxxxxxxx"
.\scripts\publish-gitea.ps1 # 上传默认产物,版本自动推断
.\scripts\publish-gitea.ps1 -Version v1.0.1 # 指定版本
.\scripts\publish-gitea.ps1 -Files a.exe, b.zip # 指定文件
.PARAMETER Files
待上传文件列表。缺省时自动探测 target/release/mqttx(.exe)。
.PARAMETER Version
软件包版本。缺省时依次尝试 git describe --tags、gpui-app/Cargo.toml。
.PARAMETER Owner
覆盖 GITEA_OWNER(缺省时从 git remote origin 推断)。
.PARAMETER Repo
覆盖 GITEA_REPO(缺省时从 git remote origin 推断,失败为 mqttx)。
.PARAMETER PackageName
覆盖 GITEA_PKG(默认 mqttx)。
#>
[CmdletBinding()]
param(
[string[]]$Files = @(),
[string]$Version = "",
[string]$Owner = "",
[string]$Repo = "",
[string]$PackageName = ""
)
$ErrorActionPreference = "Stop"
# Windows PowerShell 5.1 默认可能协商 TLS1.0,现代 Gitea 实例要求 TLS1.2+
try {
[Net.ServicePointManager]::SecurityProtocol = `
[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
} catch { }
function Die($msg) { Write-Error "错误: $msg"; exit 1 }
# ── 必填配置 ────────────────────────────────────────────────────────────────
$GiteaUrl = $env:GITEA_URL
$Token = $env:GITEA_TOKEN
if (-not $GiteaUrl) { Die "缺少环境变量 GITEA_URL(如 https://gitea.example.com)" }
if (-not $Token) { Die "缺少环境变量 GITEA_TOKEN(需 package 写权限)" }
$GiteaUrl = $GiteaUrl.TrimEnd("/")
$Root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$Pkg = if ($PackageName) { $PackageName } elseif ($env:GITEA_PKG) { $env:GITEA_PKG } else { "mqttx" }
# ── owner/repo:参数 > 环境变量 > git remote origin 推断 ─────────────────────
if (-not $Owner) { $Owner = $env:GITEA_OWNER }
if (-not $Repo) { $Repo = $env:GITEA_REPO }
if (-not $Owner -or -not $Repo) {
$remote = git -C $Root remote get-url origin 2>$null
if ($remote) {
# 兼容 git@host:owner/repo.git 与 https://host/owner/repo.git
if ($remote -match "://") {
# URL 形式:取 host 之后的路径段
$path = ($remote -split "/", 4)[-1]
}
else {
# SCP 形式 git@host:owner/repo
$path = ($remote -split ":", 2)[-1]
}
$path = $path -replace "\.git$", ""
$seg = @($path -split "/" | Where-Object { $_ })
if ($seg.Count -ge 2) {
if (-not $Owner) { $Owner = $seg[0] }
if (-not $Repo) { $Repo = $seg[1] }
}
}
}
if (-not $Owner) { Die "无法推断 owner,请用 -Owner 或 GITEA_OWNER 指定" }
if (-not $Repo) { $Repo = $Pkg }
# ── 版本:参数 > git describe > Cargo.toml ──────────────────────────────────
if (-not $Version) {
$Version = (git -C $Root describe --tags --always 2>$null)
}
if (-not $Version) {
$m = Select-String -Path (Join-Path $Root "gpui-app\Cargo.toml") -Pattern '^version = "(.+)"' | Select-Object -First 1
if ($m) { $Version = $m.Matches[0].Groups[1].Value }
}
if (-not $Version) { Die "无法确定版本号" }
$Version = $Version -replace "/", "-" # Gitea 版本段不允许 "/"
# ── 默认产物 ────────────────────────────────────────────────────────────────
if (-not $Files -or $Files.Count -eq 0) {
$exe = Join-Path $Root "target\release\mqttx.exe"
$bin = Join-Path $Root "target\release\mqttx"
if (Test-Path $exe) { $Files = @($exe) }
elseif (Test-Path $bin) { $Files = @($bin) }
else { Die "未找到产物,请先 cargo build --release,或用 -Files 指定" }
}
Write-Host "→ Gitea: $GiteaUrl"
Write-Host "→ 软件包: $Owner/$Pkg@$Version"
Write-Host "→ 仓库: $Owner/$Repo"
Write-Host "→ 产物: $($Files -join ', ')"
foreach ($f in $Files) {
if (-not (Test-Path $f)) { Die "文件不存在: $f" }
$name = Split-Path -Leaf $f
$size = "{0:N1} MB" -f ((Get-Item $f).Length / 1MB)
Write-Host "→ 上传 $name ($size)"
$url = "$GiteaUrl/api/packages/$Owner/generic/$Pkg/$Version/$name"
# Windows PowerShell 5.1 无 -InFile 的 token 认证上传也走 Invoke-WebRequest
try {
Invoke-RestMethod -Method Put `
-Headers @{ Authorization = "token $Token" } `
-InFile $f `
-ContentType "application/octet-stream" `
-Uri $url | Out-Null
Write-Host " ✓ 已发布 $name" -ForegroundColor Green
}
catch {
$status = $null
if ($_.Exception.Response) { $status = [int]$_.Exception.Response.StatusCode }
if ($status -eq 409) {
Write-Host " ! 已存在同名版本(409),跳过:$name" -ForegroundColor Yellow
continue
}
Die "上传失败: $($_.Exception.Message)"
}
# 附带 sha256 校验文件
$sum = (Get-FileHash -Algorithm SHA256 $f).Hash.ToLower()
$sidecar = "$f.sha256"
"$sum $name" | Set-Content -NoNewline -Encoding ascii $sidecar
try {
Invoke-RestMethod -Method Put `
-Headers @{ Authorization = "token $Token" } `
-InFile $sidecar `
-ContentType "text/plain" `
-Uri "$url.sha256" | Out-Null
Write-Host " ✓ 已发布 $name.sha256" -ForegroundColor Green
}
catch {
Write-Host " ! sha256 文件上传失败" -ForegroundColor Yellow
}
}
Write-Host "完成。软件包页面: $GiteaUrl/$Owner?tab=packages" -ForegroundColor Cyan
# ── Release 附件:OTA 更新源(应用内 update.rs 只查 /releases)──────────────
# 规范化版本号:tag 推断出的 "v1.0.2" 与 Cargo 的 "1.0.2" 统一为无 v 数字段
$VerNum = $Version -replace "^v", ""
$Triple = "x86_64-pc-windows-msvc"
$Api = "$GiteaUrl/api/v1/repos/$Owner/$Repo"
# 取(或创建)与版本同名 tag 的 Release
$rel = $null
try {
$rel = Invoke-RestMethod -Headers @{ Authorization = "token $Token" } -Uri "$Api/releases/tags/v$VerNum"
} catch { }
if (-not $rel) {
try {
$rel = Invoke-RestMethod -Method Post `
-Headers @{ Authorization = "token $Token" } `
-ContentType "application/json" `
-Body (@{ tag_name = "v$VerNum"; name = "v$VerNum"; draft = $false; prerelease = $false } | ConvertTo-Json) `
-Uri "$Api/releases"
Write-Host "→ 已创建 Release v$VerNum"
}
catch {
Write-Host " ! Release 创建/查询失败:$($_.Exception.Message);跳过附件上传" -ForegroundColor Yellow
return
}
}
$releaseId = $rel.id
Write-Host "→ Release v$VerNum (id=$releaseId),上传附件…"
function Upload-ReleaseAsset($uri, $filePath) {
# Windows PowerShell 5.1 没有 -Form,用 .NET HttpClient 组 multipart(字段名必须为 attachment)
Add-Type -AssemblyName System.Net.Http
$client = New-Object System.Net.Http.HttpClient
$client.DefaultRequestHeaders.Authorization =
New-Object System.Net.Http.Headers.AuthenticationHeaderValue("token", $Token)
$content = New-Object System.Net.Http.MultipartFormDataContent
$fs = [System.IO.File]::OpenRead($filePath)
try {
$fileContent = New-Object System.Net.Http.StreamContent($fs)
$fileContent.Headers.ContentType =
[System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("application/octet-stream")
$content.Add($fileContent, "attachment", (Split-Path -Leaf $filePath))
$resp = $client.PostAsync($uri, $content).GetAwaiter().GetResult()
return [int]$resp.StatusCode
}
finally {
$fs.Dispose()
$client.Dispose()
}
}
foreach ($f in $Files) {
$name = Split-Path -Leaf $f
# 已符合 OTA 命名约定的原样使用;否则按约定规范重命名后上传
if ($name -match "^mqttx-v.*-mqttx(\.exe)?$") { $asset = $name }
elseif ($name.EndsWith(".exe")) { $asset = "mqttx-v$VerNum-$Triple-mqttx.exe" }
else { $asset = "mqttx-v$VerNum-$Triple-mqttx" }
$tmp = Join-Path $env:TEMP $asset
Copy-Item $f $tmp -Force
$sum = (Get-FileHash -Algorithm SHA256 $f).Hash.ToLower()
"$sum $asset" | Set-Content -NoNewline -Encoding ascii "$tmp.sha256"
foreach ($a in @($asset, "$asset.sha256")) {
$code = Upload-ReleaseAsset "$Api/releases/$releaseId/assets?name=$a" (Join-Path $env:TEMP $a)
if ($code -eq 201) {
Write-Host " ✓ Release 附件 $a" -ForegroundColor Green
}
else {
Write-Host " ! Release 附件 $a 上传失败 (HTTP $code)" -ForegroundColor Yellow
}
}
Remove-Item $tmp, "$tmp.sha256" -ErrorAction SilentlyContinue
}
Write-Host "完成。Release 页面: $GiteaUrl/$Owner/$Repo/releases/tag/v$VerNum" -ForegroundColor Cyan