mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2025-12-19 18:18:27 -05:00
fix(client): .app.tar.gz exam bin is macos (#64095)
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user