electrobun 0.0.19-beta.58 → 0.0.19-beta.60
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.
- package/package.json +1 -1
- package/src/cli/index.ts +456 -168
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -65,56 +65,81 @@ const ELECTROBUN_DEP_PATH = join(projectRoot, "node_modules", "electrobun");
|
|
|
65
65
|
// When debugging electrobun with the example app use the builds (dev or release) right from the source folder
|
|
66
66
|
// For developers using electrobun cli via npm use the release versions in /dist
|
|
67
67
|
// This lets us not have to commit src build folders to git and provide pre-built binaries
|
|
68
|
-
const PATHS = {
|
|
69
|
-
BUN_BINARY: join(ELECTROBUN_DEP_PATH, "dist", "bun") + binExt,
|
|
70
|
-
LAUNCHER_DEV: join(ELECTROBUN_DEP_PATH, "dist", "electrobun") + binExt,
|
|
71
|
-
LAUNCHER_RELEASE: join(ELECTROBUN_DEP_PATH, "dist", "launcher") + binExt,
|
|
72
|
-
MAIN_JS: join(ELECTROBUN_DEP_PATH, "dist", "main.js"),
|
|
73
|
-
NATIVE_WRAPPER_MACOS: join(
|
|
74
|
-
ELECTROBUN_DEP_PATH,
|
|
75
|
-
"dist",
|
|
76
|
-
"libNativeWrapper.dylib"
|
|
77
|
-
),
|
|
78
|
-
NATIVE_WRAPPER_WIN: join(ELECTROBUN_DEP_PATH, "dist", "libNativeWrapper.dll"),
|
|
79
|
-
NATIVE_WRAPPER_LINUX: join(ELECTROBUN_DEP_PATH, "dist", "libNativeWrapper.so"),
|
|
80
|
-
NATIVE_WRAPPER_LINUX_CEF: join(ELECTROBUN_DEP_PATH, "dist", "libNativeWrapper_cef.so"),
|
|
81
|
-
WEBVIEW2LOADER_WIN: join(ELECTROBUN_DEP_PATH, "dist", "WebView2Loader.dll"),
|
|
82
|
-
BSPATCH: join(ELECTROBUN_DEP_PATH, "dist", "bspatch") + binExt,
|
|
83
|
-
EXTRACTOR: join(ELECTROBUN_DEP_PATH, "dist", "extractor") + binExt,
|
|
84
|
-
BSDIFF: join(ELECTROBUN_DEP_PATH, "dist", "bsdiff") + binExt,
|
|
85
|
-
CEF_FRAMEWORK_MACOS: join(
|
|
86
|
-
ELECTROBUN_DEP_PATH,
|
|
87
|
-
"dist",
|
|
88
|
-
"cef",
|
|
89
|
-
"Chromium Embedded Framework.framework"
|
|
90
|
-
),
|
|
91
|
-
CEF_HELPER_MACOS: join(ELECTROBUN_DEP_PATH, "dist", "cef", "process_helper"),
|
|
92
|
-
CEF_HELPER_WIN: join(ELECTROBUN_DEP_PATH, "dist", "cef", "process_helper.exe"),
|
|
93
|
-
CEF_HELPER_LINUX: join(ELECTROBUN_DEP_PATH, "dist", "cef", "process_helper"),
|
|
94
|
-
CEF_DIR: join(ELECTROBUN_DEP_PATH, "dist", "cef"),
|
|
95
|
-
};
|
|
96
68
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
69
|
+
// Function to get platform-specific paths
|
|
70
|
+
function getPlatformPaths(targetOS: 'macos' | 'win' | 'linux', targetArch: 'arm64' | 'x64') {
|
|
71
|
+
const binExt = targetOS === 'win' ? '.exe' : '';
|
|
72
|
+
const platformDistDir = join(ELECTROBUN_DEP_PATH, `dist-${targetOS}-${targetArch}`);
|
|
73
|
+
const sharedDistDir = join(ELECTROBUN_DEP_PATH, "dist");
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
// Platform-specific binaries (from dist-OS-ARCH/)
|
|
77
|
+
BUN_BINARY: join(platformDistDir, "bun") + binExt,
|
|
78
|
+
LAUNCHER_DEV: join(platformDistDir, "electrobun") + binExt,
|
|
79
|
+
LAUNCHER_RELEASE: join(platformDistDir, "launcher") + binExt,
|
|
80
|
+
NATIVE_WRAPPER_MACOS: join(platformDistDir, "libNativeWrapper.dylib"),
|
|
81
|
+
NATIVE_WRAPPER_WIN: join(platformDistDir, "libNativeWrapper.dll"),
|
|
82
|
+
NATIVE_WRAPPER_LINUX: join(platformDistDir, "libNativeWrapper.so"),
|
|
83
|
+
NATIVE_WRAPPER_LINUX_CEF: join(platformDistDir, "libNativeWrapper_cef.so"),
|
|
84
|
+
WEBVIEW2LOADER_WIN: join(platformDistDir, "WebView2Loader.dll"),
|
|
85
|
+
BSPATCH: join(platformDistDir, "bspatch") + binExt,
|
|
86
|
+
EXTRACTOR: join(platformDistDir, "extractor") + binExt,
|
|
87
|
+
BSDIFF: join(platformDistDir, "bsdiff") + binExt,
|
|
88
|
+
CEF_FRAMEWORK_MACOS: join(platformDistDir, "cef", "Chromium Embedded Framework.framework"),
|
|
89
|
+
CEF_HELPER_MACOS: join(platformDistDir, "cef", "process_helper"),
|
|
90
|
+
CEF_HELPER_WIN: join(platformDistDir, "cef", "process_helper.exe"),
|
|
91
|
+
CEF_HELPER_LINUX: join(platformDistDir, "cef", "process_helper"),
|
|
92
|
+
CEF_DIR: join(platformDistDir, "cef"),
|
|
93
|
+
|
|
94
|
+
// Shared platform-independent files (from dist/)
|
|
95
|
+
// These work with existing package.json and development workflow
|
|
96
|
+
MAIN_JS: join(sharedDistDir, "main.js"),
|
|
97
|
+
API_DIR: join(sharedDistDir, "api"),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Default PATHS for host platform (backward compatibility)
|
|
102
|
+
const PATHS = getPlatformPaths(OS, ARCH);
|
|
103
|
+
|
|
104
|
+
async function ensureCoreDependencies(targetOS?: 'macos' | 'win' | 'linux', targetArch?: 'arm64' | 'x64') {
|
|
105
|
+
// Use provided target platform or default to host platform
|
|
106
|
+
const platformOS = targetOS || OS;
|
|
107
|
+
const platformArch = targetArch || ARCH;
|
|
108
|
+
|
|
109
|
+
// Get platform-specific paths
|
|
110
|
+
const platformPaths = getPlatformPaths(platformOS, platformArch);
|
|
111
|
+
|
|
112
|
+
// Check platform-specific binaries
|
|
113
|
+
const requiredBinaries = [
|
|
114
|
+
platformPaths.BUN_BINARY,
|
|
115
|
+
platformPaths.LAUNCHER_RELEASE,
|
|
103
116
|
// Platform-specific native wrapper
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
platformOS === 'macos' ? platformPaths.NATIVE_WRAPPER_MACOS :
|
|
118
|
+
platformOS === 'win' ? platformPaths.NATIVE_WRAPPER_WIN :
|
|
119
|
+
platformPaths.NATIVE_WRAPPER_LINUX
|
|
107
120
|
];
|
|
108
121
|
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
// Check shared files (main.js should be in shared dist/)
|
|
123
|
+
const requiredSharedFiles = [
|
|
124
|
+
platformPaths.MAIN_JS
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
const missingBinaries = requiredBinaries.filter(file => !existsSync(file));
|
|
128
|
+
const missingSharedFiles = requiredSharedFiles.filter(file => !existsSync(file));
|
|
129
|
+
|
|
130
|
+
// If only shared files are missing, that's expected in production (they come via npm)
|
|
131
|
+
if (missingBinaries.length === 0 && missingSharedFiles.length > 0) {
|
|
132
|
+
console.log(`Shared files missing (expected in production): ${missingSharedFiles.map(f => f.replace(ELECTROBUN_DEP_PATH, '.')).join(', ')}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Only download if platform-specific binaries are missing
|
|
136
|
+
if (missingBinaries.length === 0) {
|
|
111
137
|
return;
|
|
112
138
|
}
|
|
113
139
|
|
|
114
|
-
// Show which
|
|
115
|
-
|
|
116
|
-
console.log(
|
|
117
|
-
console.log('Downloading core binaries...');
|
|
140
|
+
// Show which binaries are missing
|
|
141
|
+
console.log(`Core dependencies not found for ${platformOS}-${platformArch}. Missing files:`, missingBinaries.map(f => f.replace(ELECTROBUN_DEP_PATH, '.')).join(', '));
|
|
142
|
+
console.log(`Downloading core binaries for ${platformOS}-${platformArch}...`);
|
|
118
143
|
|
|
119
144
|
// Get the current Electrobun version from package.json
|
|
120
145
|
const packageJsonPath = join(ELECTROBUN_DEP_PATH, 'package.json');
|
|
@@ -129,8 +154,8 @@ async function ensureCoreDependencies() {
|
|
|
129
154
|
}
|
|
130
155
|
}
|
|
131
156
|
|
|
132
|
-
const platformName =
|
|
133
|
-
const archName =
|
|
157
|
+
const platformName = platformOS === 'macos' ? 'darwin' : platformOS === 'win' ? 'win' : 'linux';
|
|
158
|
+
const archName = platformArch;
|
|
134
159
|
const coreTarballUrl = `https://github.com/blackboardsh/electrobun/releases/download/${version}/electrobun-core-${platformName}-${archName}.tar.gz`;
|
|
135
160
|
|
|
136
161
|
console.log(`Downloading core binaries from: ${coreTarballUrl}`);
|
|
@@ -143,7 +168,7 @@ async function ensureCoreDependencies() {
|
|
|
143
168
|
}
|
|
144
169
|
|
|
145
170
|
// Create temp file
|
|
146
|
-
const tempFile = join(ELECTROBUN_DEP_PATH,
|
|
171
|
+
const tempFile = join(ELECTROBUN_DEP_PATH, `core-${platformOS}-${platformArch}-temp.tar.gz`);
|
|
147
172
|
const fileStream = createWriteStream(tempFile);
|
|
148
173
|
|
|
149
174
|
// Write response to file
|
|
@@ -157,73 +182,102 @@ async function ensureCoreDependencies() {
|
|
|
157
182
|
}
|
|
158
183
|
fileStream.end();
|
|
159
184
|
|
|
160
|
-
// Extract to dist directory
|
|
161
|
-
console.log(
|
|
162
|
-
const
|
|
163
|
-
mkdirSync(
|
|
185
|
+
// Extract to platform-specific dist directory
|
|
186
|
+
console.log(`Extracting core dependencies for ${platformOS}-${platformArch}...`);
|
|
187
|
+
const platformDistPath = join(ELECTROBUN_DEP_PATH, `dist-${platformOS}-${platformArch}`);
|
|
188
|
+
mkdirSync(platformDistPath, { recursive: true });
|
|
164
189
|
|
|
165
190
|
// Use Windows native tar.exe on Windows due to npm tar library issues
|
|
166
191
|
if (OS === 'win') {
|
|
167
192
|
console.log('Using Windows native tar.exe for reliable extraction...');
|
|
168
|
-
execSync(`tar -xf "${tempFile}" -C "${
|
|
193
|
+
execSync(`tar -xf "${tempFile}" -C "${platformDistPath}"`, {
|
|
169
194
|
stdio: 'inherit',
|
|
170
|
-
cwd:
|
|
195
|
+
cwd: platformDistPath
|
|
171
196
|
});
|
|
172
197
|
} else {
|
|
173
198
|
await tar.x({
|
|
174
199
|
file: tempFile,
|
|
175
|
-
cwd:
|
|
200
|
+
cwd: platformDistPath,
|
|
176
201
|
preservePaths: false,
|
|
177
202
|
strip: 0,
|
|
178
203
|
});
|
|
179
204
|
}
|
|
180
205
|
|
|
206
|
+
// NOTE: We no longer copy main.js from platform-specific downloads
|
|
207
|
+
// Platform-specific downloads should only contain native binaries
|
|
208
|
+
// main.js and api/ should be shipped via npm in the shared dist/ folder
|
|
209
|
+
|
|
181
210
|
// Clean up temp file
|
|
182
211
|
unlinkSync(tempFile);
|
|
183
212
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
console.
|
|
188
|
-
console.error('This may be caused by Windows Defender or antivirus software quarantining .js files');
|
|
189
|
-
console.error('Try temporarily disabling real-time protection or adding an exclusion for the electrobun directory');
|
|
213
|
+
// Debug: List what was actually extracted
|
|
214
|
+
try {
|
|
215
|
+
const extractedFiles = readdirSync(platformDistPath);
|
|
216
|
+
console.log(`Extracted files to ${platformDistPath}:`, extractedFiles);
|
|
190
217
|
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (existsSync(apiPath)) {
|
|
199
|
-
const apiFiles = readdirSync(apiPath);
|
|
200
|
-
console.log('API directory contents:', apiFiles);
|
|
201
|
-
if (apiFiles.length === 0) {
|
|
202
|
-
console.error('API directory is empty - this confirms antivirus is likely quarantining script files');
|
|
203
|
-
}
|
|
218
|
+
// Check if files are in subdirectories
|
|
219
|
+
for (const file of extractedFiles) {
|
|
220
|
+
const filePath = join(platformDistPath, file);
|
|
221
|
+
const stat = require('fs').statSync(filePath);
|
|
222
|
+
if (stat.isDirectory()) {
|
|
223
|
+
const subFiles = readdirSync(filePath);
|
|
224
|
+
console.log(` ${file}/: ${subFiles.join(', ')}`);
|
|
204
225
|
}
|
|
205
|
-
} catch (e) {
|
|
206
|
-
console.error('Could not list extracted files');
|
|
207
226
|
}
|
|
227
|
+
} catch (e) {
|
|
228
|
+
console.error('Could not list extracted files:', e);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Verify extraction completed successfully - check platform-specific binaries only
|
|
232
|
+
const requiredBinaries = [
|
|
233
|
+
platformPaths.BUN_BINARY,
|
|
234
|
+
platformPaths.LAUNCHER_RELEASE,
|
|
235
|
+
platformOS === 'macos' ? platformPaths.NATIVE_WRAPPER_MACOS :
|
|
236
|
+
platformOS === 'win' ? platformPaths.NATIVE_WRAPPER_WIN :
|
|
237
|
+
platformPaths.NATIVE_WRAPPER_LINUX
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
const missingBinaries = requiredBinaries.filter(file => !existsSync(file));
|
|
241
|
+
if (missingBinaries.length > 0) {
|
|
242
|
+
console.error(`Missing binaries after extraction: ${missingBinaries.map(f => f.replace(ELECTROBUN_DEP_PATH, '.')).join(', ')}`);
|
|
243
|
+
console.error('This suggests the tarball structure is different than expected');
|
|
208
244
|
}
|
|
209
245
|
|
|
210
|
-
|
|
246
|
+
// For development: if main.js doesn't exist in shared dist/, copy from platform-specific download as fallback
|
|
247
|
+
const sharedDistPath = join(ELECTROBUN_DEP_PATH, 'dist');
|
|
248
|
+
const extractedMainJs = join(platformDistPath, 'main.js');
|
|
249
|
+
const sharedMainJs = join(sharedDistPath, 'main.js');
|
|
211
250
|
|
|
212
|
-
|
|
213
|
-
|
|
251
|
+
if (existsSync(extractedMainJs) && !existsSync(sharedMainJs)) {
|
|
252
|
+
console.log('Development fallback: copying main.js from platform-specific download to shared dist/');
|
|
253
|
+
mkdirSync(sharedDistPath, { recursive: true });
|
|
254
|
+
cpSync(extractedMainJs, sharedMainJs);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log(`Core dependencies for ${platformOS}-${platformArch} downloaded and cached successfully`);
|
|
258
|
+
|
|
259
|
+
} catch (error: any) {
|
|
260
|
+
console.error(`Failed to download core dependencies for ${platformOS}-${platformArch}:`, error.message);
|
|
214
261
|
console.error('Please ensure you have an internet connection and the release exists.');
|
|
215
262
|
process.exit(1);
|
|
216
263
|
}
|
|
217
264
|
}
|
|
218
265
|
|
|
219
|
-
async function ensureCEFDependencies() {
|
|
266
|
+
async function ensureCEFDependencies(targetOS?: 'macos' | 'win' | 'linux', targetArch?: 'arm64' | 'x64') {
|
|
267
|
+
// Use provided target platform or default to host platform
|
|
268
|
+
const platformOS = targetOS || OS;
|
|
269
|
+
const platformArch = targetArch || ARCH;
|
|
270
|
+
|
|
271
|
+
// Get platform-specific paths
|
|
272
|
+
const platformPaths = getPlatformPaths(platformOS, platformArch);
|
|
273
|
+
|
|
220
274
|
// Check if CEF dependencies already exist
|
|
221
|
-
if (existsSync(
|
|
222
|
-
console.log(
|
|
275
|
+
if (existsSync(platformPaths.CEF_DIR)) {
|
|
276
|
+
console.log(`CEF dependencies found for ${platformOS}-${platformArch}, using cached version`);
|
|
223
277
|
return;
|
|
224
278
|
}
|
|
225
279
|
|
|
226
|
-
console.log(
|
|
280
|
+
console.log(`CEF dependencies not found for ${platformOS}-${platformArch}, downloading...`);
|
|
227
281
|
|
|
228
282
|
// Get the current Electrobun version from package.json
|
|
229
283
|
const packageJsonPath = join(ELECTROBUN_DEP_PATH, 'package.json');
|
|
@@ -238,8 +292,8 @@ async function ensureCEFDependencies() {
|
|
|
238
292
|
}
|
|
239
293
|
}
|
|
240
294
|
|
|
241
|
-
const platformName =
|
|
242
|
-
const archName =
|
|
295
|
+
const platformName = platformOS === 'macos' ? 'darwin' : platformOS === 'win' ? 'win' : 'linux';
|
|
296
|
+
const archName = platformArch;
|
|
243
297
|
const cefTarballUrl = `https://github.com/blackboardsh/electrobun/releases/download/${version}/electrobun-cef-${platformName}-${archName}.tar.gz`;
|
|
244
298
|
|
|
245
299
|
console.log(`Downloading CEF from: ${cefTarballUrl}`);
|
|
@@ -252,7 +306,7 @@ async function ensureCEFDependencies() {
|
|
|
252
306
|
}
|
|
253
307
|
|
|
254
308
|
// Create temp file
|
|
255
|
-
const tempFile = join(ELECTROBUN_DEP_PATH,
|
|
309
|
+
const tempFile = join(ELECTROBUN_DEP_PATH, `cef-${platformOS}-${platformArch}-temp.tar.gz`);
|
|
256
310
|
const fileStream = createWriteStream(tempFile);
|
|
257
311
|
|
|
258
312
|
// Write response to file
|
|
@@ -266,21 +320,22 @@ async function ensureCEFDependencies() {
|
|
|
266
320
|
}
|
|
267
321
|
fileStream.end();
|
|
268
322
|
|
|
269
|
-
// Extract to dist directory
|
|
270
|
-
console.log(
|
|
271
|
-
const
|
|
323
|
+
// Extract to platform-specific dist directory
|
|
324
|
+
console.log(`Extracting CEF dependencies for ${platformOS}-${platformArch}...`);
|
|
325
|
+
const platformDistPath = join(ELECTROBUN_DEP_PATH, `dist-${platformOS}-${platformArch}`);
|
|
326
|
+
mkdirSync(platformDistPath, { recursive: true });
|
|
272
327
|
|
|
273
328
|
// Use Windows native tar.exe on Windows due to npm tar library issues
|
|
274
329
|
if (OS === 'win') {
|
|
275
330
|
console.log('Using Windows native tar.exe for reliable extraction...');
|
|
276
|
-
execSync(`tar -xf "${tempFile}" -C "${
|
|
331
|
+
execSync(`tar -xf "${tempFile}" -C "${platformDistPath}"`, {
|
|
277
332
|
stdio: 'inherit',
|
|
278
|
-
cwd:
|
|
333
|
+
cwd: platformDistPath
|
|
279
334
|
});
|
|
280
335
|
} else {
|
|
281
336
|
await tar.x({
|
|
282
337
|
file: tempFile,
|
|
283
|
-
cwd:
|
|
338
|
+
cwd: platformDistPath,
|
|
284
339
|
preservePaths: false,
|
|
285
340
|
strip: 0,
|
|
286
341
|
});
|
|
@@ -289,10 +344,25 @@ async function ensureCEFDependencies() {
|
|
|
289
344
|
// Clean up temp file
|
|
290
345
|
unlinkSync(tempFile);
|
|
291
346
|
|
|
292
|
-
|
|
347
|
+
// Debug: List what was actually extracted for CEF
|
|
348
|
+
try {
|
|
349
|
+
const extractedFiles = readdirSync(platformDistPath);
|
|
350
|
+
console.log(`CEF extracted files to ${platformDistPath}:`, extractedFiles);
|
|
351
|
+
|
|
352
|
+
// Check if CEF directory was created
|
|
353
|
+
const cefDir = join(platformDistPath, 'cef');
|
|
354
|
+
if (existsSync(cefDir)) {
|
|
355
|
+
const cefFiles = readdirSync(cefDir);
|
|
356
|
+
console.log(`CEF directory contents: ${cefFiles.slice(0, 10).join(', ')}${cefFiles.length > 10 ? '...' : ''}`);
|
|
357
|
+
}
|
|
358
|
+
} catch (e) {
|
|
359
|
+
console.error('Could not list CEF extracted files:', e);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
console.log(`CEF dependencies for ${platformOS}-${platformArch} downloaded and cached successfully`);
|
|
293
363
|
|
|
294
|
-
} catch (error) {
|
|
295
|
-
console.error(
|
|
364
|
+
} catch (error: any) {
|
|
365
|
+
console.error(`Failed to download CEF dependencies for ${platformOS}-${platformArch}:`, error.message);
|
|
296
366
|
console.error('Please ensure you have an internet connection and the release exists.');
|
|
297
367
|
process.exit(1);
|
|
298
368
|
}
|
|
@@ -327,6 +397,7 @@ const defaultConfig = {
|
|
|
327
397
|
build: {
|
|
328
398
|
buildFolder: "build",
|
|
329
399
|
artifactFolder: "artifacts",
|
|
400
|
+
targets: undefined, // Will default to current platform if not specified
|
|
330
401
|
mac: {
|
|
331
402
|
codesign: false,
|
|
332
403
|
notarize: false,
|
|
@@ -360,7 +431,10 @@ if (!command) {
|
|
|
360
431
|
const config = getConfig();
|
|
361
432
|
|
|
362
433
|
const envArg =
|
|
363
|
-
process.argv.find((arg) => arg.startsWith("env="))?.split("=")[1] || "";
|
|
434
|
+
process.argv.find((arg) => arg.startsWith("--env="))?.split("=")[1] || "";
|
|
435
|
+
|
|
436
|
+
const targetsArg =
|
|
437
|
+
process.argv.find((arg) => arg.startsWith("--targets="))?.split("=")[1] || "";
|
|
364
438
|
|
|
365
439
|
const validEnvironments = ["dev", "canary", "stable"];
|
|
366
440
|
|
|
@@ -368,8 +442,175 @@ const validEnvironments = ["dev", "canary", "stable"];
|
|
|
368
442
|
const buildEnvironment: "dev" | "canary" | "stable" =
|
|
369
443
|
validEnvironments.includes(envArg) ? envArg : "dev";
|
|
370
444
|
|
|
445
|
+
// Determine build targets
|
|
446
|
+
type BuildTarget = { os: 'macos' | 'win' | 'linux', arch: 'arm64' | 'x64' };
|
|
447
|
+
|
|
448
|
+
function parseBuildTargets(): BuildTarget[] {
|
|
449
|
+
// If explicit targets provided via CLI
|
|
450
|
+
if (targetsArg) {
|
|
451
|
+
if (targetsArg === 'current') {
|
|
452
|
+
return [{ os: OS, arch: ARCH }];
|
|
453
|
+
} else if (targetsArg === 'all') {
|
|
454
|
+
return parseConfigTargets();
|
|
455
|
+
} else {
|
|
456
|
+
// Parse comma-separated targets like "macos-arm64,win-x64"
|
|
457
|
+
return targetsArg.split(',').map(target => {
|
|
458
|
+
const [os, arch] = target.trim().split('-') as [string, string];
|
|
459
|
+
if (!['macos', 'win', 'linux'].includes(os) || !['arm64', 'x64'].includes(arch)) {
|
|
460
|
+
console.error(`Invalid target: ${target}. Format should be: os-arch (e.g., macos-arm64)`);
|
|
461
|
+
process.exit(1);
|
|
462
|
+
}
|
|
463
|
+
return { os, arch } as BuildTarget;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Default behavior: always build for current platform only
|
|
469
|
+
// This ensures predictable, fast builds unless explicitly requesting multi-platform
|
|
470
|
+
return [{ os: OS, arch: ARCH }];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function parseConfigTargets(): BuildTarget[] {
|
|
474
|
+
// If config has targets, use them
|
|
475
|
+
if (config.build.targets && config.build.targets.length > 0) {
|
|
476
|
+
return config.build.targets.map(target => {
|
|
477
|
+
if (target === 'current') {
|
|
478
|
+
return { os: OS, arch: ARCH };
|
|
479
|
+
}
|
|
480
|
+
const [os, arch] = target.split('-') as [string, string];
|
|
481
|
+
if (!['macos', 'win', 'linux'].includes(os) || !['arm64', 'x64'].includes(arch)) {
|
|
482
|
+
console.error(`Invalid target in config: ${target}. Format should be: os-arch (e.g., macos-arm64)`);
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
return { os, arch } as BuildTarget;
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// If no config targets and --targets=all, use all available platforms
|
|
490
|
+
if (targetsArg === 'all') {
|
|
491
|
+
console.log('No targets specified in config, using all available platforms');
|
|
492
|
+
return [
|
|
493
|
+
{ os: 'macos', arch: 'arm64' },
|
|
494
|
+
{ os: 'macos', arch: 'x64' },
|
|
495
|
+
{ os: 'win', arch: 'x64' },
|
|
496
|
+
{ os: 'linux', arch: 'x64' },
|
|
497
|
+
{ os: 'linux', arch: 'arm64' }
|
|
498
|
+
];
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Default to current platform
|
|
502
|
+
return [{ os: OS, arch: ARCH }];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const buildTargets = parseBuildTargets();
|
|
506
|
+
|
|
507
|
+
// Show build targets to user
|
|
508
|
+
if (buildTargets.length === 1) {
|
|
509
|
+
console.log(`Building for ${buildTargets[0].os}-${buildTargets[0].arch} (${buildEnvironment})`);
|
|
510
|
+
} else {
|
|
511
|
+
const targetList = buildTargets.map(t => `${t.os}-${t.arch}`).join(', ');
|
|
512
|
+
console.log(`Building for multiple targets: ${targetList} (${buildEnvironment})`);
|
|
513
|
+
console.log(`Running ${buildTargets.length} parallel builds...`);
|
|
514
|
+
|
|
515
|
+
// Spawn parallel build processes
|
|
516
|
+
const buildPromises = buildTargets.map(async (target) => {
|
|
517
|
+
const targetString = `${target.os}-${target.arch}`;
|
|
518
|
+
const prefix = `[${targetString}]`;
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
// Try to find the electrobun binary in node_modules/.bin or use bunx
|
|
522
|
+
const electrobunBin = join(projectRoot, 'node_modules', '.bin', 'electrobun');
|
|
523
|
+
let command: string[];
|
|
524
|
+
|
|
525
|
+
if (existsSync(electrobunBin)) {
|
|
526
|
+
command = [electrobunBin, 'build', `--env=${buildEnvironment}`, `--targets=${targetString}`];
|
|
527
|
+
} else {
|
|
528
|
+
// Fallback to bunx which should resolve node_modules binaries
|
|
529
|
+
command = ['bunx', 'electrobun', 'build', `--env=${buildEnvironment}`, `--targets=${targetString}`];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
console.log(`${prefix} Running:`, command.join(' '));
|
|
533
|
+
|
|
534
|
+
const result = await Bun.spawn(command, {
|
|
535
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
536
|
+
env: process.env,
|
|
537
|
+
cwd: projectRoot // Ensure we're in the right directory
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Pipe output with prefix
|
|
541
|
+
if (result.stdout) {
|
|
542
|
+
const reader = result.stdout.getReader();
|
|
543
|
+
while (true) {
|
|
544
|
+
const { done, value } = await reader.read();
|
|
545
|
+
if (done) break;
|
|
546
|
+
const text = new TextDecoder().decode(value);
|
|
547
|
+
// Add prefix to each line
|
|
548
|
+
const prefixedText = text.split('\n').map(line =>
|
|
549
|
+
line ? `${prefix} ${line}` : line
|
|
550
|
+
).join('\n');
|
|
551
|
+
process.stdout.write(prefixedText);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (result.stderr) {
|
|
556
|
+
const reader = result.stderr.getReader();
|
|
557
|
+
while (true) {
|
|
558
|
+
const { done, value } = await reader.read();
|
|
559
|
+
if (done) break;
|
|
560
|
+
const text = new TextDecoder().decode(value);
|
|
561
|
+
const prefixedText = text.split('\n').map(line =>
|
|
562
|
+
line ? `${prefix} ${line}` : line
|
|
563
|
+
).join('\n');
|
|
564
|
+
process.stderr.write(prefixedText);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const exitCode = await result.exited;
|
|
569
|
+
return { target, exitCode, success: exitCode === 0 };
|
|
570
|
+
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error(`${prefix} Failed to start build:`, error);
|
|
573
|
+
return { target, exitCode: 1, success: false, error };
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// Wait for all builds to complete
|
|
578
|
+
const results = await Promise.allSettled(buildPromises);
|
|
579
|
+
|
|
580
|
+
// Report final results
|
|
581
|
+
console.log('\n=== Build Results ===');
|
|
582
|
+
let allSucceeded = true;
|
|
583
|
+
|
|
584
|
+
for (const result of results) {
|
|
585
|
+
if (result.status === 'fulfilled') {
|
|
586
|
+
const { target, success, exitCode } = result.value;
|
|
587
|
+
const status = success ? '✅ SUCCESS' : '❌ FAILED';
|
|
588
|
+
console.log(`${target.os}-${target.arch}: ${status} (exit code: ${exitCode})`);
|
|
589
|
+
if (!success) allSucceeded = false;
|
|
590
|
+
} else {
|
|
591
|
+
console.log(`Build rejected: ${result.reason}`);
|
|
592
|
+
allSucceeded = false;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (!allSucceeded) {
|
|
597
|
+
console.log('\nSome builds failed. Check the output above for details.');
|
|
598
|
+
process.exit(1);
|
|
599
|
+
} else {
|
|
600
|
+
console.log('\nAll builds completed successfully! 🎉');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
process.exit(0);
|
|
604
|
+
}
|
|
605
|
+
|
|
371
606
|
// todo (yoav): dev builds should include the branch name, and/or allow configuration via external config
|
|
372
|
-
|
|
607
|
+
// For now, assume single target build (we'll refactor for multi-target later)
|
|
608
|
+
const currentTarget = buildTargets[0];
|
|
609
|
+
const buildSubFolder = `${buildEnvironment}-${currentTarget.os}-${currentTarget.arch}`;
|
|
610
|
+
|
|
611
|
+
// Use target OS/ARCH for build logic (instead of current machine's OS/ARCH)
|
|
612
|
+
const targetOS = currentTarget.os;
|
|
613
|
+
const targetARCH = currentTarget.arch;
|
|
373
614
|
|
|
374
615
|
const buildFolder = join(projectRoot, config.build.buildFolder, buildSubFolder);
|
|
375
616
|
|
|
@@ -444,7 +685,7 @@ const appFileName = (
|
|
|
444
685
|
)
|
|
445
686
|
.replace(/\s/g, "")
|
|
446
687
|
.replace(/\./g, "-");
|
|
447
|
-
const bundleFileName =
|
|
688
|
+
const bundleFileName = targetOS === 'macos' ? `${appFileName}.app` : appFileName;
|
|
448
689
|
|
|
449
690
|
// const logPath = `/Library/Logs/Electrobun/ExampleApp/dev/out.log`;
|
|
450
691
|
|
|
@@ -454,8 +695,11 @@ if (commandArg === "init") {
|
|
|
454
695
|
// todo (yoav): init a repo folder structure
|
|
455
696
|
console.log("initializing electrobun project");
|
|
456
697
|
} else if (commandArg === "build") {
|
|
457
|
-
// Ensure core binaries are available before starting build
|
|
458
|
-
await ensureCoreDependencies();
|
|
698
|
+
// Ensure core binaries are available for the target platform before starting build
|
|
699
|
+
await ensureCoreDependencies(currentTarget.os, currentTarget.arch);
|
|
700
|
+
|
|
701
|
+
// Get platform-specific paths for the current target
|
|
702
|
+
const targetPaths = getPlatformPaths(currentTarget.os, currentTarget.arch);
|
|
459
703
|
|
|
460
704
|
// refresh build folder
|
|
461
705
|
if (existsSync(buildFolder)) {
|
|
@@ -480,7 +724,7 @@ if (commandArg === "init") {
|
|
|
480
724
|
appBundleMacOSPath,
|
|
481
725
|
appBundleFolderResourcesPath,
|
|
482
726
|
appBundleFolderFrameworksPath,
|
|
483
|
-
} = createAppBundle(appFileName, buildFolder);
|
|
727
|
+
} = createAppBundle(appFileName, buildFolder, targetOS);
|
|
484
728
|
|
|
485
729
|
const appBundleAppCodePath = join(appBundleFolderResourcesPath, "app");
|
|
486
730
|
|
|
@@ -552,7 +796,7 @@ if (commandArg === "init") {
|
|
|
552
796
|
// cpSync(zigLauncherBinarySource, zigLauncherDestination, {recursive: true, dereference: true});
|
|
553
797
|
// For dev builds, use the actual CLI binary that's currently running
|
|
554
798
|
// It could be in .cache (npm install) or bin (local dev)
|
|
555
|
-
let devLauncherPath =
|
|
799
|
+
let devLauncherPath = targetPaths.LAUNCHER_DEV;
|
|
556
800
|
if (buildEnvironment === "dev" && !existsSync(devLauncherPath)) {
|
|
557
801
|
// Check .cache location (npm installed)
|
|
558
802
|
const cachePath = join(ELECTROBUN_DEP_PATH, ".cache", "electrobun") + binExt;
|
|
@@ -571,7 +815,7 @@ if (commandArg === "init") {
|
|
|
571
815
|
buildEnvironment === "dev"
|
|
572
816
|
? devLauncherPath
|
|
573
817
|
: // Note: for release use the zig launcher optimized for smol size
|
|
574
|
-
|
|
818
|
+
targetPaths.LAUNCHER_RELEASE;
|
|
575
819
|
const bunCliLauncherDestination = join(appBundleMacOSPath, "launcher") + binExt;
|
|
576
820
|
const destLauncherFolder = dirname(bunCliLauncherDestination);
|
|
577
821
|
if (!existsSync(destLauncherFolder)) {
|
|
@@ -584,11 +828,11 @@ if (commandArg === "init") {
|
|
|
584
828
|
dereference: true,
|
|
585
829
|
});
|
|
586
830
|
|
|
587
|
-
cpSync(
|
|
831
|
+
cpSync(targetPaths.MAIN_JS, join(appBundleMacOSPath, 'main.js'));
|
|
588
832
|
|
|
589
833
|
// Bun runtime binary
|
|
590
834
|
// todo (yoav): this only works for the current architecture
|
|
591
|
-
const bunBinarySourcePath =
|
|
835
|
+
const bunBinarySourcePath = targetPaths.BUN_BINARY;
|
|
592
836
|
// Note: .bin/bun binary in node_modules is a symlink to the versioned one in another place
|
|
593
837
|
// in node_modules, so we have to dereference here to get the actual binary in the bundle.
|
|
594
838
|
const bunBinaryDestInBundlePath = join(appBundleMacOSPath, "bun") + binExt;
|
|
@@ -600,8 +844,8 @@ if (commandArg === "init") {
|
|
|
600
844
|
cpSync(bunBinarySourcePath, bunBinaryDestInBundlePath, { dereference: true });
|
|
601
845
|
|
|
602
846
|
// copy native wrapper dynamic library
|
|
603
|
-
if (
|
|
604
|
-
const nativeWrapperMacosSource =
|
|
847
|
+
if (targetOS === 'macos') {
|
|
848
|
+
const nativeWrapperMacosSource = targetPaths.NATIVE_WRAPPER_MACOS;
|
|
605
849
|
const nativeWrapperMacosDestination = join(
|
|
606
850
|
appBundleMacOSPath,
|
|
607
851
|
"libNativeWrapper.dylib"
|
|
@@ -609,8 +853,8 @@ if (commandArg === "init") {
|
|
|
609
853
|
cpSync(nativeWrapperMacosSource, nativeWrapperMacosDestination, {
|
|
610
854
|
dereference: true,
|
|
611
855
|
});
|
|
612
|
-
} else if (
|
|
613
|
-
const nativeWrapperMacosSource =
|
|
856
|
+
} else if (targetOS === 'win') {
|
|
857
|
+
const nativeWrapperMacosSource = targetPaths.NATIVE_WRAPPER_WIN;
|
|
614
858
|
const nativeWrapperMacosDestination = join(
|
|
615
859
|
appBundleMacOSPath,
|
|
616
860
|
"libNativeWrapper.dll"
|
|
@@ -619,7 +863,7 @@ if (commandArg === "init") {
|
|
|
619
863
|
dereference: true,
|
|
620
864
|
});
|
|
621
865
|
|
|
622
|
-
const webview2LibSource =
|
|
866
|
+
const webview2LibSource = targetPaths.WEBVIEW2LOADER_WIN;
|
|
623
867
|
const webview2LibDestination = join(
|
|
624
868
|
appBundleMacOSPath,
|
|
625
869
|
"WebView2Loader.dll"
|
|
@@ -627,10 +871,10 @@ if (commandArg === "init") {
|
|
|
627
871
|
// copy webview2 system webview library
|
|
628
872
|
cpSync(webview2LibSource, webview2LibDestination);
|
|
629
873
|
|
|
630
|
-
} else if (
|
|
874
|
+
} else if (targetOS === 'linux') {
|
|
631
875
|
// Choose the appropriate native wrapper based on bundleCEF setting
|
|
632
876
|
const useCEF = config.build.linux?.bundleCEF;
|
|
633
|
-
const nativeWrapperLinuxSource = useCEF ?
|
|
877
|
+
const nativeWrapperLinuxSource = useCEF ? targetPaths.NATIVE_WRAPPER_LINUX_CEF : targetPaths.NATIVE_WRAPPER_LINUX;
|
|
634
878
|
const nativeWrapperLinuxDestination = join(
|
|
635
879
|
appBundleMacOSPath,
|
|
636
880
|
"libNativeWrapper.so"
|
|
@@ -648,13 +892,13 @@ if (commandArg === "init") {
|
|
|
648
892
|
|
|
649
893
|
|
|
650
894
|
// Download CEF binaries if needed when bundleCEF is enabled
|
|
651
|
-
if ((
|
|
652
|
-
(
|
|
653
|
-
(
|
|
895
|
+
if ((targetOS === 'macos' && config.build.mac?.bundleCEF) ||
|
|
896
|
+
(targetOS === 'win' && config.build.win?.bundleCEF) ||
|
|
897
|
+
(targetOS === 'linux' && config.build.linux?.bundleCEF)) {
|
|
654
898
|
|
|
655
|
-
await ensureCEFDependencies();
|
|
656
|
-
if (
|
|
657
|
-
const cefFrameworkSource =
|
|
899
|
+
await ensureCEFDependencies(currentTarget.os, currentTarget.arch);
|
|
900
|
+
if (targetOS === 'macos') {
|
|
901
|
+
const cefFrameworkSource = targetPaths.CEF_FRAMEWORK_MACOS;
|
|
658
902
|
const cefFrameworkDestination = join(
|
|
659
903
|
appBundleFolderFrameworksPath,
|
|
660
904
|
"Chromium Embedded Framework.framework"
|
|
@@ -675,7 +919,7 @@ if (commandArg === "init") {
|
|
|
675
919
|
"bun Helper (Renderer)",
|
|
676
920
|
];
|
|
677
921
|
|
|
678
|
-
const helperSourcePath =
|
|
922
|
+
const helperSourcePath = targetPaths.CEF_HELPER_MACOS;
|
|
679
923
|
cefHelperNames.forEach((helperName) => {
|
|
680
924
|
const destinationPath = join(
|
|
681
925
|
appBundleFolderFrameworksPath,
|
|
@@ -695,10 +939,9 @@ if (commandArg === "init") {
|
|
|
695
939
|
dereference: true,
|
|
696
940
|
});
|
|
697
941
|
});
|
|
698
|
-
} else if (
|
|
699
|
-
// Copy CEF DLLs from dist/cef/ to the main executable directory
|
|
700
|
-
const
|
|
701
|
-
const cefSourcePath = join(electrobunDistPath, "cef");
|
|
942
|
+
} else if (targetOS === 'win') {
|
|
943
|
+
// Copy CEF DLLs from platform-specific dist/cef/ to the main executable directory
|
|
944
|
+
const cefSourcePath = targetPaths.CEF_DIR;
|
|
702
945
|
const cefDllFiles = [
|
|
703
946
|
'libcef.dll',
|
|
704
947
|
'chrome_elf.dll',
|
|
@@ -738,7 +981,7 @@ if (commandArg === "init") {
|
|
|
738
981
|
});
|
|
739
982
|
|
|
740
983
|
// Copy CEF resources to MacOS/cef/ subdirectory for other resources like locales
|
|
741
|
-
const cefResourcesSource =
|
|
984
|
+
const cefResourcesSource = targetPaths.CEF_DIR;
|
|
742
985
|
const cefResourcesDestination = join(appBundleMacOSPath, 'cef');
|
|
743
986
|
|
|
744
987
|
if (existsSync(cefResourcesSource)) {
|
|
@@ -757,7 +1000,7 @@ if (commandArg === "init") {
|
|
|
757
1000
|
"bun Helper (Renderer)",
|
|
758
1001
|
];
|
|
759
1002
|
|
|
760
|
-
const helperSourcePath =
|
|
1003
|
+
const helperSourcePath = targetPaths.CEF_HELPER_WIN;
|
|
761
1004
|
if (existsSync(helperSourcePath)) {
|
|
762
1005
|
cefHelperNames.forEach((helperName) => {
|
|
763
1006
|
const destinationPath = join(appBundleMacOSPath, `${helperName}.exe`);
|
|
@@ -767,10 +1010,9 @@ if (commandArg === "init") {
|
|
|
767
1010
|
} else {
|
|
768
1011
|
console.log(`WARNING: Missing CEF helper: ${helperSourcePath}`);
|
|
769
1012
|
}
|
|
770
|
-
} else if (
|
|
771
|
-
// Copy CEF shared libraries from dist/cef/ to the main executable directory
|
|
772
|
-
const
|
|
773
|
-
const cefSourcePath = join(electrobunDistPath, "cef");
|
|
1013
|
+
} else if (targetOS === 'linux') {
|
|
1014
|
+
// Copy CEF shared libraries from platform-specific dist/cef/ to the main executable directory
|
|
1015
|
+
const cefSourcePath = targetPaths.CEF_DIR;
|
|
774
1016
|
|
|
775
1017
|
if (existsSync(cefSourcePath)) {
|
|
776
1018
|
const cefSoFiles = [
|
|
@@ -856,7 +1098,7 @@ if (commandArg === "init") {
|
|
|
856
1098
|
"bun Helper (Renderer)",
|
|
857
1099
|
];
|
|
858
1100
|
|
|
859
|
-
const helperSourcePath =
|
|
1101
|
+
const helperSourcePath = targetPaths.CEF_HELPER_LINUX;
|
|
860
1102
|
if (existsSync(helperSourcePath)) {
|
|
861
1103
|
cefHelperNames.forEach((helperName) => {
|
|
862
1104
|
const destinationPath = join(appBundleMacOSPath, helperName);
|
|
@@ -872,7 +1114,7 @@ if (commandArg === "init") {
|
|
|
872
1114
|
|
|
873
1115
|
|
|
874
1116
|
// copy native bindings
|
|
875
|
-
const bsPatchSource =
|
|
1117
|
+
const bsPatchSource = targetPaths.BSPATCH;
|
|
876
1118
|
const bsPatchDestination = join(appBundleMacOSPath, "bspatch") + binExt;
|
|
877
1119
|
const bsPatchDestFolder = dirname(bsPatchDestination);
|
|
878
1120
|
if (!existsSync(bsPatchDestFolder)) {
|
|
@@ -968,12 +1210,21 @@ if (commandArg === "init") {
|
|
|
968
1210
|
|
|
969
1211
|
// Run postBuild script
|
|
970
1212
|
if (config.scripts.postBuild) {
|
|
971
|
-
|
|
972
|
-
|
|
1213
|
+
// Use host platform's bun binary for running scripts, not target platform's
|
|
1214
|
+
const hostPaths = getPlatformPaths(OS, ARCH);
|
|
1215
|
+
|
|
1216
|
+
Bun.spawnSync([hostPaths.BUN_BINARY, config.scripts.postBuild], {
|
|
973
1217
|
stdio: ["ignore", "inherit", "inherit"],
|
|
974
1218
|
env: {
|
|
975
1219
|
...process.env,
|
|
976
1220
|
ELECTROBUN_BUILD_ENV: buildEnvironment,
|
|
1221
|
+
ELECTROBUN_OS: targetOS, // Use target OS for environment variables
|
|
1222
|
+
ELECTROBUN_ARCH: targetARCH, // Use target ARCH for environment variables
|
|
1223
|
+
ELECTROBUN_BUILD_DIR: buildFolder,
|
|
1224
|
+
ELECTROBUN_APP_NAME: appFileName,
|
|
1225
|
+
ELECTROBUN_APP_VERSION: config.app.version,
|
|
1226
|
+
ELECTROBUN_APP_IDENTIFIER: config.app.identifier,
|
|
1227
|
+
ELECTROBUN_ARTIFACT_DIR: artifactFolder,
|
|
977
1228
|
},
|
|
978
1229
|
});
|
|
979
1230
|
}
|
|
@@ -1017,8 +1268,9 @@ if (commandArg === "init") {
|
|
|
1017
1268
|
);
|
|
1018
1269
|
|
|
1019
1270
|
// todo (yoav): add these to config
|
|
1271
|
+
// Only codesign/notarize when building macOS targets on macOS host
|
|
1020
1272
|
const shouldCodesign =
|
|
1021
|
-
buildEnvironment !== "dev" && config.build.mac.codesign;
|
|
1273
|
+
buildEnvironment !== "dev" && targetOS === 'macos' && OS === 'macos' && config.build.mac.codesign;
|
|
1022
1274
|
const shouldNotarize = shouldCodesign && config.build.mac.notarize;
|
|
1023
1275
|
|
|
1024
1276
|
if (shouldCodesign) {
|
|
@@ -1053,6 +1305,8 @@ if (commandArg === "init") {
|
|
|
1053
1305
|
// 6.5. code sign and notarize the dmg
|
|
1054
1306
|
// 7. copy artifacts to directory [self-extractor dmg, zstd app bundle, bsdiff patch, update.json]
|
|
1055
1307
|
|
|
1308
|
+
// Add platform suffix for all artifacts
|
|
1309
|
+
const platformSuffix = `-${targetOS}-${targetARCH}`;
|
|
1056
1310
|
const tarPath = `${appBundleFolderPath}.tar`;
|
|
1057
1311
|
|
|
1058
1312
|
// tar the signed and notarized app bundle
|
|
@@ -1076,7 +1330,8 @@ if (commandArg === "init") {
|
|
|
1076
1330
|
// than saving 1 more MB of space/bandwidth.
|
|
1077
1331
|
|
|
1078
1332
|
const compressedTarPath = `${tarPath}.zst`;
|
|
1079
|
-
|
|
1333
|
+
const platformCompressedTarPath = compressedTarPath.replace('.tar.zst', `${platformSuffix}.tar.zst`);
|
|
1334
|
+
artifactsToUpload.push(platformCompressedTarPath);
|
|
1080
1335
|
|
|
1081
1336
|
// zstd compress tarball
|
|
1082
1337
|
// todo (yoav): consider using c bindings for zstd for speed instead of wasm
|
|
@@ -1085,23 +1340,28 @@ if (commandArg === "init") {
|
|
|
1085
1340
|
await ZstdInit().then(async ({ ZstdSimple, ZstdStream }) => {
|
|
1086
1341
|
// Note: Simple is much faster than stream, but stream is better for large files
|
|
1087
1342
|
// todo (yoav): consider a file size cutoff to switch to stream instead of simple.
|
|
1343
|
+
const useStream = tarball.size > 100 * 1024 * 1024;
|
|
1344
|
+
|
|
1088
1345
|
if (tarball.size > 0) {
|
|
1089
|
-
|
|
1346
|
+
// Uint8 array filestream of the tar file
|
|
1090
1347
|
|
|
1091
1348
|
const data = new Uint8Array(tarBuffer);
|
|
1349
|
+
|
|
1092
1350
|
const compressionLevel = 22;
|
|
1093
1351
|
const compressedData = ZstdSimple.compress(data, compressionLevel);
|
|
1094
1352
|
|
|
1095
1353
|
console.log(
|
|
1096
1354
|
"compressed",
|
|
1097
|
-
|
|
1355
|
+
data.length,
|
|
1098
1356
|
"bytes",
|
|
1099
1357
|
"from",
|
|
1100
|
-
|
|
1358
|
+
tarBuffer.byteLength,
|
|
1101
1359
|
"bytes"
|
|
1102
1360
|
);
|
|
1103
1361
|
|
|
1104
1362
|
await Bun.write(compressedTarPath, compressedData);
|
|
1363
|
+
// Copy to platform-specific filename for upload
|
|
1364
|
+
cpSync(compressedTarPath, platformCompressedTarPath);
|
|
1105
1365
|
}
|
|
1106
1366
|
});
|
|
1107
1367
|
|
|
@@ -1109,7 +1369,7 @@ if (commandArg === "init") {
|
|
|
1109
1369
|
// now and it needs the same name as the original app bundle.
|
|
1110
1370
|
rmdirSync(appBundleFolderPath, { recursive: true });
|
|
1111
1371
|
|
|
1112
|
-
const selfExtractingBundle = createAppBundle(appFileName, buildFolder);
|
|
1372
|
+
const selfExtractingBundle = createAppBundle(appFileName, buildFolder, targetOS);
|
|
1113
1373
|
const compressedTarballInExtractingBundlePath = join(
|
|
1114
1374
|
selfExtractingBundle.appBundleFolderResourcesPath,
|
|
1115
1375
|
`${hash}.tar.zst`
|
|
@@ -1118,7 +1378,7 @@ if (commandArg === "init") {
|
|
|
1118
1378
|
// copy the zstd tarball to the self-extracting app bundle
|
|
1119
1379
|
cpSync(compressedTarPath, compressedTarballInExtractingBundlePath);
|
|
1120
1380
|
|
|
1121
|
-
const selfExtractorBinSourcePath =
|
|
1381
|
+
const selfExtractorBinSourcePath = targetPaths.EXTRACTOR;
|
|
1122
1382
|
const selfExtractorBinDestinationPath = join(
|
|
1123
1383
|
selfExtractingBundle.appBundleMacOSPath,
|
|
1124
1384
|
"launcher"
|
|
@@ -1150,28 +1410,49 @@ if (commandArg === "init") {
|
|
|
1150
1410
|
console.log("skipping notarization");
|
|
1151
1411
|
}
|
|
1152
1412
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1413
|
+
// DMG creation for macOS only
|
|
1414
|
+
if (targetOS === 'macos') {
|
|
1415
|
+
console.log("creating dmg...");
|
|
1416
|
+
// make a dmg
|
|
1417
|
+
const dmgPath = join(buildFolder, `${appFileName}.dmg`);
|
|
1418
|
+
const platformDmgPath = join(buildFolder, `${appFileName}${platformSuffix}.dmg`);
|
|
1419
|
+
artifactsToUpload.push(platformDmgPath);
|
|
1420
|
+
// hdiutil create -volname "YourAppName" -srcfolder /path/to/YourApp.app -ov -format UDZO YourAppName.dmg
|
|
1421
|
+
// Note: use UDBZ for better compression vs. UDZO
|
|
1422
|
+
execSync(
|
|
1423
|
+
`hdiutil create -volname "${appFileName}" -srcfolder ${escapePathForTerminal(
|
|
1424
|
+
appBundleFolderPath
|
|
1425
|
+
)} -ov -format UDBZ ${escapePathForTerminal(dmgPath)}`
|
|
1426
|
+
);
|
|
1164
1427
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1428
|
+
if (shouldCodesign) {
|
|
1429
|
+
codesignAppBundle(dmgPath);
|
|
1430
|
+
} else {
|
|
1431
|
+
console.log("skipping codesign");
|
|
1432
|
+
}
|
|
1170
1433
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1434
|
+
if (shouldNotarize) {
|
|
1435
|
+
notarizeAndStaple(dmgPath);
|
|
1436
|
+
} else {
|
|
1437
|
+
console.log("skipping notarization");
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// Copy to platform-specific filename
|
|
1441
|
+
cpSync(dmgPath, platformDmgPath);
|
|
1173
1442
|
} else {
|
|
1174
|
-
|
|
1443
|
+
// For Windows and Linux, add the self-extracting bundle directly
|
|
1444
|
+
const platformBundlePath = join(buildFolder, `${appFileName}${platformSuffix}${targetOS === 'win' ? '.exe' : ''}`);
|
|
1445
|
+
// Copy the self-extracting bundle to platform-specific filename
|
|
1446
|
+
if (targetOS === 'win') {
|
|
1447
|
+
// On Windows, create a self-extracting exe
|
|
1448
|
+
// For now, just copy the bundle folder
|
|
1449
|
+
artifactsToUpload.push(compressedTarPath.replace('.tar.zst', `${platformSuffix}.tar.zst`));
|
|
1450
|
+
} else if (targetOS === 'linux') {
|
|
1451
|
+
// On Linux, create a tar.gz of the bundle
|
|
1452
|
+
const linuxTarPath = join(buildFolder, `${appFileName}${platformSuffix}.tar.gz`);
|
|
1453
|
+
execSync(`tar -czf ${escapePathForTerminal(linuxTarPath)} -C ${escapePathForTerminal(buildFolder)} ${escapePathForTerminal(basename(appBundleFolderPath))}`);
|
|
1454
|
+
artifactsToUpload.push(linuxTarPath);
|
|
1455
|
+
}
|
|
1175
1456
|
}
|
|
1176
1457
|
|
|
1177
1458
|
// refresh artifacts folder
|
|
@@ -1190,11 +1471,15 @@ if (commandArg === "init") {
|
|
|
1190
1471
|
// the download button or display on your marketing site or in the app.
|
|
1191
1472
|
version: config.app.version,
|
|
1192
1473
|
hash: hash.toString(),
|
|
1474
|
+
platform: OS,
|
|
1475
|
+
arch: ARCH,
|
|
1193
1476
|
// channel: buildEnvironment,
|
|
1194
1477
|
// bucketUrl: config.release.bucketUrl
|
|
1195
1478
|
});
|
|
1196
1479
|
|
|
1197
|
-
|
|
1480
|
+
// Platform-specific update.json
|
|
1481
|
+
const platformUpdateJsonName = `update${platformSuffix}.json`;
|
|
1482
|
+
await Bun.write(join(artifactFolder, platformUpdateJsonName), updateJsonContent);
|
|
1198
1483
|
|
|
1199
1484
|
// generate bsdiff
|
|
1200
1485
|
// https://storage.googleapis.com/eggbun-static/electrobun-playground/canary/ElectrobunPlayground-canary.app.tar.zst
|
|
@@ -1204,7 +1489,7 @@ if (commandArg === "init") {
|
|
|
1204
1489
|
const urlToPrevUpdateJson = join(
|
|
1205
1490
|
config.release.bucketUrl,
|
|
1206
1491
|
buildEnvironment,
|
|
1207
|
-
`update.json`
|
|
1492
|
+
`update${platformSuffix}.json`
|
|
1208
1493
|
);
|
|
1209
1494
|
const cacheBuster = Math.random().toString(36).substring(7);
|
|
1210
1495
|
const updateJsonResponse = await fetch(
|
|
@@ -1216,13 +1501,13 @@ if (commandArg === "init") {
|
|
|
1216
1501
|
const urlToLatestTarball = join(
|
|
1217
1502
|
config.release.bucketUrl,
|
|
1218
1503
|
buildEnvironment,
|
|
1219
|
-
`${appFileName}.app.tar.zst`
|
|
1504
|
+
`${appFileName}.app${platformSuffix}.tar.zst`
|
|
1220
1505
|
);
|
|
1221
1506
|
|
|
1222
1507
|
|
|
1223
1508
|
// attempt to get the previous version to create a patch file
|
|
1224
|
-
if (updateJsonResponse.ok) {
|
|
1225
|
-
const prevUpdateJson = await updateJsonResponse
|
|
1509
|
+
if (updateJsonResponse && updateJsonResponse.ok) {
|
|
1510
|
+
const prevUpdateJson = await updateJsonResponse!.json();
|
|
1226
1511
|
|
|
1227
1512
|
const prevHash = prevUpdateJson.hash;
|
|
1228
1513
|
console.log("PREVIOUS HASH", prevHash);
|
|
@@ -1261,9 +1546,10 @@ if (commandArg === "init") {
|
|
|
1261
1546
|
console.log("diff previous and new tarballs...");
|
|
1262
1547
|
// Run it as a separate process to leverage multi-threadedness
|
|
1263
1548
|
// especially for creating multiple diffs in parallel
|
|
1264
|
-
const bsdiffpath =
|
|
1549
|
+
const bsdiffpath = targetPaths.BSDIFF;
|
|
1265
1550
|
const patchFilePath = join(buildFolder, `${prevHash}.patch`);
|
|
1266
|
-
|
|
1551
|
+
const platformPatchFilePath = join(buildFolder, `${prevHash}${platformSuffix}.patch`);
|
|
1552
|
+
artifactsToUpload.push(platformPatchFilePath);
|
|
1267
1553
|
const result = Bun.spawnSync(
|
|
1268
1554
|
[bsdiffpath, prevTarballPath, tarPath, patchFilePath, "--use-zstd"],
|
|
1269
1555
|
{ cwd: buildFolder }
|
|
@@ -1273,6 +1559,8 @@ if (commandArg === "init") {
|
|
|
1273
1559
|
result.stdout.toString(),
|
|
1274
1560
|
result.stderr.toString()
|
|
1275
1561
|
);
|
|
1562
|
+
// Copy to platform-specific filename
|
|
1563
|
+
cpSync(patchFilePath, platformPatchFilePath);
|
|
1276
1564
|
}
|
|
1277
1565
|
} else {
|
|
1278
1566
|
console.log("prevoius version not found at: ", urlToLatestTarball);
|
|
@@ -1587,8 +1875,8 @@ function notarizeAndStaple(appOrDmgPath: string) {
|
|
|
1587
1875
|
// have the same name but different subfolders in our build directory. or I guess delete the first one after tar/compression and then create the other one.
|
|
1588
1876
|
// either way you can pass in the parent folder here for that flexibility.
|
|
1589
1877
|
// for intel/arm builds on mac we'll probably have separate subfolders as well and build them in parallel.
|
|
1590
|
-
function createAppBundle(bundleName: string, parentFolder: string) {
|
|
1591
|
-
if (
|
|
1878
|
+
function createAppBundle(bundleName: string, parentFolder: string, targetOS: 'macos' | 'win' | 'linux') {
|
|
1879
|
+
if (targetOS === 'macos') {
|
|
1592
1880
|
// macOS bundle structure
|
|
1593
1881
|
const bundleFileName = `${bundleName}.app`;
|
|
1594
1882
|
const appBundleFolderPath = join(parentFolder, bundleFileName);
|
|
@@ -1615,7 +1903,7 @@ function createAppBundle(bundleName: string, parentFolder: string) {
|
|
|
1615
1903
|
appBundleFolderResourcesPath,
|
|
1616
1904
|
appBundleFolderFrameworksPath,
|
|
1617
1905
|
};
|
|
1618
|
-
} else if (
|
|
1906
|
+
} else if (targetOS === 'linux' || targetOS === 'win') {
|
|
1619
1907
|
// Linux/Windows simpler structure
|
|
1620
1908
|
const appBundleFolderPath = join(parentFolder, bundleName);
|
|
1621
1909
|
const appBundleFolderContentsPath = appBundleFolderPath; // No Contents folder needed
|
|
@@ -1636,6 +1924,6 @@ function createAppBundle(bundleName: string, parentFolder: string) {
|
|
|
1636
1924
|
appBundleFolderFrameworksPath,
|
|
1637
1925
|
};
|
|
1638
1926
|
} else {
|
|
1639
|
-
throw new Error(`Unsupported OS: ${
|
|
1927
|
+
throw new Error(`Unsupported OS: ${targetOS}`);
|
|
1640
1928
|
}
|
|
1641
1929
|
}
|