cvm-cli
Version:
A unified CLI tool for managing PHP, Node.js, and Python versions with virtual environment and dependency management support.
156 lines (125 loc) • 6.03 kB
JavaScript
const BaseVersionManager = require('./baseVersionManager');
const fetch = require('node-fetch');
const fs = require('fs-extra');
const chalk = require('chalk');
const path = require('path');
const CVMUtils = require('./utils');
class PythonVersionManager extends BaseVersionManager {
constructor() {
super('python');
}
async getAvailableVersions() {
// For simplicity, return a list of commonly used Python versions
// In a real implementation, you'd fetch from python.org or use pyenv's list
return [
'3.12.0', '3.11.7', '3.11.6', '3.11.5', '3.11.4', '3.11.3', '3.11.2', '3.11.1', '3.11.0',
'3.10.13', '3.10.12', '3.10.11', '3.10.10', '3.10.9', '3.10.8', '3.10.7', '3.10.6', '3.10.5', '3.10.4', '3.10.3', '3.10.2', '3.10.1', '3.10.0',
'3.9.18', '3.9.17', '3.9.16', '3.9.15', '3.9.14', '3.9.13', '3.9.12', '3.9.11', '3.9.10', '3.9.9', '3.9.8', '3.9.7', '3.9.6', '3.9.5', '3.9.4', '3.9.3', '3.9.2', '3.9.1', '3.9.0',
'3.8.18', '3.8.17', '3.8.16', '3.8.15', '3.8.14', '3.8.13', '3.8.12', '3.8.11', '3.8.10', '3.8.9', '3.8.8', '3.8.7', '3.8.6', '3.8.5', '3.8.4', '3.8.3', '3.8.2', '3.8.1', '3.8.0'
];
}
async install(version) {
if (await this.isVersionInstalled(version)) {
console.log(chalk.yellow(`Python ${version} is already installed.`));
return true;
}
console.log(chalk.blue(`Installing Python ${version}...`));
const platform = this.getPlatform();
const versionDir = path.join(this.languageDir, version);
try {
if (platform === 'linux') {
await this.installOnLinux(version, versionDir);
} else if (platform === 'darwin') {
await this.installOnMacOS(version, versionDir);
} else if (platform === 'win32') {
await this.installOnWindows(version, versionDir);
} else {
throw new Error(`Unsupported platform: ${platform}`);
}
console.log(chalk.green(`✓ Python ${version} installed successfully`));
return true;
} catch (error) {
console.error(chalk.red(`Failed to install Python ${version}:`), error.message);
// Clean up partially installed version
if (await fs.pathExists(versionDir)) {
await fs.remove(versionDir);
}
return false;
}
}
async installOnLinux(version, versionDir) {
// For Linux, we'll compile from source
const downloadUrl = `https://www.python.org/ftp/python/${version}/Python-${version}.tgz`;
const downloadDir = path.join(this.languageDir, 'downloads');
const downloadPath = path.join(downloadDir, `Python-${version}.tgz`);
const sourceDir = path.join(downloadDir, `Python-${version}`);
console.log(chalk.yellow(' Downloading Python source...'));
await this.downloadFile(downloadUrl, downloadPath);
console.log(chalk.yellow(' Extracting source...'));
await this.extractTarGz(downloadPath, sourceDir);
console.log(chalk.yellow(' Configuring build...'));
await this.executeCommand(`./configure --prefix="${versionDir}" --enable-optimizations`, {
cwd: sourceDir
});
console.log(chalk.yellow(' Building Python (this may take a while)...'));
await this.executeCommand('make -j$(nproc)', { cwd: sourceDir });
console.log(chalk.yellow(' Installing Python...'));
await this.executeCommand('make altinstall', { cwd: sourceDir });
// Clean up
await fs.remove(downloadDir);
}
async installOnMacOS(version, versionDir) {
// Similar to Linux but may need different configure options
const downloadUrl = `https://www.python.org/ftp/python/${version}/Python-${version}.tgz`;
const downloadDir = path.join(this.languageDir, 'downloads');
const downloadPath = path.join(downloadDir, `Python-${version}.tgz`);
const sourceDir = path.join(downloadDir, `Python-${version}`);
console.log(chalk.yellow(' Downloading Python source...'));
await this.downloadFile(downloadUrl, downloadPath);
console.log(chalk.yellow(' Extracting source...'));
await this.extractTarGz(downloadPath, sourceDir);
console.log(chalk.yellow(' Configuring build...'));
await this.executeCommand(`./configure --prefix="${versionDir}" --enable-optimizations`, {
cwd: sourceDir
});
console.log(chalk.yellow(' Building Python (this may take a while)...'));
await this.executeCommand('make -j$(sysctl -n hw.ncpu)', { cwd: sourceDir });
console.log(chalk.yellow(' Installing Python...'));
await this.executeCommand('make altinstall', { cwd: sourceDir });
// Clean up
await fs.remove(downloadDir);
}
async installOnWindows(version, versionDir) {
// For Windows, we'll download the installer
throw new Error('Windows Python installation not implemented yet. Please install manually.');
}
getBinDir(versionDir) {
return path.join(versionDir, 'bin');
}
async createVirtualEnv(name, pythonVersion) {
if (!pythonVersion) {
pythonVersion = await this.getCurrentVersion();
}
if (!pythonVersion || !await this.isVersionInstalled(pythonVersion)) {
console.log(chalk.red('No Python version specified or installed'));
return false;
}
const versionDir = path.join(this.languageDir, pythonVersion);
const pythonBin = path.join(versionDir, 'bin', `python${pythonVersion.split('.').slice(0, 2).join('.')}`);
const envDir = path.join(CVMUtils.getEnvironmentsDir(), name);
if (await fs.pathExists(envDir)) {
console.log(chalk.yellow(`Virtual environment '${name}' already exists`));
return false;
}
console.log(chalk.blue(`Creating virtual environment '${name}' with Python ${pythonVersion}...`));
try {
await this.executeCommand(`"${pythonBin}" -m venv "${envDir}"`);
console.log(chalk.green(`✓ Virtual environment '${name}' created successfully`));
return true;
} catch (error) {
console.error(chalk.red(`Failed to create virtual environment:`), error.message);
return false;
}
}
}
module.exports = PythonVersionManager;