fix(client): .app.tar.gz exam bin is macos (#64095)

This commit is contained in:
Shaun Hamilton
2025-12-11 04:29:20 +02:00
committed by GitHub
parent 51b6a310ac
commit dbc5dd4f4a

View File

@@ -11,7 +11,6 @@ import {
Row,
Col
} from '@freecodecamp/ui';
import { isEmpty } from 'lodash';
import { useTranslation, withTranslation } from 'react-i18next';
import { createSelector } from 'reselect';
import { connect } from 'react-redux';
@@ -108,50 +107,72 @@ function ShowExamDownload({
const { t } = useTranslation();
function handleDownloadLink(downloadLinks: string[]) {
const win = downloadLinks.find(link => link.match(/\.exe/));
const macARM = downloadLinks.find(
link => link.match(/aarch64/) && link.match(/\.dmg/)
);
const macX64 = downloadLinks.find(
link => link.match(/x64/) && link.match(/\.dmg/)
);
// Filter out signature and metadata files first
const filtered = downloadLinks.filter(link => !/\.(sig|json)$/i.test(link));
const linuxARM = downloadLinks.find(
link => link.match(/aarch64/) && link.match(/tar\.gz/)
);
function normalizeArch(token: string): string {
if (!token) return '';
const t = token.toLowerCase();
if (/aarch64|arm64|arm/i.test(t)) return 'arm';
if (/x86_64|x64|amd64/i.test(t)) return 'x64';
if (/x86|i386|i686/i.test(t)) return 'x86';
return t;
}
const linuxX64 = downloadLinks.find(
link => link.match(/amd64/) && link.match(/AppImage/)
);
const items = filtered.map(link => {
const urlEnd = link.split('/').pop() ?? '';
const name = urlEnd;
let ext = '';
if (name.endsWith('.app.tar.gz')) ext = '.app.tar.gz';
else if (name.endsWith('.tar.gz')) ext = '.tar.gz';
else if (name.endsWith('.AppImage')) ext = '.AppImage';
else if (name.endsWith('.dmg')) ext = '.dmg';
else if (name.endsWith('.exe')) ext = '.exe';
else {
const m = name.match(/(\.[^./]+)$/);
ext = m ? m[0] : '';
}
const archMatch = name.match(
/(aarch64|arm64|amd64|x86_64|x64|x86|i386)/i
);
const archToken = archMatch ? normalizeArch(archMatch[0]) : '';
return { link, name, ext, arch: archToken };
});
const detectedArch = normalizeArch(os.architecture || '');
function pickByExts(exts: string[], preferArch?: string) {
// prefer both ext + arch
let found = items.find(
it => exts.includes(it.ext) && it.arch === preferArch
);
if (found) return found.link;
// then any with ext and unspecified arch
found = items.find(
it => exts.includes(it.ext) && (!preferArch || it.arch === '')
);
if (found) return found.link;
// then any with ext
found = items.find(it => exts.includes(it.ext));
return found ? found.link : '';
}
if (os.os === 'WIN') {
if (isEmpty(win)) return '';
return win;
return pickByExts(['.exe'], detectedArch) || '';
}
if (os.os === 'MAC') {
if (os.architecture.toLowerCase() === 'arm') {
if (isEmpty(macARM)) return '';
return macARM;
} else {
if (isEmpty(macX64)) return '';
return macX64;
}
// prefer .dmg files
return pickByExts(['.dmg'], detectedArch) || '';
}
if (os.os === 'LINUX') {
if (os.architecture.toLowerCase() === 'arm') {
if (isEmpty(linuxARM)) return '';
return linuxARM;
} else {
if (isEmpty(linuxX64)) return '';
return linuxX64;
}
// prefer AppImage, then .app.tar.gz, then .tar.gz
return (
pickByExts(['.AppImage', '.app.tar.gz', '.tar.gz'], detectedArch) || ''
);
}
return '';
@@ -337,31 +358,22 @@ function getRecommendedOs({
arch: string;
ext: string;
}): string {
switch (arch) {
case 'x64':
switch (ext) {
case '.dmg':
return 'x64 MacOS';
case '.AppImage':
case '.app.tar.gz':
return 'x64 Linux';
default:
return 'x64 Windows';
}
case 'aarch64':
switch (ext) {
case '.dmg':
return 'ARM MacOS';
case '.app.tar.gz':
return 'ARM Linux';
default:
return 'ARM Windows';
}
case 'amd64':
return 'x64 Linux';
default:
return '';
}
const osToExt = {
MacOS: ['.dmg', '.app', '.app.tar.gz'],
Linux: ['.deb', '.rpm', '.AppImage', '.tar.gz', '.AppImage.tar.gz'],
Windows: ['.exe', '.msi']
} as const;
const archToHuman: Record<string, string> = {
x64: '64-bit',
aarch64: 'ARM',
amd64: '64-bit',
i386: '32-bit',
x86: '32-bit'
};
const os = Object.entries(osToExt).find(([_, exts]) => exts.includes(ext));
return `${archToHuman[arch] ?? arch} ${os ? os[0] : ''}`;
}
function getLatest(releases: GitProps[]): GitProps {