Files
multica/apps/web/features/landing/utils/parse-release-assets.ts
Jiayuan Zhang 47e9c8add5 fix(web): add macOS Intel downloads to landing page (#5517)
Co-authored-by: Lambda <lambda@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-16 15:11:27 +08:00

101 lines
3.5 KiB
TypeScript

/**
* Parses the GitHub Releases API asset array into a structured
* download asset map. Skips auxiliary files (blockmaps, update
* manifests, checksums) and the CLI tarballs — only desktop
* installer artifacts are relevant on the /download page.
*
* Desktop artifact naming (see apps/desktop/electron-builder.yml):
* multica-desktop-{version}-mac-{arch}.{dmg|zip}
* multica-desktop-{version}-windows-{arch}.exe
* multica-desktop-{version}-linux-{arch}.{AppImage|deb|rpm}
*
* Linux arch appears as amd64 / x86_64 / arm64 / aarch64 depending
* on the format; we normalize to amd64 and arm64.
*/
export interface GitHubAsset {
name: string;
browser_download_url: string;
}
export interface DownloadAssets {
macArm64Dmg?: string;
macArm64Zip?: string;
macX64Dmg?: string;
macX64Zip?: string;
winX64Exe?: string;
winArm64Exe?: string;
linuxAmd64AppImage?: string;
linuxAmd64Deb?: string;
linuxAmd64Rpm?: string;
linuxArm64AppImage?: string;
linuxArm64Deb?: string;
linuxArm64Rpm?: string;
}
const DESKTOP_ARTIFACT_RE =
/^multica-desktop-[^-]+-(mac|windows|linux)-([a-z0-9_]+)\.(dmg|zip|exe|AppImage|deb|rpm)$/i;
function normalizeLinuxArch(arch: string): "amd64" | "arm64" | null {
const a = arch.toLowerCase();
if (a === "amd64" || a === "x86_64") return "amd64";
if (a === "arm64" || a === "aarch64") return "arm64";
return null;
}
export function parseReleaseAssets(raw: GitHubAsset[]): DownloadAssets {
const out: DownloadAssets = {};
for (const asset of raw) {
const name = asset.name;
// Skip auxiliary files that share the release (update manifests,
// blockmaps, checksums). CLI tarballs and other non-desktop
// artifacts are excluded automatically because they don't match
// DESKTOP_ARTIFACT_RE below.
if (name.endsWith(".blockmap") || name.endsWith(".yml")) continue;
if (name.startsWith("checksums")) continue;
const match = DESKTOP_ARTIFACT_RE.exec(name);
if (!match) continue;
const platform = match[1];
const arch = match[2];
const ext = match[3];
if (!platform || !arch || !ext) continue;
const archLower = arch.toLowerCase();
const extLower = ext.toLowerCase();
const url = asset.browser_download_url;
if (platform === "mac") {
if (archLower === "arm64") {
if (extLower === "dmg") out.macArm64Dmg = url;
else if (extLower === "zip") out.macArm64Zip = url;
} else if (archLower === "x64") {
if (extLower === "dmg") out.macX64Dmg = url;
else if (extLower === "zip") out.macX64Zip = url;
}
} else if (platform === "windows") {
if (extLower !== "exe") continue;
if (archLower === "x64") out.winX64Exe = url;
else if (archLower === "arm64") out.winArm64Exe = url;
} else if (platform === "linux") {
const normalized = normalizeLinuxArch(arch);
if (!normalized) continue;
const e = extLower;
if (normalized === "amd64") {
if (e === "appimage") out.linuxAmd64AppImage = url;
else if (e === "deb") out.linuxAmd64Deb = url;
else if (e === "rpm") out.linuxAmd64Rpm = url;
} else {
if (e === "appimage") out.linuxArm64AppImage = url;
else if (e === "deb") out.linuxArm64Deb = url;
else if (e === "rpm") out.linuxArm64Rpm = url;
}
}
}
return out;
}
/** Whether any desktop asset was parsed out. Used for UI degradation. */
export function hasAnyAsset(assets: DownloadAssets): boolean {
return Object.values(assets).some((v) => typeof v === "string");
}