Skip to content

Commit a1efed9

Browse files
Better binary for zips containing binaries
1 parent 06f112d commit a1efed9

3 files changed

Lines changed: 57 additions & 53 deletions

File tree

lib/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ const createInstallationRecord = (source, selected, metadata = {}) => {
101101

102102
const extractName = (selected) => {
103103
return selected.name
104-
.replace(/\.(tar\.gz|zip|dmg|pkg|deb|app)$/i, "")
104+
.replace(/\.(tar\.gz|tar\.xz|zip|dmg|pkg|deb|app)$/i, "")
105105
.replace(/v?[0-9]+\.[0-9]+\.[0-9]+/i, "")
106106
.replace(/[-_]+(?:darwin|linux|windows|mac|osx|apple|x64|arm64|aarch64|universal|amd64)[-_]*/gi, "")
107107
.replace(/(?:darwin|linux|windows|mac|osx|apple|x64|arm64|aarch64|universal|amd64)[-_]*/gi, "")

lib/installer.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ const performInstallation = async (args, isUpdate = false, yesFlag = false) => {
144144
selected,
145145
downloadPath,
146146
log,
147+
yesFlag,
147148
);
148149

149150
if (!isUpdate) {
@@ -364,7 +365,7 @@ const displayScriptList = (installScripts, log) => {
364365
log.log(` e. Edit selected script before running`);
365366
};
366367

367-
const handleSingleScript = async (script, log) => {
368+
const handleSingleScript = async (script, log, yesFlag = false) => {
368369
displayScriptPreview(script, log);
369370

370371
while (true) {
@@ -384,7 +385,7 @@ const handleSingleScript = async (script, log) => {
384385
if (choice === 'e') {
385386
const editedScript = await editScriptInEditor(script);
386387
displayScriptPreview(editedScript, log);
387-
388+
388389
const shouldRun = await confirm(
389390
"Run this edited install script (y) or continue to regular installation (n)?",
390391
"y",
@@ -401,7 +402,7 @@ const handleSingleScript = async (script, log) => {
401402
}
402403
};
403404

404-
const handleMultipleScripts = async (installScripts, log) => {
405+
const handleMultipleScripts = async (installScripts, log, yesFlag = false) => {
405406
const CONTINUE_OPTION = installScripts.length + 1;
406407
const EXIT_OPTION = installScripts.length + 2;
407408

@@ -430,7 +431,7 @@ const handleMultipleScripts = async (installScripts, log) => {
430431
const scriptToEdit = installScripts[scriptChoice - 1];
431432
const editedScript = await editScriptInEditor(scriptToEdit);
432433
displayScriptPreview(editedScript, log);
433-
434+
434435
const shouldRun = await confirm(
435436
"Run this edited install script (y) or choose a different one (n)?",
436437
"y",
@@ -464,14 +465,14 @@ const handleMultipleScripts = async (installScripts, log) => {
464465
}
465466
};
466467

467-
const selectInstallScript = async (installScripts, log) => {
468+
const selectInstallScript = async (installScripts, log, yesFlag = false) => {
468469
if (installScripts.length === 0) return null;
469470

470471
if (installScripts.length === 1) {
471-
return await handleSingleScript(installScripts[0], log);
472+
return await handleSingleScript(installScripts[0], log, yesFlag);
472473
}
473474

474-
return await handleMultipleScripts(installScripts, log);
475+
return await handleMultipleScripts(installScripts, log, yesFlag);
475476
};
476477

477478
const executeInstallScript = (script, log) => {
@@ -569,7 +570,7 @@ const handleGitHubSource = async (source, platformInfo, capabilities, log) => {
569570
}
570571
log.debug(`Found ${installScripts.length} install script(s)`);
571572

572-
const selectedScript = await selectInstallScript(installScripts, log);
573+
const selectedScript = await selectInstallScript(installScripts, log, yesFlag);
573574

574575
if (selectedScript) {
575576
executeInstallScript(selectedScript, log);
@@ -626,7 +627,7 @@ const downloadSelected = async (selected, source, log) => {
626627
return downloadPath;
627628
};
628629

629-
const installSelected = async (selected, downloadPath, log) => {
630+
const installSelected = async (selected, downloadPath, log, yesFlag = false) => {
630631
const outputDir = path.join(tmpdir, "outputs");
631632
fs.mkdirSync(outputDir, { recursive: true });
632633

@@ -714,8 +715,9 @@ const installSelected = async (selected, downloadPath, log) => {
714715
yesFlag
715716
);
716717
binariesList = [selected.name];
717-
// Ask to open app
718-
if (await confirm(`Open app ${path.basename(destinations[0])}?`, "y", yesFlag)) {
718+
719+
// Ask to open app
720+
if (await confirm(`Open app ${path.basename(destinations[0])}?`, "y", yesFlag)) {
719721
execSync(`open -n ${JSON.stringify(destinations[0])}`);
720722
}
721723
break;
@@ -726,7 +728,7 @@ const installSelected = async (selected, downloadPath, log) => {
726728

727729
if (
728730
selected.extension &&
729-
["tar.gz", "zip", "tar.zst"].includes(selected.extension)
731+
["tar.gz", "zip", "tar.zst", "tar.xz"].includes(selected.extension)
730732
) {
731733
extractArchive(downloadPath, outputDir, selected.extension);
732734
} else {
@@ -758,9 +760,9 @@ const installSelected = async (selected, downloadPath, log) => {
758760
installationMethod = packageResult.method;
759761
destinations = packageResult.destinations;
760762
binariesList = packageResult.binaries;
761-
if (installationMethod === "archive_app") {
763+
if (installationMethod === "archive_app") {
762764
// Ask to open app
763-
if (await confirm(`Open app ${path.basename(destinations[0])}?`, "y", yesFlag)) {
765+
if (await confirm(`Open app ${path.basename(destinations[0])}?`, "y", yesFlag)) {
764766
execSync(`open -n ${JSON.stringify(destinations[0])}`);
765767
}
766768
}

lib/installers.js

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -245,15 +245,15 @@ const getBinaries = (dir) => {
245245
if (appDirectories.some((app) => f.startsWith(app + "/"))) {
246246
return false;
247247
}
248-
248+
249249
const filePath = path.resolve(dir, f);
250250
const fileOutput = execSync(`file ${JSON.stringify(filePath)}`).toString();
251-
251+
252252
// Check if it's an executable binary or script
253-
return fileOutput.includes("executable") ||
254-
fileOutput.includes("ELF") ||
255-
fileOutput.includes("Mach-O") ||
256-
fileOutput.includes("script");
253+
return fileOutput.includes("executable") ||
254+
fileOutput.includes("ELF") ||
255+
fileOutput.includes("Mach-O") ||
256+
fileOutput.includes("script");
257257
} catch {
258258
return false;
259259
}
@@ -263,40 +263,40 @@ const getBinaries = (dir) => {
263263
const looksLikeBinary = (f) => {
264264
const filename = path.basename(f).toLowerCase();
265265
const dirname = path.dirname(f).toLowerCase();
266-
266+
267267
// Skip files in common non-binary directories
268-
if (dirname.includes("doc") || dirname.includes("docs") || dirname.includes("man") ||
269-
dirname.includes("runtime") || dirname.includes("lib") || dirname.includes("share")) {
268+
if (dirname.includes("doc") || dirname.includes("docs") || dirname.includes("man") ||
269+
dirname.includes("runtime") || dirname.includes("lib") || dirname.includes("share")) {
270270
return false;
271271
}
272-
272+
273273
// Skip obvious non-binary files
274-
if (filename.includes("readme") || filename.includes("license") ||
275-
filename.includes("changelog") || filename.includes("copying") ||
276-
filename.includes("install") || filename.includes("makefile") ||
277-
filename.includes(".md") || filename.includes(".txt") ||
278-
filename.includes(".1") || filename.includes(".json") ||
279-
filename.includes(".yaml") || filename.includes(".yml") ||
280-
filename.includes(".toml") || filename.includes(".cfg") ||
281-
filename.includes(".conf") || filename.includes(".ini")) {
274+
if (filename.includes("readme") || filename.includes("license") ||
275+
filename.includes("changelog") || filename.includes("copying") ||
276+
filename.includes("install") || filename.includes("makefile") ||
277+
filename.includes(".md") || filename.includes(".txt") ||
278+
filename.includes(".1") || filename.includes(".json") ||
279+
filename.includes(".yaml") || filename.includes(".yml") ||
280+
filename.includes(".toml") || filename.includes(".cfg") ||
281+
filename.includes(".conf") || filename.includes(".ini")) {
282282
return false;
283283
}
284-
284+
285285
// Common binary naming patterns
286286
const binaryPatterns = [
287287
/^[a-z0-9_-]+$/, // Simple name like hx, git, node
288288
/^[a-z0-9_-]+\.(exe|bin|run)$/, // Extensions
289289
];
290-
290+
291291
return binaryPatterns.some(pattern => pattern.test(filename));
292292
};
293293

294-
const executableFiles = allFiles.filter(isExecutable);
295-
const potentialBinaries = allFiles.filter(looksLikeBinary);
296-
294+
const executableFiles = allFiles.filter(f => isExecutable(f) && looksLikeBinary(f));
295+
const potentialBinaries = allFiles.filter(f => looksLikeBinary(f) && !executableFiles.includes(f));
296+
297297
// Combine and remove duplicates
298298
binaries.push(...executableFiles);
299-
binaries.push(...potentialBinaries.filter(f => !executableFiles.includes(f)));
299+
binaries.push(...potentialBinaries);
300300

301301
return [...new Set(binaries)]; // Remove duplicates
302302
};
@@ -316,15 +316,15 @@ const selectBinaries = async (binaries, packageName, logger = null, yesFlag = fa
316316
// Filter out obvious non-binaries for cleaner selection
317317
const filteredBinaries = binaries.filter(f => {
318318
const filename = path.basename(f).toLowerCase();
319-
return !filename.includes('readme') &&
320-
!filename.includes('license') &&
321-
!filename.includes('changelog') &&
322-
!filename.includes('.md') &&
323-
!filename.includes('.txt') &&
324-
!filename.includes('.1') && // man pages
325-
!f.includes('doc/') &&
326-
!f.includes('docs/') &&
327-
!f.includes('man/');
319+
return !filename.includes('readme') &&
320+
!filename.includes('license') &&
321+
!filename.includes('changelog') &&
322+
!filename.includes('.md') &&
323+
!filename.includes('.txt') &&
324+
!filename.includes('.1') && // man pages
325+
!f.includes('doc/') &&
326+
!f.includes('docs/') &&
327+
!f.includes('man/');
328328
});
329329

330330
// If filtering leaves us with just one, use it
@@ -390,15 +390,17 @@ const processExtractedPackages = async (
390390
outputDir,
391391
selectedName,
392392
checkPathFn,
393-
logger
393+
logger,
394+
isMountedVolume = false,
395+
yesFlag = false
394396
) => {
395397
// If an .app bundle was extracted from the archive, install it as a macOS app
396398
const appBundle = binaries.find((f) => f.toLowerCase().endsWith(".app"));
397399
if (appBundle && process.platform === "darwin") {
398400
if (logger) {
399401
logger.log(`Installing .app bundle from archive: ${appBundle}`);
400402
}
401-
const destinations = await installApp(appBundle, outputDir, checkPathFn, logger);
403+
const destinations = await installApp(appBundle, outputDir, checkPathFn, logger, yesFlag);
402404
return {
403405
method: "archive_app",
404406
destinations,
@@ -428,7 +430,7 @@ const processExtractedPackages = async (
428430

429431
if (appFile) {
430432
logger.log(`Installing .app bundle from DMG: ${appFile}`);
431-
const destinations = await installApp(appFile, mountDir, checkPathFn, logger);
433+
const destinations = await installApp(appFile, mountDir, checkPathFn, logger, yesFlag);
432434
return {
433435
method: "dmg_app",
434436
destinations,
@@ -590,10 +592,10 @@ const installBinaries = async (
590592

591593
for (const binary of binaries) {
592594
const binaryPath = path.join(outputDir, binary);
593-
const cleanName = extractName({ name: selectedName });
595+
const cleanName = path.basename(binary);
594596
const dest = path.join(os.homedir(), ".local", "bin", cleanName);
595597

596-
await checkPathFn(dest, yesFlag);
598+
await checkPathFn(dest, yesFlag);
597599

598600
// Don't try to chmod files on mounted volumes (like DMGs)
599601
if (!isMountedVolume) {

0 commit comments

Comments
 (0)