- packages/shared: 版本号解析 vX.Y.Z-dav-{client}-{build} + manifest远程检查
- packages/client-android: Expo52 + expo-updates JS bundle OTA
- PackageInstallerModule.kt: APK静默安装原生模块(~60行Kotlin)
- monorepo: pnpm workspace + 版本号体系
83 lines
2.4 KiB
TypeScript
83 lines
2.4 KiB
TypeScript
/** 版本号解析 vX.Y.Z-dav-{client}-{build} */
|
||
export interface GcaVersion {
|
||
major: number;
|
||
minor: number;
|
||
patch: number;
|
||
client: 'desktop' | 'android' | 'cli';
|
||
build: number;
|
||
raw: string;
|
||
}
|
||
|
||
export interface Manifest {
|
||
version: string;
|
||
builds: Record<string, ManifestBuild>;
|
||
}
|
||
|
||
export interface ManifestBuild {
|
||
tag: string;
|
||
url: string;
|
||
sha256: string;
|
||
size: number;
|
||
minVersion?: string;
|
||
}
|
||
|
||
const MANIFEST_URL = 'https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/manifest/manifest.json';
|
||
|
||
const VERSION_RE = /^v(\d+)\.(\d+)\.(\d+)-dav-(desktop|android|cli)-(\d+)$/;
|
||
|
||
export function parseVersion(raw: string): GcaVersion | null {
|
||
const m = raw.trim().match(VERSION_RE);
|
||
if (!m) return null;
|
||
return {
|
||
major: parseInt(m[1], 10),
|
||
minor: parseInt(m[2], 10),
|
||
patch: parseInt(m[3], 10),
|
||
client: m[4] as GcaVersion['client'],
|
||
build: parseInt(m[5], 10),
|
||
raw,
|
||
};
|
||
}
|
||
|
||
export function compareVersion(a: GcaVersion, b: GcaVersion): number {
|
||
if (a.major !== b.major) return a.major - b.major;
|
||
if (a.minor !== b.minor) return a.minor - b.minor;
|
||
if (a.patch !== b.patch) return a.patch - b.patch;
|
||
return a.build - b.build;
|
||
}
|
||
|
||
/** 调用方传入本地版本号,返回是否需要更新以及目标 manifest entry */
|
||
export async function checkUpdate(
|
||
client: GcaVersion['client'],
|
||
localVersion: GcaVersion,
|
||
manifestUrl = MANIFEST_URL,
|
||
): Promise<{ shouldUpdate: boolean; build: ManifestBuild | null; remoteVersion: GcaVersion | null }> {
|
||
try {
|
||
const resp = await fetch(manifestUrl);
|
||
if (!resp.ok) return { shouldUpdate: false, build: null, remoteVersion: null };
|
||
|
||
const manifest: Manifest = await resp.json();
|
||
const build = manifest.builds[client];
|
||
if (!build) return { shouldUpdate: false, build: null, remoteVersion: null };
|
||
|
||
const remoteVersion = parseVersion(build.tag);
|
||
if (!remoteVersion) return { shouldUpdate: false, build: null, remoteVersion: null };
|
||
|
||
return {
|
||
shouldUpdate: compareVersion(remoteVersion, localVersion) > 0,
|
||
build,
|
||
remoteVersion,
|
||
};
|
||
} catch {
|
||
return { shouldUpdate: false, build: null, remoteVersion: null };
|
||
}
|
||
}
|
||
|
||
/** SemVer 比较(用于 expo-updates manifest) */
|
||
export function semverGt(a: string, b: string): boolean {
|
||
const [aM, am, aP] = a.split('.').map(Number);
|
||
const [bM, bm, bP] = b.split('.').map(Number);
|
||
if (aM !== bM) return aM > bM;
|
||
if (am !== bm) return am > bm;
|
||
return aP > bP;
|
||
}
|