electrobun 0.0.19-beta.74 → 0.0.19-beta.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +3 -1
  2. package/src/cli/index.ts +84 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrobun",
3
- "version": "0.0.19-beta.74",
3
+ "version": "0.0.19-beta.75",
4
4
  "description": "Build ultra fast, tiny, and cross-platform desktop apps with Typescript.",
5
5
  "license": "MIT",
6
6
  "author": "Blackboard Technologies Inc.",
@@ -48,10 +48,12 @@
48
48
  "build:push:artifacts": "bun scripts/build-and-upload-artifacts.js"
49
49
  },
50
50
  "devDependencies": {
51
+ "@types/archiver": "^6.0.3",
51
52
  "@types/bun": "1.1.9"
52
53
  },
53
54
  "dependencies": {
54
55
  "@oneidentity/zstd-js": "^1.0.3",
56
+ "archiver": "^7.0.1",
55
57
  "rpc-anywhere": "1.5.0",
56
58
  "tar": "^6.2.1"
57
59
  }
package/src/cli/index.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "fs";
15
15
  import { execSync } from "child_process";
16
16
  import tar from "tar";
17
+ import archiver from "archiver";
17
18
  import { ZstdInit } from "@oneidentity/zstd-js/wasm";
18
19
  import { OS, ARCH } from '../shared/platform';
19
20
  import { getTemplate, getTemplateNames } from './templates/embedded';
@@ -22,7 +23,7 @@ import { getTemplate, getTemplateNames } from './templates/embedded';
22
23
  const MAX_CHUNK_SIZE = 1024 * 2;
23
24
 
24
25
 
25
- const binExt = OS === 'win' ? '.exe' : '';
26
+ // const binExt = OS === 'win' ? '.exe' : '';
26
27
 
27
28
  // this when run as an npm script this will be where the folder where package.json is.
28
29
  const projectRoot = process.cwd();
@@ -611,6 +612,7 @@ const buildSubFolder = `${buildEnvironment}-${currentTarget.os}-${currentTarget.
611
612
  // Use target OS/ARCH for build logic (instead of current machine's OS/ARCH)
612
613
  const targetOS = currentTarget.os;
613
614
  const targetARCH = currentTarget.arch;
615
+ const targetBinExt = targetOS === 'win' ? '.exe' : '';
614
616
 
615
617
  const buildFolder = join(projectRoot, config.build.buildFolder, buildSubFolder);
616
618
 
@@ -846,7 +848,7 @@ if (commandArg === "init") {
846
848
  // Only copy launcher for non-dev builds
847
849
  if (buildEnvironment !== "dev") {
848
850
  const bunCliLauncherBinarySource = targetPaths.LAUNCHER_RELEASE;
849
- const bunCliLauncherDestination = join(appBundleMacOSPath, "launcher") + binExt;
851
+ const bunCliLauncherDestination = join(appBundleMacOSPath, "launcher") + targetBinExt;
850
852
  const destLauncherFolder = dirname(bunCliLauncherDestination);
851
853
  if (!existsSync(destLauncherFolder)) {
852
854
  // console.info('creating folder: ', destFolder);
@@ -866,7 +868,7 @@ if (commandArg === "init") {
866
868
  const bunBinarySourcePath = targetPaths.BUN_BINARY;
867
869
  // Note: .bin/bun binary in node_modules is a symlink to the versioned one in another place
868
870
  // in node_modules, so we have to dereference here to get the actual binary in the bundle.
869
- const bunBinaryDestInBundlePath = join(appBundleMacOSPath, "bun") + binExt;
871
+ const bunBinaryDestInBundlePath = join(appBundleMacOSPath, "bun") + targetBinExt;
870
872
  const destFolder2 = dirname(bunBinaryDestInBundlePath);
871
873
  if (!existsSync(destFolder2)) {
872
874
  // console.info('creating folder: ', destFolder);
@@ -1172,7 +1174,7 @@ if (commandArg === "init") {
1172
1174
 
1173
1175
  // copy native bindings
1174
1176
  const bsPatchSource = targetPaths.BSPATCH;
1175
- const bsPatchDestination = join(appBundleMacOSPath, "bspatch") + binExt;
1177
+ const bsPatchDestination = join(appBundleMacOSPath, "bspatch") + targetBinExt;
1176
1178
  const bsPatchDestFolder = dirname(bsPatchDestination);
1177
1179
  if (!existsSync(bsPatchDestFolder)) {
1178
1180
  mkdirSync(bsPatchDestFolder, { recursive: true });
@@ -1501,7 +1503,13 @@ if (commandArg === "init") {
1501
1503
  targetPaths,
1502
1504
  buildEnvironment
1503
1505
  );
1504
- artifactsToUpload.push(selfExtractingExePath);
1506
+
1507
+ // Wrap Windows exe in zip for native Windows support
1508
+ const wrappedExePath = await wrapInArchive(selfExtractingExePath, buildFolder, 'zip');
1509
+ artifactsToUpload.push(wrappedExePath);
1510
+
1511
+ // Also keep the raw exe for backwards compatibility (optional)
1512
+ // artifactsToUpload.push(selfExtractingExePath);
1505
1513
  } else if (targetOS === 'linux') {
1506
1514
  // Create desktop file for Linux
1507
1515
  const desktopFileContent = `[Desktop Entry]
