Files
NapCatQQ/packages/napcat-webui-frontend/src/utils/version.ts
手瓜一十雪 8eb1aa2fb4 Refactor GitHub tag fetching and mirror management
Replaces legacy tag fetching logic in napcat-common with a new mirror.ts module that centralizes GitHub mirror configuration, selection, and tag retrieval. Updates helper.ts to use the new mirror system and semver comparison, and exports compareSemVer for broader use. Updates workflows and scripts to generate and propagate build version information, and improves build status comment formatting for PRs. Also updates release workflow to use a new OpenAI key and model.
2026-01-03 14:42:24 +08:00

134 lines
4.1 KiB
TypeScript

/**
* SemVer 2.0 正则表达式
* 格式: 主版本号.次版本号.修订号[-先行版本号][+版本编译信息]
* 参考: https://semver.org/lang/zh-CN/
*/
const SEMVER_REGEX = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
interface SemVerInfo {
valid: boolean;
normalized: string;
major: number;
minor: number;
patch: number;
prerelease: string | null;
buildmetadata: string | null;
}
/**
* 解析版本号
* @param version 版本字符串
* @returns SemVer 解析结果
*/
export const parseSemVer = (version: string | undefined | null): SemVerInfo => {
if (!version || typeof version !== 'string') {
return { valid: false, normalized: '1.0.0-dev', major: 1, minor: 0, patch: 0, prerelease: 'dev', buildmetadata: null };
}
const match = version.trim().match(SEMVER_REGEX);
if (match) {
const major = parseInt(match[1]!, 10);
const minor = parseInt(match[2]!, 10);
const patch = parseInt(match[3]!, 10);
const prerelease = match[4] || null;
const buildmetadata = match[5] || null;
let normalized = `${major}.${minor}.${patch}`;
if (prerelease) normalized += `-${prerelease}`;
if (buildmetadata) normalized += `+${buildmetadata}`;
return { valid: true, normalized, major, minor, patch, prerelease, buildmetadata };
}
return { valid: false, normalized: '1.0.0-dev', major: 1, minor: 0, patch: 0, prerelease: 'dev', buildmetadata: null };
};
/**
* 版本号转为数字 (兼容旧代码)
* @param version 版本号
* @returns 版本号数字
*/
export const versionToNumber = (version: string): number => {
const info = parseSemVer(version);
return info.patch + info.minor * 100 + info.major * 10000;
};
/**
* 比较版本号 (SemVer 2.0 规范)
* @param version1 版本号1
* @param version2 版本号2
* @returns 比较结果
* 0: 相等
* 1: version1 > version2
* -1: version1 < version2
*/
export const compareVersion = (version1: string, version2: string): -1 | 0 | 1 => {
const a = parseSemVer(version1);
const b = parseSemVer(version2);
if (!a.valid || !b.valid) {
return 0;
}
// 比较主版本号
if (a.major !== b.major) return a.major > b.major ? 1 : -1;
// 比较次版本号
if (a.minor !== b.minor) return a.minor > b.minor ? 1 : -1;
// 比较修订号
if (a.patch !== b.patch) return a.patch > b.patch ? 1 : -1;
// 有先行版本号的版本优先级较低
if (a.prerelease && !b.prerelease) return -1;
if (!a.prerelease && b.prerelease) return 1;
// 两者都有先行版本号时,按规则比较
if (a.prerelease && b.prerelease) {
const aParts = a.prerelease.split('.');
const bParts = b.prerelease.split('.');
const len = Math.max(aParts.length, bParts.length);
for (let i = 0; i < len; i++) {
const aPart = aParts[i];
const bPart = bParts[i];
if (aPart === undefined) return -1;
if (bPart === undefined) return 1;
const aNum = /^\d+$/.test(aPart) ? parseInt(aPart, 10) : NaN;
const bNum = /^\d+$/.test(bPart) ? parseInt(bPart, 10) : NaN;
// 数字 vs 数字
if (!isNaN(aNum) && !isNaN(bNum)) {
if (aNum !== bNum) return aNum > bNum ? 1 : -1;
continue;
}
// 数字优先级低于字符串
if (!isNaN(aNum)) return -1;
if (!isNaN(bNum)) return 1;
// 字符串 vs 字符串
if (aPart !== bPart) return aPart > bPart ? 1 : -1;
}
}
return 0;
};
/**
* 判断是否有新版本可用
* 只比较正式版本 (不带先行版本号的)
* 当前版本是先行版本时,与相同基础版本的正式版相比认为需要更新
* @param currentVersion 当前版本
* @param latestVersion 最新版本 (release tag)
* @returns 是否有新版本
*/
export const hasNewVersion = (currentVersion: string, latestVersion: string): boolean => {
const current = parseSemVer(currentVersion);
const latest = parseSemVer(latestVersion);
if (!current.valid || !latest.valid) {
return false;
}
// 使用 compareVersion 比较
return compareVersion(latestVersion, currentVersion) > 0;
};