mirror of
https://github.com/dream-pep/koring-launcher.git
synced 2026-09-12 05:45:18 +08:00
updater: enable prerelease & resilient tag discovery
Enable prerelease updates and make release discovery more robust. Set autoUpdater.allowPrerelease = true to ensure {base}-{buildId} prerelease tags are detected. Add compareVersionTags to compare X.Y.Z-buildId where buildId is compared numerically. discoverLatestTag now collects candidates from both /releases/latest HTML and the (proxied) GitHub API, picks the newest by the comparator, and logs candidates. Also continue trying other mirrors when a mirror reports no update. Update docs and CI workflow comments to explain the prerelease behavior.
This commit is contained in:
@@ -30,7 +30,10 @@
|
||||
# 正式对外发布需生产证书(OV/EV)+ 对应生产签名策略,届时只换 SIGNPATH_SIGNING_POLICY_SLUG。
|
||||
#
|
||||
# ⚠️ 版本号注意(electron-updater 语义):
|
||||
# - {base}-{buildId} 属于 semver prerelease;已安装同格式版本的用户可正常收到更高 buildId 的更新。
|
||||
# - {base}-{buildId} 属于 semver prerelease;electron-updater 默认 allowPrerelease=false 会跳过带
|
||||
# prerelease 后缀的新版本,客户端必须在 electron/updater.ts 设置 autoUpdater.allowPrerelease=true
|
||||
# (已落地),否则如 v1.2.1-4 永远检测不到 v1.2.1-5。
|
||||
# - 开启后:已安装同格式版本的用户可正常收到更高 buildId 的更新(buildId 数值比较)。
|
||||
# - 若未来发布不带 buildId 的稳定版本(如 1.2.0),稳定版用户不会自动升级到带 buildId 的构建。
|
||||
# - 切换 BUILD ID 方案(时间 ID → Run Number)时,旧格式数值更大(2608271921 > 12),
|
||||
# 老用户不会自动升级 —— 切换时应同时提升 base(如 1.3.0-12 > 1.2.0-2608271921)。
|
||||
|
||||
@@ -310,7 +310,11 @@ src/
|
||||
- 上传产物:setup.exe + latest.yml + release-notes.md(electron-updater 更新清单)
|
||||
|
||||
**⚠️ 版本语义注意(electron-updater)**:
|
||||
- `{base}-{buildId}` 属 semver prerelease:同格式版本之间可正常升级(buildId 更大者胜)
|
||||
- `{base}-{buildId}` 属 semver prerelease;**electron-updater 默认 `allowPrerelease=false` 会直接跳过带
|
||||
prerelease 后缀的新版本**(GitHub provider 过滤 prerelease release + isUpdateAvailable 的
|
||||
`semver.prerelease` 校验)——**必须**在 `electron/updater.ts` 设置 `autoUpdater.allowPrerelease = true`,
|
||||
否则如 v1.2.1-4 永远检测不到 v1.2.1-5(该问题已修复并落地)。
|
||||
- 开启后:同格式版本之间可正常升级(buildId 更大者胜,数值比较,如 `1.2.1-5 > 1.2.1-4`)
|
||||
- 若未来发布**不带** buildId 的稳定版本(如 `1.2.0`),稳定版用户不会自动升级到带 buildId 的构建
|
||||
- **迁移注意**:从时间 ID(`2608271921`)切换到 Run Number 后,旧格式数值更大(`2608271921 > 12`),
|
||||
老用户不会自动升级到新格式——切换时应同时提升 base(如 `1.3.0-12 > 1.2.0-2608271921`)
|
||||
|
||||
+48
-8
@@ -67,6 +67,27 @@ function getMirrors(): string[] {
|
||||
return DEFAULT_MIRRORS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个版本 tag(如 v1.2.1-5 / v1.2.1 / v1.2.0-2608271921),返回 a - b。
|
||||
* 项目版本为 {base}-{buildId}:base 按 X.Y.Z 数值比较,buildId 按数值比较(而非字符串)。
|
||||
* 无 buildId 的稳定版视为大于同 base 的任意 prerelease(与 semver 一致)。
|
||||
*/
|
||||
function compareVersionTags(a: string, b: string): number {
|
||||
const parse = (t: string): { base: number[]; build: number } => {
|
||||
const s = t.replace(/^v/i, '');
|
||||
const [base, build = ''] = s.split('-');
|
||||
const nums = base.split('.').map((n) => parseInt(n, 10) || 0);
|
||||
while (nums.length < 3) nums.push(0);
|
||||
return { base: nums, build: build === '' ? Number.MAX_SAFE_INTEGER : parseInt(build, 10) || 0 };
|
||||
};
|
||||
const pa = parse(a);
|
||||
const pb = parse(b);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (pa.base[i] !== pb.base[i]) return pa.base[i] - pb.base[i];
|
||||
}
|
||||
return pa.build - pb.build;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新服务:electron-updater(GitHub provider)+ 加速源兜底。
|
||||
* 状态:idle → checking → available → downloading ⇄ paused → downloaded → installing → quitAndInstall
|
||||
@@ -107,6 +128,11 @@ class UpdateService {
|
||||
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
// 项目版本方案为 {base}-{buildId}(如 1.2.1-5),属 semver prerelease(带 - 后缀)。
|
||||
// electron-updater 默认 allowPrerelease=false:GitHub provider 会过滤 prerelease release,
|
||||
// isUpdateAvailable 也会拒绝带 prerelease 后缀的新版本 → v1.2.1-4 永远检测不到 v1.2.1-5。
|
||||
// 必须显式开启(本项目的每个发布版本都是 prerelease 形式,不存在误升级稳定版的问题)。
|
||||
autoUpdater.allowPrerelease = true;
|
||||
autoUpdater.logger = console;
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
this.state = 'checking';
|
||||
@@ -240,7 +266,10 @@ class UpdateService {
|
||||
this.emit();
|
||||
await autoUpdater.checkForUpdates();
|
||||
this.suppressErrors = false;
|
||||
return this.buildPayload();
|
||||
// 镜像若反馈无更新(可能发现的是旧 tag / latest.yml 不匹配),
|
||||
// 不要就此返回 not-available,继续尝试下一个源
|
||||
if (this.state !== 'not-available') return this.buildPayload();
|
||||
console.warn(`[updater] ${mirror} 反馈无可用更新,尝试下一个源`);
|
||||
} catch (err) {
|
||||
console.warn(`[updater] 加速源 ${mirror} 检查失败: ${String((err as Error)?.message ?? err)}`);
|
||||
}
|
||||
@@ -254,11 +283,15 @@ class UpdateService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过加速源发现最新 release tag(不需要 GitHub API):
|
||||
* 1) /releases/latest 页面 HTML 中提取 tag(多数下载型加速源可代理该页面)
|
||||
* 2) 少数加速源可代理 api.github.com,作为备选
|
||||
* 通过加速源发现最新 release tag:
|
||||
* 1) /releases/latest 页面 HTML 中提取 tag(多数下载型加速源可代理该页面;
|
||||
* ⚠️ 该页面只指向最新「非 prerelease」release,本项目所有版本都是 prerelease 形式,结果可能偏旧)
|
||||
* 2) GitHub API(经加速源代理,列表含 prerelease),作为备选
|
||||
* 最终取两种方式候选集中版本最大者(buildId 数值比较),避免旧 tag 覆盖新 prerelease。
|
||||
*/
|
||||
private async discoverLatestTag(mirror: string): Promise<string | null> {
|
||||
const candidates: string[] = [];
|
||||
|
||||
// 方式 1:HTML 页面
|
||||
try {
|
||||
const pageUrl = `${mirror}/https://github.com/${OWNER}/${REPO}/releases/latest`;
|
||||
@@ -269,7 +302,7 @@ class UpdateService {
|
||||
if (res.ok) {
|
||||
const html = await res.text();
|
||||
const m = html.match(/\/releases\/tag\/(v[^"<]+)/);
|
||||
if (m?.[1]) return m[1];
|
||||
if (m?.[1]) candidates.push(m[1]);
|
||||
}
|
||||
} catch {
|
||||
/* 尝试下一种方式 */
|
||||
@@ -284,14 +317,21 @@ class UpdateService {
|
||||
});
|
||||
if (res.ok) {
|
||||
const releases: { draft?: boolean; tag_name?: string }[] = await res.json();
|
||||
const release = (releases ?? []).find((r) => !r.draft && !!r.tag_name);
|
||||
if (release?.tag_name) return release.tag_name;
|
||||
for (const r of releases ?? []) {
|
||||
if (!r.draft && r.tag_name) candidates.push(r.tag_name);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
return null;
|
||||
if (candidates.length === 0) return null;
|
||||
candidates.sort(compareVersionTags);
|
||||
const latest = candidates[candidates.length - 1];
|
||||
if (candidates.length > 1) {
|
||||
console.log(`[updater] ${mirror} 候选版本: ${candidates.join(', ')} → 取 ${latest}`);
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user