backport: surface docker errors and add --debug flag for worker logs
Cherry-pick of KeygraphHQ/shannon#299 (ccb5303). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ export interface StartArgs {
|
|||||||
workspace?: string;
|
workspace?: string;
|
||||||
output?: string;
|
output?: string;
|
||||||
pipelineTesting: boolean;
|
pipelineTesting: boolean;
|
||||||
|
debug: boolean;
|
||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,14 +111,22 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
...(outputDir && { outputDir }),
|
...(outputDir && { outputDir }),
|
||||||
workspace,
|
workspace,
|
||||||
...(args.pipelineTesting && { pipelineTesting: true }),
|
...(args.pipelineTesting && { pipelineTesting: true }),
|
||||||
|
...(args.debug && { debug: true }),
|
||||||
});
|
});
|
||||||
|
|
||||||
// 14. Wait for workflow to register, then display info
|
// 14. Bail if `docker run -d` itself fails (mount error, image missing, etc.)
|
||||||
proc.on('error', (err) => {
|
const dockerExitCode = await new Promise<number>((resolve) => {
|
||||||
console.error(`Failed to start worker: ${err.message}`);
|
proc.once('exit', (code) => resolve(code ?? 1));
|
||||||
process.exit(1);
|
proc.once('error', (err) => {
|
||||||
|
console.error(`Failed to start worker: ${err.message}`);
|
||||||
|
resolve(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (dockerExitCode !== 0) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
// Detect whether this is a fresh workspace or a resume by checking session.json existence
|
// Detect whether this is a fresh workspace or a resume by checking session.json existence
|
||||||
const sessionJson = path.join(workspacesDir, workspace, 'session.json');
|
const sessionJson = path.join(workspacesDir, workspace, 'session.json');
|
||||||
const isResume = fs.existsSync(sessionJson);
|
const isResume = fs.existsSync(sessionJson);
|
||||||
@@ -182,6 +191,9 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
} catch {
|
} catch {
|
||||||
// Container may have already exited
|
// Container may have already exited
|
||||||
}
|
}
|
||||||
|
if (args.debug) {
|
||||||
|
printDebugHint(containerName);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
@@ -195,6 +207,14 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
process.on('exit', cleanup);
|
process.on('exit', cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printDebugHint(containerName: string): void {
|
||||||
|
console.log('');
|
||||||
|
console.log(` Worker container preserved: ${containerName}`);
|
||||||
|
console.log(` Inspect logs: docker logs ${containerName}`);
|
||||||
|
console.log(` Remove: docker rm ${containerName}`);
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
function printInfo(
|
function printInfo(
|
||||||
args: StartArgs,
|
args: StartArgs,
|
||||||
workspace: string,
|
workspace: string,
|
||||||
|
|||||||
+11
-3
@@ -159,13 +159,19 @@ export interface WorkerOptions {
|
|||||||
outputDir?: string;
|
outputDir?: string;
|
||||||
workspace: string;
|
workspace: string;
|
||||||
pipelineTesting?: boolean;
|
pipelineTesting?: boolean;
|
||||||
|
debug?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spawn the worker container in detached mode and return the process.
|
* Spawn the worker container in detached mode and return the process.
|
||||||
|
* When `opts.debug` is true, omits `--rm` so the container persists for log inspection.
|
||||||
*/
|
*/
|
||||||
export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
||||||
const args = ['run', '-d', '--rm', '--name', opts.containerName, '--network', 'shannon-net'];
|
const args = ['run', '-d'];
|
||||||
|
if (!opts.debug) {
|
||||||
|
args.push('--rm');
|
||||||
|
}
|
||||||
|
args.push('--name', opts.containerName, '--network', 'shannon-net');
|
||||||
|
|
||||||
// Add host flag for Linux
|
// Add host flag for Linux
|
||||||
args.push(...addHostFlag());
|
args.push(...addHostFlag());
|
||||||
@@ -227,9 +233,11 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
|||||||
args.push('--pipeline-testing');
|
args.push('--pipeline-testing');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent MSYS/Git Bash from converting Unix paths (e.g. /repos/my-repo) to Windows paths
|
// Inherit stderr so `docker run` daemon errors surface to the user;
|
||||||
|
// ignore stdin/stdout (the container ID is noise).
|
||||||
return spawn('docker', args, {
|
return spawn('docker', args, {
|
||||||
stdio: 'pipe',
|
stdio: ['ignore', 'ignore', 'inherit'],
|
||||||
|
// Prevent MSYS/Git Bash from converting Unix paths on Windows
|
||||||
...(os.platform() === 'win32' && { env: { ...process.env, MSYS_NO_PATHCONV: '1' } }),
|
...(os.platform() === 'win32' && { env: { ...process.env, MSYS_NO_PATHCONV: '1' } }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ Options for 'start':
|
|||||||
-o, --output <path> Copy deliverables to this directory after run
|
-o, --output <path> Copy deliverables to this directory after run
|
||||||
-w, --workspace <name> Named workspace (auto-resumes if exists)
|
-w, --workspace <name> Named workspace (auto-resumes if exists)
|
||||||
--pipeline-testing Use minimal prompts for fast testing
|
--pipeline-testing Use minimal prompts for fast testing
|
||||||
|
--debug Preserve worker container after exit for log inspection
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
${prefix} start -u https://example.com -r ${mode === 'local' ? 'my-repo' : './my-repo'}
|
${prefix} start -u https://example.com -r ${mode === 'local' ? 'my-repo' : './my-repo'}
|
||||||
@@ -94,6 +95,7 @@ interface ParsedStartArgs {
|
|||||||
workspace?: string;
|
workspace?: string;
|
||||||
output?: string;
|
output?: string;
|
||||||
pipelineTesting: boolean;
|
pipelineTesting: boolean;
|
||||||
|
debug: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStartArgs(argv: string[]): ParsedStartArgs {
|
function parseStartArgs(argv: string[]): ParsedStartArgs {
|
||||||
@@ -103,6 +105,7 @@ function parseStartArgs(argv: string[]): ParsedStartArgs {
|
|||||||
let workspace: string | undefined;
|
let workspace: string | undefined;
|
||||||
let output: string | undefined;
|
let output: string | undefined;
|
||||||
let pipelineTesting = false;
|
let pipelineTesting = false;
|
||||||
|
let debug = false;
|
||||||
|
|
||||||
for (let i = 0; i < argv.length; i++) {
|
for (let i = 0; i < argv.length; i++) {
|
||||||
const arg = argv[i];
|
const arg = argv[i];
|
||||||
@@ -147,6 +150,9 @@ function parseStartArgs(argv: string[]): ParsedStartArgs {
|
|||||||
case '--pipeline-testing':
|
case '--pipeline-testing':
|
||||||
pipelineTesting = true;
|
pipelineTesting = true;
|
||||||
break;
|
break;
|
||||||
|
case '--debug':
|
||||||
|
debug = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.error(`Unknown option: ${arg}`);
|
console.error(`Unknown option: ${arg}`);
|
||||||
console.error(`Run "${getMode() === 'local' ? './shannon' : 'npx @keygraph/shannon'} help" for usage`);
|
console.error(`Run "${getMode() === 'local' ? './shannon' : 'npx @keygraph/shannon'} help" for usage`);
|
||||||
@@ -164,6 +170,7 @@ function parseStartArgs(argv: string[]): ParsedStartArgs {
|
|||||||
url,
|
url,
|
||||||
repo,
|
repo,
|
||||||
pipelineTesting,
|
pipelineTesting,
|
||||||
|
debug,
|
||||||
...(config && { config }),
|
...(config && { config }),
|
||||||
...(workspace && { workspace }),
|
...(workspace && { workspace }),
|
||||||
...(output && { output }),
|
...(output && { output }),
|
||||||
|
|||||||
Reference in New Issue
Block a user