@@ -1554,7 +1562,13 @@ exec "\$LAUNCHER_BINARY" "\$@"
1554
1562
  targetPaths,
1555
1563
  buildEnvironment
1556
1564
  );
1557
- artifactsToUpload.push(selfExtractingLinuxPath);
1565
+
1566
+ // Wrap Linux .run file in tar.gz to preserve permissions
1567
+ const wrappedRunPath = await wrapInArchive(selfExtractingLinuxPath, buildFolder, 'tar.gz');
1568
+ artifactsToUpload.push(wrappedRunPath);
1569
+
1570
+ // Also keep the raw .run for backwards compatibility (optional)
1571
+ // artifactsToUpload.push(selfExtractingLinuxPath);
1558
1572
 
1559
1573
  // On Linux, create a tar.gz of the bundle
1560
1574
  const linuxTarPath = join(buildFolder, `${appFileName}.tar.gz`);
@@ -1969,6 +1983,70 @@ async function createLinuxSelfExtractingBinary(
1969
1983
  return outputPath;
1970
1984
  }
1971
1985
 
1986
+ async function wrapInArchive(filePath: string, buildFolder: string, archiveType: 'tar.gz' | 'zip'): Promise<string> {
1987
+ const fileName = basename(filePath);
1988
+ const fileDir = dirname(filePath);
1989
+
1990
+ if (archiveType === 'tar.gz') {
1991
+ // Output filename: Setup.exe -> Setup.exe.tar.gz or Setup.run -> Setup.run.tar.gz
1992
+ const archivePath = filePath + '.tar.gz';
1993
+
1994
+ // For Linux files, ensure they have executable permissions before archiving
1995
+ if (fileName.endsWith('.run')) {
1996
+ try {
1997
+ // Try to set executable permissions (will only work on Unix-like systems)
1998
+ execSync(`chmod +x ${escapePathForTerminal(filePath)}`, { stdio: 'ignore' });
1999
+ } catch {
2000
+ // Ignore errors on Windows hosts
2001
+ }
2002
+ }
2003
+
2004
+ // Create tar.gz archive preserving permissions
2005
+ // Using the tar package for cross-platform compatibility
2006
+ await tar.c(
2007
+ {
2008
+ gzip: true,
2009
+ file: archivePath,
2010
+ cwd: fileDir,
2011
+ portable: true, // Ensures consistent behavior across platforms
2012
+ preservePaths: false,
2013
+ // The tar package should preserve file modes when creating archives
2014
+ },
2015
+ [fileName]
2016
+ );
2017
+
2018
+ console.log(`Created archive: ${archivePath} (preserving executable permissions)`);
2019
+ return archivePath;
2020
+ } else if (archiveType === 'zip') {
2021
+ // Output filename: Setup.exe -> Setup.zip
2022
+ const archivePath = filePath.replace(/\.[^.]+$/, '.zip');
2023
+
2024
+ // Create zip archive
2025
+ const output = createWriteStream(archivePath);
2026
+ const archive = archiver('zip', {
2027
+ zlib: { level: 9 } // Maximum compression
2028
+ });
2029
+
2030
+ return new Promise((resolve, reject) => {
2031
+ output.on('close', () => {
2032
+ console.log(`Created archive: ${archivePath} (${(archive.pointer() / 1024 / 1024).toFixed(2)} MB)`);
2033
+ resolve(archivePath);
2034
+ });
2035
+
2036
+ archive.on('error', (err) => {
2037
+ reject(err);
2038
+ });
2039
+
2040
+ archive.pipe(output);
2041
+
2042
+ // Add the file to the archive
2043
+ archive.file(filePath, { name: fileName });
2044
+
2045
+ archive.finalize();
2046
+ });
2047
+ }
2048
+ }
2049
+
1972
2050
  async function createAppImage(buildFolder: string, appBundlePath: string, appFileName: string, config: any): Promise<string | null> {
1973
2051
  try {
1974
2052
  console.log("Creating AppImage...");