mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
feat: add GitHub issue fix command and release command
- Introduced a new command for fetching and validating GitHub issues, allowing users to address issues directly from the command line. - Added a release command to bump the version of the application and build the Electron app, ensuring version consistency across UI and server packages. - Updated package.json files for both UI and server to version 0.7.1, reflecting the latest changes. - Implemented version utility in the server to read the version from package.json, enhancing version management across the application.
This commit is contained in:
@@ -11,6 +11,7 @@ import path from 'path';
|
||||
import { spawn, execSync, ChildProcess } from 'child_process';
|
||||
import crypto from 'crypto';
|
||||
import http, { Server } from 'http';
|
||||
import net from 'net';
|
||||
import { app, BrowserWindow, ipcMain, dialog, shell, screen } from 'electron';
|
||||
import {
|
||||
findNodeExecutable,
|
||||
@@ -51,8 +52,46 @@ if (isDev) {
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
let serverProcess: ChildProcess | null = null;
|
||||
let staticServer: Server | null = null;
|
||||
const SERVER_PORT = 3008;
|
||||
const STATIC_PORT = 3007;
|
||||
|
||||
// Default ports - will be dynamically assigned if these are in use
|
||||
const DEFAULT_SERVER_PORT = 3008;
|
||||
const DEFAULT_STATIC_PORT = 3007;
|
||||
|
||||
// Actual ports in use (set during startup)
|
||||
let serverPort = DEFAULT_SERVER_PORT;
|
||||
let staticPort = DEFAULT_STATIC_PORT;
|
||||
|
||||
/**
|
||||
* Check if a port is available
|
||||
*/
|
||||
function isPortAvailable(port: number): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const server = net.createServer();
|
||||
server.once('error', () => {
|
||||
resolve(false);
|
||||
});
|
||||
server.once('listening', () => {
|
||||
server.close(() => {
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
server.listen(port, '127.0.0.1');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an available port starting from the preferred port
|
||||
* Tries up to 100 ports in sequence
|
||||
*/
|
||||
async function findAvailablePort(preferredPort: number): Promise<number> {
|
||||
for (let offset = 0; offset < 100; offset++) {
|
||||
const port = preferredPort + offset;
|
||||
if (await isPortAvailable(port)) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find an available port starting from ${preferredPort}`);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Window sizing constants for kanban layout
|
||||
@@ -326,8 +365,8 @@ async function startStaticServer(): Promise<void> {
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
staticServer!.listen(STATIC_PORT, () => {
|
||||
console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`);
|
||||
staticServer!.listen(staticPort, () => {
|
||||
console.log(`[Electron] Static server running at http://localhost:${staticPort}`);
|
||||
resolve();
|
||||
});
|
||||
staticServer!.on('error', reject);
|
||||
@@ -432,7 +471,7 @@ async function startServer(): Promise<void> {
|
||||
const env = {
|
||||
...process.env,
|
||||
PATH: enhancedPath,
|
||||
PORT: SERVER_PORT.toString(),
|
||||
PORT: serverPort.toString(),
|
||||
DATA_DIR: app.getPath('userData'),
|
||||
NODE_PATH: serverNodeModules,
|
||||
// Pass API key to server for CSRF protection
|
||||
@@ -444,6 +483,8 @@ async function startServer(): Promise<void> {
|
||||
}),
|
||||
};
|
||||
|
||||
console.log(`[Electron] Server will use port ${serverPort}`);
|
||||
|
||||
console.log('[Electron] Starting backend server...');
|
||||
console.log('[Electron] Server path:', serverPath);
|
||||
console.log('[Electron] Server root (cwd):', serverRoot);
|
||||
@@ -483,7 +524,7 @@ async function waitForServer(maxAttempts = 30): Promise<void> {
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:${SERVER_PORT}/api/health`, (res) => {
|
||||
const req = http.get(`http://localhost:${serverPort}/api/health`, (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
resolve();
|
||||
} else {
|
||||
@@ -548,9 +589,9 @@ function createWindow(): void {
|
||||
mainWindow.loadURL(VITE_DEV_SERVER_URL);
|
||||
} else if (isDev) {
|
||||
// Fallback for dev without Vite server URL
|
||||
mainWindow.loadURL(`http://localhost:${STATIC_PORT}`);
|
||||
mainWindow.loadURL(`http://localhost:${staticPort}`);
|
||||
} else {
|
||||
mainWindow.loadURL(`http://localhost:${STATIC_PORT}`);
|
||||
mainWindow.loadURL(`http://localhost:${staticPort}`);
|
||||
}
|
||||
|
||||
if (isDev && process.env.OPEN_DEVTOOLS === 'true') {
|
||||
@@ -642,6 +683,21 @@ app.whenReady().then(async () => {
|
||||
ensureApiKey();
|
||||
|
||||
try {
|
||||
// Find available ports (prevents conflicts with other apps using same ports)
|
||||
serverPort = await findAvailablePort(DEFAULT_SERVER_PORT);
|
||||
if (serverPort !== DEFAULT_SERVER_PORT) {
|
||||
console.log(
|
||||
`[Electron] Default server port ${DEFAULT_SERVER_PORT} in use, using port ${serverPort}`
|
||||
);
|
||||
}
|
||||
|
||||
staticPort = await findAvailablePort(DEFAULT_STATIC_PORT);
|
||||
if (staticPort !== DEFAULT_STATIC_PORT) {
|
||||
console.log(
|
||||
`[Electron] Default static port ${DEFAULT_STATIC_PORT} in use, using port ${staticPort}`
|
||||
);
|
||||
}
|
||||
|
||||
// Start static file server in production
|
||||
if (app.isPackaged) {
|
||||
await startStaticServer();
|
||||
@@ -675,6 +731,28 @@ app.whenReady().then(async () => {
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
// Stop the server when all windows are closed, even on macOS
|
||||
// This prevents port conflicts when reopening the app
|
||||
if (serverProcess && serverProcess.pid) {
|
||||
console.log('[Electron] All windows closed, stopping server...');
|
||||
if (process.platform === 'win32') {
|
||||
try {
|
||||
execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' });
|
||||
} catch (error) {
|
||||
console.error('[Electron] Failed to kill server process:', (error as Error).message);
|
||||
}
|
||||
} else {
|
||||
serverProcess.kill('SIGTERM');
|
||||
}
|
||||
serverProcess = null;
|
||||
}
|
||||
|
||||
if (staticServer) {
|
||||
console.log('[Electron] Stopping static server...');
|
||||
staticServer.close();
|
||||
staticServer = null;
|
||||
}
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
@@ -822,7 +900,7 @@ ipcMain.handle('ping', async () => {
|
||||
|
||||
// Get server URL for HTTP client
|
||||
ipcMain.handle('server:getUrl', async () => {
|
||||
return `http://localhost:${SERVER_PORT}`;
|
||||
return `http://localhost:${serverPort}`;
|
||||
});
|
||||
|
||||
// Get API key for authentication
|
||||
|
||||
Reference in New Issue
Block a user