mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
feat: enhance Electron app packaging and server preparation
- Added afterPack script in package.json to rebuild native modules for the server bundle. - Improved icon handling in main.js to support cross-platform formats and verify icon existence. - Updated startStaticServer function to return a promise for better error handling. - Introduced a new script, rebuild-server-natives.js, to rebuild native modules based on the target architecture. - Enhanced prepare-server.js to include native module rebuilding step for improved terminal functionality.
This commit is contained in:
@@ -26,17 +26,34 @@ let staticServer = null;
|
|||||||
const SERVER_PORT = 3008;
|
const SERVER_PORT = 3008;
|
||||||
const STATIC_PORT = 3007;
|
const STATIC_PORT = 3007;
|
||||||
|
|
||||||
// Get icon path - works in both dev and production
|
// Get icon path - works in both dev and production, cross-platform
|
||||||
function getIconPath() {
|
function getIconPath() {
|
||||||
return app.isPackaged
|
// Different icon formats for different platforms
|
||||||
? path.join(process.resourcesPath, "app", "public", "logo.png")
|
let iconFile;
|
||||||
: path.join(__dirname, "../public/logo.png");
|
if (process.platform === "win32") {
|
||||||
|
iconFile = "icon.ico";
|
||||||
|
} else if (process.platform === "darwin") {
|
||||||
|
iconFile = "logo_larger.png";
|
||||||
|
} else {
|
||||||
|
// Linux
|
||||||
|
iconFile = "logo_larger.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconPath = path.join(__dirname, "../public", iconFile);
|
||||||
|
|
||||||
|
// Verify the icon exists
|
||||||
|
if (!fs.existsSync(iconPath)) {
|
||||||
|
console.warn(`[Electron] Icon not found at: ${iconPath}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start static file server for production builds
|
* Start static file server for production builds
|
||||||
*/
|
*/
|
||||||
function startStaticServer() {
|
async function startStaticServer() {
|
||||||
const staticPath = path.join(__dirname, "../out");
|
const staticPath = path.join(__dirname, "../out");
|
||||||
|
|
||||||
staticServer = http.createServer((request, response) => {
|
staticServer = http.createServer((request, response) => {
|
||||||
@@ -89,8 +106,15 @@ function startStaticServer() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
staticServer.listen(STATIC_PORT, () => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`);
|
staticServer.listen(STATIC_PORT, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,12 +257,12 @@ async function waitForServer(maxAttempts = 30) {
|
|||||||
* Create the main window
|
* Create the main window
|
||||||
*/
|
*/
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
mainWindow = new BrowserWindow({
|
const iconPath = getIconPath();
|
||||||
|
const windowOptions = {
|
||||||
width: 1400,
|
width: 1400,
|
||||||
height: 900,
|
height: 900,
|
||||||
minWidth: 1024,
|
minWidth: 1024,
|
||||||
minHeight: 700,
|
minHeight: 700,
|
||||||
icon: getIconPath(),
|
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, "preload.js"),
|
preload: path.join(__dirname, "preload.js"),
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
@@ -246,7 +270,14 @@ function createWindow() {
|
|||||||
},
|
},
|
||||||
titleBarStyle: "hiddenInset",
|
titleBarStyle: "hiddenInset",
|
||||||
backgroundColor: "#0a0a0a",
|
backgroundColor: "#0a0a0a",
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Only set icon if it exists
|
||||||
|
if (iconPath) {
|
||||||
|
windowOptions.icon = iconPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow = new BrowserWindow(windowOptions);
|
||||||
|
|
||||||
// Load Next.js dev server in development or static server in production
|
// Load Next.js dev server in development or static server in production
|
||||||
const isDev = !app.isPackaged;
|
const isDev = !app.isPackaged;
|
||||||
@@ -270,13 +301,20 @@ function createWindow() {
|
|||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
// Set app icon (dock icon on macOS)
|
// Set app icon (dock icon on macOS)
|
||||||
if (process.platform === "darwin" && app.dock) {
|
if (process.platform === "darwin" && app.dock) {
|
||||||
app.dock.setIcon(getIconPath());
|
const iconPath = getIconPath();
|
||||||
|
if (iconPath) {
|
||||||
|
try {
|
||||||
|
app.dock.setIcon(iconPath);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[Electron] Failed to set dock icon:", error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Start static file server in production
|
// Start static file server in production
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
startStaticServer();
|
await startStaticServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start backend server
|
// Start backend server
|
||||||
|
|||||||
@@ -95,6 +95,7 @@
|
|||||||
"appId": "com.automaker.app",
|
"appId": "com.automaker.app",
|
||||||
"productName": "Automaker",
|
"productName": "Automaker",
|
||||||
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
||||||
|
"afterPack": "./scripts/rebuild-server-natives.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "dist"
|
"output": "dist"
|
||||||
},
|
},
|
||||||
@@ -113,6 +114,10 @@
|
|||||||
"from": "server-bundle/node_modules",
|
"from": "server-bundle/node_modules",
|
||||||
"to": "server/node_modules"
|
"to": "server/node_modules"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"from": "server-bundle/package.json",
|
||||||
|
"to": "server/package.json"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"from": "../../.env",
|
"from": "../../.env",
|
||||||
"to": ".env",
|
"to": ".env",
|
||||||
|
|||||||
@@ -64,4 +64,18 @@ execSync('npm install --omit=dev', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Step 6: Rebuild native modules for current architecture
|
||||||
|
// This is critical for modules like node-pty that have native bindings
|
||||||
|
console.log('🔨 Rebuilding native modules for current architecture...');
|
||||||
|
try {
|
||||||
|
execSync('npm rebuild', {
|
||||||
|
cwd: BUNDLE_DIR,
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
console.log('✅ Native modules rebuilt successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('⚠️ Warning: Failed to rebuild native modules. Terminal functionality may not work.');
|
||||||
|
console.warn(' Error:', error.message);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('\n✅ Server prepared for bundling at:', BUNDLE_DIR);
|
console.log('\n✅ Server prepared for bundling at:', BUNDLE_DIR);
|
||||||
|
|||||||
66
apps/app/scripts/rebuild-server-natives.js
Normal file
66
apps/app/scripts/rebuild-server-natives.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Electron-builder afterPack hook
|
||||||
|
* Rebuilds native modules in the server bundle for the target architecture
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
|
exports.default = async function(context) {
|
||||||
|
const { appOutDir, electronPlatformName, arch, packager } = context;
|
||||||
|
const electronVersion = packager.config.electronVersion;
|
||||||
|
|
||||||
|
// Convert arch to string if it's a number (electron-builder sometimes passes indices)
|
||||||
|
const archNames = ['ia32', 'x64', 'armv7l', 'arm64', 'universal'];
|
||||||
|
const archStr = typeof arch === 'number' ? archNames[arch] : arch;
|
||||||
|
|
||||||
|
console.log(`\n🔨 Rebuilding server native modules for ${electronPlatformName}-${archStr}...`);
|
||||||
|
|
||||||
|
// Path to server node_modules in the packaged app
|
||||||
|
let serverNodeModulesPath;
|
||||||
|
if (electronPlatformName === 'darwin') {
|
||||||
|
serverNodeModulesPath = path.join(
|
||||||
|
appOutDir,
|
||||||
|
`${packager.appInfo.productName}.app`,
|
||||||
|
'Contents',
|
||||||
|
'Resources',
|
||||||
|
'server',
|
||||||
|
'node_modules'
|
||||||
|
);
|
||||||
|
} else if (electronPlatformName === 'win32') {
|
||||||
|
serverNodeModulesPath = path.join(
|
||||||
|
appOutDir,
|
||||||
|
'resources',
|
||||||
|
'server',
|
||||||
|
'node_modules'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
serverNodeModulesPath = path.join(
|
||||||
|
appOutDir,
|
||||||
|
'resources',
|
||||||
|
'server',
|
||||||
|
'node_modules'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Rebuild native modules for the target architecture
|
||||||
|
const rebuildCmd = `npx --yes @electron/rebuild --version=${electronVersion} --arch=${archStr} --force --module-dir="${serverNodeModulesPath}/.."`;
|
||||||
|
|
||||||
|
console.log(` Command: ${rebuildCmd}`);
|
||||||
|
|
||||||
|
const { stdout, stderr } = await execAsync(rebuildCmd);
|
||||||
|
if (stdout) console.log(stdout);
|
||||||
|
if (stderr) console.error(stderr);
|
||||||
|
|
||||||
|
console.log(`✅ Server native modules rebuilt successfully for ${archStr}\n`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Failed to rebuild server native modules:`, error.message);
|
||||||
|
// Don't fail the build, just warn
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user