mirror of
https://github.com/steel-dev/cli.git
synced 2026-09-14 20:47:34 +08:00
163 lines
4.2 KiB
TypeScript
163 lines
4.2 KiB
TypeScript
import React, {useEffect, useState, useRef} from 'react';
|
|
import {spawn, ChildProcess} from 'child_process';
|
|
import {Task} from 'ink-task-list';
|
|
import {useTask} from '../../hooks/usetask.js';
|
|
import {useRunStep} from '../../context/runstepcontext.js';
|
|
import {CONFIG_DIR, REPO_URL} from '../../utils/constants.js';
|
|
import path from 'path';
|
|
import spinners from 'cli-spinners';
|
|
|
|
function isDockerRunning() {
|
|
try {
|
|
const child = spawn('docker', ['info'], {stdio: 'ignore'});
|
|
return new Promise<boolean>(resolve => {
|
|
child.on('exit', code => {
|
|
resolve(code === 0);
|
|
});
|
|
child.on('error', () => {
|
|
resolve(false);
|
|
});
|
|
});
|
|
} catch {
|
|
return Promise.resolve(false);
|
|
}
|
|
}
|
|
|
|
export default function BrowserRunner() {
|
|
const [state, task, , , setTask, setLoading, setError] = useTask();
|
|
const {step, setStep, envVars} = useRunStep();
|
|
const [output, setOutput] = useState<string>('');
|
|
const childProcessRef = useRef<ChildProcess | null>(null);
|
|
|
|
const cleanupChildProcess = () => {
|
|
if (childProcessRef.current && !childProcessRef.current.killed) {
|
|
childProcessRef.current.kill('SIGTERM');
|
|
setTimeout(() => {
|
|
if (childProcessRef.current && !childProcessRef.current.killed) {
|
|
childProcessRef.current.kill('SIGKILL');
|
|
}
|
|
childProcessRef.current = null;
|
|
}, 2000);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
cleanupChildProcess();
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (step === 'browserrunner' && !task) {
|
|
setLoading(true);
|
|
|
|
async function startBrowserRunner() {
|
|
try {
|
|
if (!envVars['STEEL_API_URL'].includes('localhost')) {
|
|
setTask('Skipping Docker Compose');
|
|
setStep('runner');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
const cloneChild = spawn('git', ['clone', REPO_URL], {
|
|
cwd: CONFIG_DIR,
|
|
stdio: 'ignore',
|
|
});
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
cloneChild.on('exit', code => {
|
|
if (code === 0 || code === 128) {
|
|
// 128 = already exists
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`Git clone failed with code ${code}`));
|
|
}
|
|
});
|
|
cloneChild.on('error', reject);
|
|
});
|
|
|
|
const dockerRunning = await isDockerRunning();
|
|
if (!dockerRunning) {
|
|
console.log(
|
|
'⚠️ Docker is not running. Please start it and try again.',
|
|
);
|
|
setError('Docker is not running');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
console.log('🚀 Starting Docker Compose...');
|
|
const folderName = path.basename(REPO_URL, '.git');
|
|
const child = spawn(
|
|
'docker-compose',
|
|
['-f', 'docker-compose.yml', 'up', '-d'],
|
|
{
|
|
cwd: path.join(CONFIG_DIR, folderName),
|
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
env: {
|
|
...process.env,
|
|
API_PORT: String(
|
|
Number(envVars['STEEL_API_URL'].split(':')[2]) ||
|
|
Number(envVars['STEEL_API_URL'].split(':')[1]) ||
|
|
3000,
|
|
),
|
|
},
|
|
},
|
|
);
|
|
|
|
childProcessRef.current = child;
|
|
|
|
// Stream stdout to output state
|
|
child.stdout?.on('data', data => {
|
|
const text = data.toString();
|
|
setOutput(prev => prev + text);
|
|
});
|
|
|
|
// Stream stderr to output state
|
|
child.stderr?.on('data', data => {
|
|
const text = data.toString();
|
|
setOutput(prev => prev + text);
|
|
});
|
|
|
|
child.on('close', (code, signal) => {
|
|
childProcessRef.current = null;
|
|
|
|
if (code === 0) {
|
|
setTask('Docker Compose started successfully');
|
|
setStep('runner');
|
|
} else if (signal) {
|
|
setError(`Docker Compose terminated by signal: ${signal}`);
|
|
} else {
|
|
setError(`Docker Compose failed with exit code ${code}`);
|
|
}
|
|
setLoading(false);
|
|
});
|
|
|
|
child.on('error', error => {
|
|
childProcessRef.current = null;
|
|
setError(`Error starting Steel Browser Locally: ${error.message}`);
|
|
setLoading(false);
|
|
});
|
|
} catch (error) {
|
|
console.error('Error in browser runner:', error);
|
|
setError(`Error starting Steel Browser Locally: ${error.message}`);
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
startBrowserRunner();
|
|
}
|
|
}, [step]);
|
|
|
|
return (
|
|
<Task
|
|
label={`Running Steel Browser locally`}
|
|
state={state}
|
|
spinner={spinners.dots}
|
|
isExpanded={state === 'loading'}
|
|
output={state === 'loading' ? output : undefined}
|
|
/>
|
|
);
|
|
}
|