gitarsenal-cli
Version:
CLI tool for creating Modal sandboxes with GitHub repositories
256 lines (219 loc) • 7.09 kB
JavaScript
const { promisify } = require('util');
const { exec } = require('child_process');
const which = require('which');
const chalk = require('chalk');
const path = require('path');
const fs = require('fs');
const execAsync = promisify(exec);
/**
* Get the Python executable path from the virtual environment
* @returns {string} - Path to Python executable or 'python3'
*/
function getPythonExecutable() {
// Check if PYTHON_EXECUTABLE is set by the virtual environment activation
if (process.env.PYTHON_EXECUTABLE && fs.existsSync(process.env.PYTHON_EXECUTABLE)) {
return process.env.PYTHON_EXECUTABLE;
}
// Try to find the virtual environment Python
const venvPath = path.join(__dirname, '..', '.venv');
const isWindows = process.platform === 'win32';
// Check for uv-style virtual environment first
const uvPythonPath = path.join(venvPath, 'bin', 'python');
// Check for traditional venv structure
const traditionalPythonPath = isWindows ?
path.join(venvPath, 'Scripts', 'python.exe') :
path.join(venvPath, 'bin', 'python');
if (fs.existsSync(uvPythonPath)) {
return uvPythonPath;
} else if (fs.existsSync(traditionalPythonPath)) {
return traditionalPythonPath;
}
// Fall back to system Python
return isWindows ? 'python' : 'python3';
}
/**
* Check if a command is available in the system
* @param {string} command - Command to check
* @returns {Promise<boolean>} - True if command exists
*/
async function commandExists(command) {
try {
await which(command);
return true;
} catch (error) {
return false;
}
}
/**
* Check Python version
* @returns {Promise<{exists: boolean, version: string|null}>}
*/
async function checkPython() {
const pythonExecutable = getPythonExecutable();
try {
const { stdout } = await execAsync(`${pythonExecutable} --version`);
const version = stdout.trim().split(' ')[1];
return { exists: true, version };
} catch (error) {
try {
const { stdout } = await execAsync('python3 --version');
const version = stdout.trim().split(' ')[1];
return { exists: true, version };
} catch (error) {
return { exists: false, version: null };
}
}
}
/**
* Check if Modal is installed
* @returns {Promise<boolean>}
*/
async function checkModal() {
const pythonExecutable = getPythonExecutable();
try {
// Check if modal is importable in the Python environment
await execAsync(`${pythonExecutable} -c "import modal"`);
return true;
} catch (error) {
try {
// Fall back to checking the modal command
await execAsync('modal --version');
return true;
} catch (error) {
return false;
}
}
}
/**
* Check if E2B code interpreter is installed
* @returns {Promise<boolean>}
*/
async function checkE2B() {
const pythonExecutable = getPythonExecutable();
try {
// Use a more reliable method to check if the module is importable
const { stdout } = await execAsync(`${pythonExecutable} -c "import importlib.util; print(importlib.util.find_spec('e2b_code_interpreter') is not None)"`);
return stdout.trim() === 'True';
} catch (error) {
return false;
}
}
/**
* Check if Git is installed
* @returns {Promise<boolean>}
*/
async function checkGit() {
try {
await execAsync('git --version');
return true;
} catch (error) {
return false;
}
}
async function checkGitingest() {
const pythonExecutable = getPythonExecutable();
try {
// Check if gitingest is importable in the Python environment
await execAsync(`${pythonExecutable} -c "import gitingest"`);
return true;
} catch (error) {
try {
// Fall back to checking the gitingest command
await execAsync('gitingest --help');
return true;
} catch (error) {
return false;
}
}
}
/**
* Check all required dependencies
* @returns {Promise<boolean>} - True if all dependencies are met
*/
async function checkDependencies() {
const pythonCheck = await checkPython();
const modalExists = await checkModal();
const e2bExists = await checkE2B();
const gitExists = await checkGit();
const gitingestExists = await checkGitingest();
let allDependenciesMet = true;
console.log('\n--- Dependency Check ---');
if (pythonCheck.exists) {
console.log(`${chalk.green('✓')} Python ${pythonCheck.version} found`);
} else {
console.log(`${chalk.red('✗')} Python not found. Please install Python 3.8 or newer.`);
allDependenciesMet = false;
}
if (modalExists) {
console.log(`${chalk.green('✓')} Modal CLI found`);
} else {
console.log(`${chalk.red('✗')} Modal CLI not found. Please install it with: pip install modal`);
allDependenciesMet = false;
}
if (e2bExists) {
console.log(`${chalk.green('✓')} E2B Code Interpreter found`);
} else {
console.log(`${chalk.red('✗')} E2B Code Interpreter not found. Please install it with: pip install e2b-code-interpreter`);
allDependenciesMet = false;
}
if (gitingestExists) {
console.log(`${chalk.green('✓')} Gitingest CLI found`);
} else {
console.log(`${chalk.red('✗')} Gitingest CLI not found. Please install it with: pip install gitingest`);
allDependenciesMet = false;
}
if (gitExists) {
console.log(`${chalk.green('✓')} Git found`);
} else {
console.log(`${chalk.red('✗')} Git not found. Please install Git.`);
allDependenciesMet = false;
}
console.log('------------------------\n');
return allDependenciesMet;
}
/**
* Check all required dependencies except E2B
* @returns {Promise<boolean>} - True if all dependencies are met
*/
async function checkDependenciesExceptE2B() {
const pythonCheck = await checkPython();
const modalExists = await checkModal();
const gitExists = await checkGit();
const gitingestExists = await checkGitingest();
let allDependenciesMet = true;
console.log('\n--- Dependency Check ---');
if (pythonCheck.exists) {
console.log(`${chalk.green('✓')} Python ${pythonCheck.version} found`);
} else {
console.log(`${chalk.red('✗')} Python not found. Please install Python 3.8 or newer.`);
allDependenciesMet = false;
}
if (modalExists) {
console.log(`${chalk.green('✓')} Modal CLI found`);
} else {
console.log(`${chalk.yellow('!')} Modal CLI not found. Not required for E2B sandbox.`);
}
if (gitingestExists) {
console.log(`${chalk.green('✓')} Gitingest CLI found`);
} else {
console.log(`${chalk.red('✗')} Gitingest CLI not found. Please install it with: pip install gitingest`);
allDependenciesMet = false;
}
if (gitExists) {
console.log(`${chalk.green('✓')} Git found`);
} else {
console.log(`${chalk.red('✗')} Git not found. Please install Git.`);
allDependenciesMet = false;
}
console.log('------------------------\n');
return allDependenciesMet;
}
module.exports = {
checkDependencies,
checkDependenciesExceptE2B,
commandExists,
checkPython,
checkModal,
checkE2B,
checkGit
};