feat: 重写为 GPUI 原生桌面应用并接入 Gitea 软件包发布
- gpui-kit 0.6 原生 UI:连接管理、消息流/日志、发布预设、全局变量、 设置、阿里云连接预设、原生 Logo - MQTT 5.0/3.1.1 引擎(rumqttc,TCP/TLS/WS/WSS),独立 tokio runtime + 事件泵,真实 broker 端到端测试 - 连接表单:保存前可一键测试连接(宽松校验,自动生成临时 Client ID), 高级属性(认证/MQTT 5 属性/遗嘱)按需折叠显示,WS 路径仅 WS/WSS 显示 - bug 修复:Hex 奇数长度显式报错、测试连接唯一 Client ID、Base64/Hex 发布发送真实字节、v5 发布记录携带 Content-Type/用户属性、预设应用 清除旧 Content-Type、模板时间基准统一;补 8 个单元测试 - Gitea 发布:Generic Package 上传脚本(bash/PowerShell)+ Actions release 工作流(tag 触发,双平台构建),README 使用说明
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
发布构建产物到 Gitea Package Registry(Generic 包)。
|
||||
|
||||
.DESCRIPTION
|
||||
用 Gitea Generic Package API 上传文件:
|
||||
PUT {GITEA_URL}/api/packages/{owner}/generic/{pkg}/{version}/{filename}
|
||||
认证:Authorization: token <GITEA_TOKEN>
|
||||
同名版本重复上传会得到 409,脚本按"已存在"处理。
|
||||
上传成功后附加 .sha256 校验文件。
|
||||
|
||||
.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 PackageName
|
||||
覆盖 GITEA_PKG(默认 mqttx)。
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string[]]$Files = @(),
|
||||
[string]$Version = "",
|
||||
[string]$Owner = "",
|
||||
[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:参数 > GITEA_OWNER > git remote origin 推断 ──────────────────────
|
||||
if (-not $Owner) { $Owner = $env:GITEA_OWNER }
|
||||
if (-not $Owner) {
|
||||
$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) { $Owner = $seg[0] }
|
||||
}
|
||||
}
|
||||
if (-not $Owner) { Die "无法推断 owner,请用 -Owner 或 GITEA_OWNER 指定" }
|
||||
|
||||
# ── 版本:参数 > 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 "→ 产物: $($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
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env bash
|
||||
# 发布构建产物到 Gitea Package Registry(Generic 包)。
|
||||
#
|
||||
# 用法:
|
||||
# GITEA_URL=https://gitea.example.com GITEA_TOKEN=xxx \
|
||||
# ./scripts/publish-gitea.sh [文件...] [版本]
|
||||
#
|
||||
# 环境变量:
|
||||
# GITEA_URL 实例地址,如 https://gitea.example.com(必填)
|
||||
# GITEA_TOKEN 访问令牌,需 package 写权限(必填)
|
||||
# GITEA_OWNER 软件包所属用户/组织(默认:由 git remote 推断,失败则报错)
|
||||
# GITEA_PKG 软件包名(默认 mqttx)
|
||||
#
|
||||
# 版本推断顺序:参数 > git describe --tags > gpui-app/Cargo.toml。
|
||||
set -euo pipefail
|
||||
|
||||
die() { echo "错误: $*" >&2; exit 1; }
|
||||
|
||||
[ -n "${GITEA_URL:-}" ] || die "缺少 GITEA_URL"
|
||||
[ -n "${GITEA_TOKEN:-}" ] || die "缺少 GITEA_TOKEN"
|
||||
GITEA_URL="${GITEA_URL%/}"
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PKG="${GITEA_PKG:-mqttx}"
|
||||
|
||||
# ── owner:优先环境变量,其次从 git remote 推断 ──────────────────────────────
|
||||
owner_of_remote() {
|
||||
local url path
|
||||
url="$(git -C "$ROOT" remote get-url origin 2>/dev/null)" || return 1
|
||||
case "$url" in
|
||||
*://*) path="${url#*://}"; path="${path#*/}" ;; # https://host/owner/repo.git
|
||||
*:*) path="${url#*:}" ;; # git@host:owner/repo.git
|
||||
*) return 1 ;;
|
||||
esac
|
||||
path="${path%.git}"
|
||||
printf '%s\n' "${path%%/*}"
|
||||
}
|
||||
OWNER="${GITEA_OWNER:-$(owner_of_remote || true)}"
|
||||
[ -n "${OWNER:-}" ] || die "无法推断 GITEA_OWNER,请显式设置"
|
||||
|
||||
# ── 版本:参数 > git describe > Cargo.toml ───────────────────────────────────
|
||||
FILES=()
|
||||
for arg in "$@"; do
|
||||
if [ -f "$arg" ]; then FILES+=("$arg"); else VERSION="$arg"; fi
|
||||
done
|
||||
if [ -z "${VERSION:-}" ]; then
|
||||
VERSION="$(git -C "$ROOT" describe --tags --always 2>/dev/null || true)"
|
||||
fi
|
||||
if [ -z "${VERSION:-}" ]; then
|
||||
VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' "$ROOT/gpui-app/Cargo.toml" | head -1)"
|
||||
fi
|
||||
[ -n "${VERSION:-}" ] || die "无法确定版本号"
|
||||
# Gitea 版本段不允许 "/",tag 形如 v1.0.0-3-gabc123 时规范化
|
||||
VERSION="${VERSION//\//-}"
|
||||
|
||||
# ── 默认产物 ────────────────────────────────────────────────────────────────
|
||||
if [ "${#FILES[@]}" -eq 0 ]; then
|
||||
if [ -x "$ROOT/target/release/mqttx.exe" ]; then
|
||||
FILES=("$ROOT/target/release/mqttx.exe")
|
||||
elif [ -x "$ROOT/target/release/mqttx" ]; then
|
||||
FILES=("$ROOT/target/release/mqttx")
|
||||
else
|
||||
die "未找到产物,请先 cargo build --release,或显式传入文件"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "→ Gitea: $GITEA_URL"
|
||||
echo "→ 软件包: $OWNER/$PKG@$VERSION"
|
||||
echo "→ 产物: ${FILES[*]}"
|
||||
|
||||
sha256() {
|
||||
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
|
||||
else shasum -a 256 "$1" | cut -d' ' -f1; fi
|
||||
}
|
||||
|
||||
for f in "${FILES[@]}"; do
|
||||
[ -f "$f" ] || die "文件不存在: $f"
|
||||
name="$(basename "$f")"
|
||||
echo "→ 上传 $name ($(du -h "$f" | cut -f1))"
|
||||
|
||||
code="$(curl -sS -o /tmp/gitea-pkg-resp.txt -w '%{http_code}' \
|
||||
-X PUT \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
--upload-file "$f" \
|
||||
"${GITEA_URL}/api/packages/${OWNER}/generic/${PKG}/${VERSION}/${name}")"
|
||||
|
||||
case "$code" in
|
||||
201|200|409) # 409 = 同名版本已存在,Gitea 拒绝覆盖
|
||||
if [ "$code" = "409" ]; then
|
||||
echo " ! 已存在同名版本(409),跳过:$name"
|
||||
else
|
||||
echo " ✓ 已发布 $name (HTTP $code)"
|
||||
# 附带 sha256 校验文件,便于下游验证
|
||||
sum="$(sha256 "$f")"
|
||||
echo "$sum $name" > "${f}.sha256"
|
||||
code2="$(curl -sS -o /dev/null -w '%{http_code}' \
|
||||
-X PUT \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: text/plain" \
|
||||
--data-binary "@${f}.sha256" \
|
||||
"${GITEA_URL}/api/packages/${OWNER}/generic/${PKG}/${VERSION}/${name}.sha256")"
|
||||
[ "$code2" = "201" ] || echo " ! sha256 文件上传失败 (HTTP $code2)"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo " ✗ 上传失败 (HTTP $code)" >&2
|
||||
cat /tmp/gitea-pkg-resp.txt >&2 || true
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "完成。软件包页面: ${GITEA_URL}/${OWNER}?tab=packages"
|
||||
Reference in New Issue
Block a user