Files
sundynix-agentix/sundynix-desktop/frontend/src/lib/version.ts
T
Blizzard 8c980d2b37 feat(desktop,ci): GitHub Releases 分发 + 桌面端检查更新
让桌面端像开源项目一样用 GitHub Releases 分发,旧版用户自动获知更新:
- CD:.github/workflows/release.yml —— 打 vX.Y.Z 标签触发,macOS(universal)/
  Windows(amd64) 用 wails build 出安装包,softprops/action-gh-release 发布到
  GitHub Release(GOWORK=off,因 desktop 不在工作区)。
- 桌面端检查更新:lib/version.ts(APP_VERSION + 查 GitHub /releases/latest +
  语义版本比较 + openExternal 走 Wails BrowserOpenURL);UpdateBanner 启动时
  检查,有新版顶部横幅「前往下载」。404/限流/离线均静默不打扰。

注:未签名/公证(macOS Gatekeeper / Win SmartScreen 需后续证书);release
workflow 需真实 tag push 才能验证(GitHub 侧运行)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:26:02 +08:00

51 lines
2.1 KiB
TypeScript
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.
// 桌面端版本与"检查更新"——查 GitHub Releases 最新版,比当前版本新则提示下载。
// 发版流程:bump 此处 APP_VERSION= package.json version)→ 打 tag vX.Y.Z → release workflow 自动构建+发布。
export const APP_VERSION = "0.1.0"; // 当前桌面端版本(与 git tag 去掉 v 前缀对齐)
const REPO = "blizzardzhang/sundynix-agentix";
export interface ReleaseInfo {
version: string; // 最新版本号(不含 v
url: string; // release 页面(用户在此选 mac/win 安装包)
notes: string; // 更新日志
}
// checkUpdate 查 GitHub 最新 release;有比当前更新的版本则返回信息,否则 null(含出错/限流时静默)。
export async function checkUpdate(): Promise<ReleaseInfo | null> {
try {
const res = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
headers: { Accept: "application/vnd.github+json" },
});
if (!res.ok) return null; // 404=还没发过 release / 403=限流 → 静默
const d = (await res.json()) as { tag_name?: string; html_url?: string; body?: string };
const latest = String(d.tag_name ?? "").replace(/^v/, "");
if (!latest || !isNewer(latest, APP_VERSION)) return null;
return { version: latest, url: d.html_url ?? `https://github.com/${REPO}/releases/latest`, notes: d.body ?? "" };
} catch {
return null; // 离线等 → 不打扰
}
}
// openExternal 在系统浏览器打开链接(Wails 用 runtime.BrowserOpenURL,浏览器模式回退 window.open)。
export function openExternal(url: string): void {
const w = window as unknown as { runtime?: { BrowserOpenURL?: (u: string) => void } };
if (w.runtime?.BrowserOpenURL) {
w.runtime.BrowserOpenURL(url);
} else {
window.open(url, "_blank");
}
}
// isNewer 语义化版本比较:a 是否比 b 新(按 major.minor.patch)。
function isNewer(a: string, b: string): boolean {
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
for (let i = 0; i < 3; i++) {
const x = pa[i] ?? 0;
const y = pb[i] ?? 0;
if (x !== y) return x > y;
}
return false;
}