feat: goose windows (#880)
Co-authored-by: Ryan Versaw <ryan@versaw.com>
This commit is contained in:
+78
-12
@@ -1,6 +1,7 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { createServer } from 'net';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { getBinaryPath } from './utils/binaryPath';
|
||||
import log from './utils/logger';
|
||||
import { ChildProcessByStdio } from 'node:child_process';
|
||||
@@ -8,7 +9,7 @@ import { Readable } from 'node:stream';
|
||||
|
||||
// Find an available port to start goosed on
|
||||
export const findAvailablePort = (): Promise<number> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const server = createServer();
|
||||
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
@@ -56,16 +57,18 @@ export const startGoosed = async (
|
||||
): Promise<[number, string, ChildProcessByStdio<null, Readable, Readable>]> => {
|
||||
// we default to running goosed in home dir - if not specified
|
||||
const homeDir = os.homedir();
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
// Ensure dir is properly normalized for the platform
|
||||
if (!dir) {
|
||||
dir = homeDir;
|
||||
}
|
||||
dir = path.normalize(dir);
|
||||
|
||||
// Get the goosed binary path using the shared utility
|
||||
const goosedPath = getBinaryPath(app, 'goosed');
|
||||
let goosedPath = getBinaryPath(app, 'goosed');
|
||||
const port = await findAvailablePort();
|
||||
|
||||
// in case we want it
|
||||
//const isPackaged = app.isPackaged;
|
||||
log.info(`Starting goosed from: ${goosedPath} on port ${port} in dir ${dir}`);
|
||||
|
||||
// Define additional environment variables
|
||||
@@ -74,12 +77,15 @@ export const startGoosed = async (
|
||||
HOME: homeDir,
|
||||
// Set USERPROFILE for Windows
|
||||
USERPROFILE: homeDir,
|
||||
|
||||
// Set APPDATA for Windows
|
||||
APPDATA: process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming'),
|
||||
// Set LOCAL_APPDATA for Windows
|
||||
LOCALAPPDATA: process.env.LOCALAPPDATA || path.join(homeDir, 'AppData', 'Local'),
|
||||
// Set PATH to include the binary directory
|
||||
PATH: `${path.dirname(goosedPath)}${path.delimiter}${process.env.PATH}`,
|
||||
// start with the port specified
|
||||
GOOSE_PORT: String(port),
|
||||
|
||||
GOOSE_SERVER__SECRET_KEY: process.env.GOOSE_SERVER__SECRET_KEY,
|
||||
|
||||
// Add any additional environment variables passed in
|
||||
...env,
|
||||
};
|
||||
@@ -87,12 +93,54 @@ export const startGoosed = async (
|
||||
// Merge parent environment with additional environment variables
|
||||
const processEnv = { ...process.env, ...additionalEnv };
|
||||
|
||||
// Spawn the goosed process with the user's home directory as cwd
|
||||
const goosedProcess = spawn(goosedPath, ['agent'], {
|
||||
// Add detailed logging for troubleshooting
|
||||
log.info(`Process platform: ${process.platform}`);
|
||||
log.info(`Process cwd: ${process.cwd()}`);
|
||||
log.info(`Target working directory: ${dir}`);
|
||||
log.info(`Environment HOME: ${processEnv.HOME}`);
|
||||
log.info(`Environment USERPROFILE: ${processEnv.USERPROFILE}`);
|
||||
log.info(`Environment APPDATA: ${processEnv.APPDATA}`);
|
||||
log.info(`Environment LOCALAPPDATA: ${processEnv.LOCALAPPDATA}`);
|
||||
log.info(`Environment PATH: ${processEnv.PATH}`);
|
||||
|
||||
// Ensure proper executable path on Windows
|
||||
if (isWindows && !goosedPath.toLowerCase().endsWith('.exe')) {
|
||||
goosedPath += '.exe';
|
||||
}
|
||||
log.info(`Binary path resolved to: ${goosedPath}`);
|
||||
|
||||
// Verify binary exists
|
||||
try {
|
||||
const fs = require('fs');
|
||||
const stats = fs.statSync(goosedPath);
|
||||
log.info(`Binary exists: ${stats.isFile()}`);
|
||||
} catch (error) {
|
||||
log.error(`Binary not found at ${goosedPath}:`, error);
|
||||
throw new Error(`Binary not found at ${goosedPath}`);
|
||||
}
|
||||
|
||||
const spawnOptions = {
|
||||
cwd: dir,
|
||||
env: processEnv,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
// Hide terminal window on Windows
|
||||
windowsHide: true,
|
||||
// Run detached on Windows only to avoid terminal windows
|
||||
detached: isWindows,
|
||||
// Never use shell to avoid terminal windows
|
||||
shell: false,
|
||||
};
|
||||
|
||||
// Log spawn options for debugging
|
||||
log.info('Spawn options:', JSON.stringify(spawnOptions, null, 2));
|
||||
|
||||
// Spawn the goosed process
|
||||
const goosedProcess = spawn(goosedPath, ['agent'], spawnOptions);
|
||||
|
||||
// Only unref on Windows to allow it to run independently of the parent
|
||||
if (isWindows) {
|
||||
goosedProcess.unref();
|
||||
}
|
||||
|
||||
goosedProcess.stdout.on('data', (data) => {
|
||||
log.info(`goosed stdout for port ${port} and dir ${dir}: ${data.toString()}`);
|
||||
@@ -116,7 +164,16 @@ export const startGoosed = async (
|
||||
log.info(`Goosed isReady ${isReady}`);
|
||||
if (!isReady) {
|
||||
log.error(`Goosed server failed to start on port ${port}`);
|
||||
goosedProcess.kill();
|
||||
try {
|
||||
if (isWindows) {
|
||||
// On Windows, use taskkill to forcefully terminate the process tree
|
||||
spawn('taskkill', ['/pid', goosedProcess.pid.toString(), '/T', '/F']);
|
||||
} else {
|
||||
goosedProcess.kill();
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error while terminating goosed process:', error);
|
||||
}
|
||||
throw new Error(`Goosed server failed to start on port ${port}`);
|
||||
}
|
||||
|
||||
@@ -124,7 +181,16 @@ export const startGoosed = async (
|
||||
// TODO will need to do it at tab level next
|
||||
app.on('will-quit', () => {
|
||||
log.info('App quitting, terminating goosed server');
|
||||
goosedProcess.kill();
|
||||
try {
|
||||
if (isWindows) {
|
||||
// On Windows, use taskkill to forcefully terminate the process tree
|
||||
spawn('taskkill', ['/pid', goosedProcess.pid.toString(), '/T', '/F']);
|
||||
} else {
|
||||
goosedProcess.kill();
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error while terminating goosed process:', error);
|
||||
}
|
||||
});
|
||||
|
||||
log.info(`Goosed server successfully started on port ${port}`);
|
||||
|
||||
Reference in New Issue
Block a user