Reimplement Python package installation.
This commit is contained in:
parent
0e8e1a6e2c
commit
e6b48a9681
60
src/util/python_installer/app_install.ts
Normal file
60
src/util/python_installer/app_install.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Install devchat
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { logger } from "../logger";
|
||||||
|
import { installConda } from "./conda_install";
|
||||||
|
import { installPackage } from "./package_install";
|
||||||
|
import { installPython } from "./python_install";
|
||||||
|
|
||||||
|
// step 1. install conda
|
||||||
|
// step 2. create env with python 3.11.4
|
||||||
|
// step 3. install devchat in the env
|
||||||
|
|
||||||
|
export async function appInstall(pkgName: string, pythonVersion: string) : Promise<string> {
|
||||||
|
// install conda
|
||||||
|
logger.channel()?.info('Install conda ...')
|
||||||
|
const condaCommand = await installConda();
|
||||||
|
if (!condaCommand) {
|
||||||
|
logger.channel()?.error('Install conda failed');
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// create env with specify python
|
||||||
|
logger.channel()?.info('Create env ...');
|
||||||
|
let pythonCommand = '';
|
||||||
|
// try 3 times
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
pythonCommand = await installPython(condaCommand, pkgName, pythonVersion);
|
||||||
|
if (pythonCommand) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
logger.channel()?.info(`Create env failed, try again: ${i + 1}`);
|
||||||
|
}
|
||||||
|
if (!pythonCommand) {
|
||||||
|
logger.channel()?.error('Create env failed');
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
logger.channel()?.info(`Create env success: ${pythonCommand}`);
|
||||||
|
|
||||||
|
// install devchat in the env
|
||||||
|
logger.channel()?.info('Install python packages ...')
|
||||||
|
let isInstalled = false;
|
||||||
|
// try 3 times
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
isInstalled = await installPackage(pythonCommand, pkgName);
|
||||||
|
if (isInstalled) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
logger.channel()?.info(`Install packages failed, try again: ${i + 1}`);
|
||||||
|
}
|
||||||
|
if (!isInstalled) {
|
||||||
|
logger.channel()?.error('Install packages failed');
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return pythonCommand;
|
||||||
|
}
|
180
src/util/python_installer/conda_install.ts
Normal file
180
src/util/python_installer/conda_install.ts
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
Install conda command
|
||||||
|
Install file from https://repo.anaconda.com/miniconda/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { logger } from "../logger";
|
||||||
|
import { getCondaDownloadUrl } from "./conda_url";
|
||||||
|
import { downloadFile } from "./https_download";
|
||||||
|
|
||||||
|
import { exec, spawn } from 'child_process';
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
|
||||||
|
// Check whether conda has installed before installing conda.
|
||||||
|
// If "conda -V" runs ok, then conda has installed.
|
||||||
|
// If ~/.devchat/conda/bin/conda exists, then conda has installed.
|
||||||
|
|
||||||
|
// is "conda -V" ok? then find conda command
|
||||||
|
// is ~/.devchat/conda/bin/conda exists? then return ~/.devchat/conda/bin/conda
|
||||||
|
// find conda command by: with different os use diffenc command: which conda | where conda
|
||||||
|
async function isCondaInstalled(): Promise<string> {
|
||||||
|
// whether conda -V runs ok
|
||||||
|
const condaVersion = await runCommand('conda2 -V');
|
||||||
|
if (condaVersion) {
|
||||||
|
// find conda command by: with different os use diffenc command: which conda | where conda
|
||||||
|
const os = process.platform;
|
||||||
|
const command = os === 'win32' ? 'where conda' : 'which conda';
|
||||||
|
const condaCommand = await runCommand(command);
|
||||||
|
if (condaCommand) {
|
||||||
|
const condaCommandLines = condaCommand.split('\n');
|
||||||
|
return condaCommandLines[0].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// whether ~/.devchat/conda/bin/conda exists
|
||||||
|
const os = process.platform;
|
||||||
|
const userHome = os === 'win32' ? fs.realpathSync(process.env.USERPROFILE || '') : process.env.HOME;
|
||||||
|
const pathToConda = `${userHome}/.devchat/conda`;
|
||||||
|
const condaPath = os === 'win32' ? `${pathToConda}/Scripts/conda.exe` : `${pathToConda}/bin/conda`;
|
||||||
|
logger.channel()?.info(`checking conda path: ${condaPath}`);
|
||||||
|
const isCondaPathExists = fs.existsSync(condaPath);
|
||||||
|
if (isCondaPathExists) {
|
||||||
|
return condaPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.channel()?.info(`conda path: ${condaPath} not exists`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCommand(command: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(command, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
resolve('');
|
||||||
|
} else {
|
||||||
|
resolve(stdout.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPathExists(path: string): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(`test -e ${path}`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
resolve(false);
|
||||||
|
} else {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// install file is an exe file or sh file
|
||||||
|
// according to different os, use different command to install conda, Installing in silent mode
|
||||||
|
// install conda to USER_HOME/.devchat/conda
|
||||||
|
// return: conda command path
|
||||||
|
async function installCondaByInstallFile(installFileUrl: string) : Promise<string> {
|
||||||
|
// Determine the operating system
|
||||||
|
const os = process.platform;
|
||||||
|
|
||||||
|
// Set the installation directory for conda
|
||||||
|
const userHome = os === 'win32' ? fs.realpathSync(process.env.USERPROFILE || '') : process.env.HOME;
|
||||||
|
const pathToConda = `${userHome}/.devchat/conda`;
|
||||||
|
|
||||||
|
// Define the command to install conda based on the operating system
|
||||||
|
let command = '';
|
||||||
|
if (os === 'win32') {
|
||||||
|
const winPathToConda = pathToConda.replace(/\//g, '\\');
|
||||||
|
command = `start /wait ${installFileUrl} /InstallationType=JustMe /AddToPath=0 /RegisterPython=0 /S /D=${winPathToConda}`;
|
||||||
|
} else if (os === 'linux') {
|
||||||
|
command = `bash ${installFileUrl} -b -p ${pathToConda}`;
|
||||||
|
} else if (os === 'darwin') {
|
||||||
|
command = `bash ${installFileUrl} -b -p ${pathToConda}`;
|
||||||
|
} else {
|
||||||
|
throw new Error('Unsupported operating system');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the command to install conda
|
||||||
|
logger.channel()?.info(`install conda command: ${command}`);
|
||||||
|
try {
|
||||||
|
await executeCommand(command);
|
||||||
|
|
||||||
|
// Return the path to the conda command
|
||||||
|
let condaCommandPath = '';
|
||||||
|
if (os === 'win32') {
|
||||||
|
condaCommandPath = `${pathToConda}\\Scripts\\conda.exe`;
|
||||||
|
} else {
|
||||||
|
condaCommandPath = `${pathToConda}/bin/conda`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return condaCommandPath;
|
||||||
|
} catch(error) {
|
||||||
|
logger.channel()?.error(`install conda failed: ${error}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to execute a command
|
||||||
|
function executeCommand(command: string): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
exec(command, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
logger.channel()?.error(`exec error: ${error}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
if (stderr) {
|
||||||
|
logger.channel()?.error(`stderr: ${error}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
}
|
||||||
|
if (stdout) {
|
||||||
|
logger.channel()?.info(`${stdout}`);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function installConda() : Promise<string> {
|
||||||
|
// step 1. check whether conda has installed
|
||||||
|
// step 2. download install file
|
||||||
|
// step 3. install conda by install file
|
||||||
|
|
||||||
|
const condaCommand = await isCondaInstalled();
|
||||||
|
if (condaCommand) {
|
||||||
|
logger.channel()?.info(`conda has installed: ${condaCommand}`);
|
||||||
|
return condaCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadInstallFile = getCondaDownloadUrl();
|
||||||
|
if (!downloadInstallFile) {
|
||||||
|
logger.channel()?.error(`get conda download url failed`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.channel()?.info(`conda download url: ${downloadInstallFile}`);
|
||||||
|
let installFileLocal = '';
|
||||||
|
// try 3 times
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
installFileLocal = await downloadFile(downloadInstallFile);
|
||||||
|
if (installFileLocal) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
logger.channel()?.info(`download conda install file failed, try again ...`);
|
||||||
|
}
|
||||||
|
if (!installFileLocal) {
|
||||||
|
logger.channel()?.error(`download conda install file failed`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.channel()?.info(`conda install file: ${installFileLocal}`);
|
||||||
|
const installedConda = await installCondaByInstallFile(installFileLocal);
|
||||||
|
return installedConda;
|
||||||
|
}
|
54
src/util/python_installer/conda_url.ts
Normal file
54
src/util/python_installer/conda_url.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Get conda download url
|
||||||
|
*/
|
||||||
|
|
||||||
|
import os from 'os';
|
||||||
|
import { logger } from '../logger';
|
||||||
|
|
||||||
|
function getDownloadFileName(): string {
|
||||||
|
const platform = os.platform();
|
||||||
|
const arch = os.arch();
|
||||||
|
logger.channel()?.info(`Platform: ${platform}, Arch: ${arch}`);
|
||||||
|
|
||||||
|
if (platform === "win32") {
|
||||||
|
if (arch === "x64") {
|
||||||
|
return "Miniconda3-latest-Windows-x86_64.exe";
|
||||||
|
} else if (arch === "ia32") {
|
||||||
|
return "Miniconda3-latest-Windows-x86.exe";
|
||||||
|
} else {
|
||||||
|
return "Miniconda3-latest-Windows-x86_64.exe";
|
||||||
|
}
|
||||||
|
} else if (platform === "darwin") {
|
||||||
|
if (arch === "x64") {
|
||||||
|
return "Miniconda3-latest-MacOSX-x86_64.sh";
|
||||||
|
} else if (arch === "arm64") {
|
||||||
|
return "Miniconda3-latest-MacOSX-arm64.sh";
|
||||||
|
} else if (arch === "x86") {
|
||||||
|
return "Miniconda3-latest-MacOSX-x86.sh";
|
||||||
|
} else {
|
||||||
|
return "Miniconda3-latest-MacOSX-arm64.sh";
|
||||||
|
}
|
||||||
|
} else if (platform === "linux") {
|
||||||
|
if (arch === "x64") {
|
||||||
|
return "Miniconda3-latest-Linux-x86_64.sh";
|
||||||
|
} else if (arch === "s390x") {
|
||||||
|
return "Miniconda3-latest-Linux-s390x.sh";
|
||||||
|
} else if (arch === "ppc64le") {
|
||||||
|
return "Miniconda3-latest-Linux-ppc64le.sh";
|
||||||
|
} else if (arch === "aarch64") {
|
||||||
|
return "Miniconda3-latest-Linux-aarch64.sh";
|
||||||
|
} else if (arch === "x86") {
|
||||||
|
return "Miniconda3-latest-Linux-x86.sh";
|
||||||
|
} else if (arch === "armv7l") {
|
||||||
|
return "Miniconda3-latest-Linux-armv7l.sh";
|
||||||
|
} else {
|
||||||
|
return "Miniconda3-latest-Linux-x86_64.sh";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCondaDownloadUrl(): string {
|
||||||
|
return 'https://repo.anaconda.com/miniconda/' + getDownloadFileName();
|
||||||
|
}
|
47
src/util/python_installer/https_download.ts
Normal file
47
src/util/python_installer/https_download.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as https from 'https';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { logger } from '../logger';
|
||||||
|
|
||||||
|
// download url to tmp directory
|
||||||
|
// return: local file path or empty string
|
||||||
|
export async function downloadFile(url: string): Promise<string> {
|
||||||
|
const os = process.platform;
|
||||||
|
const tempDir = os === 'win32' ? fs.realpathSync(process.env.USERPROFILE || '') : process.env.HOME;
|
||||||
|
|
||||||
|
const fileName = path.basename(url); // 从 URL 中提取文件名称
|
||||||
|
const destination = path.join(tempDir!, fileName); // 构建文件路径
|
||||||
|
|
||||||
|
const file = fs.createWriteStream(destination);
|
||||||
|
let downloadedBytes = 0;
|
||||||
|
let totalBytes = 0;
|
||||||
|
let lastProgress = 0;
|
||||||
|
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
https.get(url, (response) => {
|
||||||
|
totalBytes = parseInt(response.headers['content-length'] || '0', 10);
|
||||||
|
|
||||||
|
response.on('data', (chunk) => {
|
||||||
|
downloadedBytes += chunk.length;
|
||||||
|
const progress = (downloadedBytes / totalBytes) * 100;
|
||||||
|
|
||||||
|
if (progress - lastProgress >= 3) {
|
||||||
|
logger.channel()?.info(`Downloaded ${downloadedBytes} bytes (${progress.toFixed(2)}%)`);
|
||||||
|
lastProgress = progress;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
response.pipe(file);
|
||||||
|
|
||||||
|
file.on('finish', () => {
|
||||||
|
file.close();
|
||||||
|
resolve(destination); // 修改为传递下载的文件路径
|
||||||
|
});
|
||||||
|
}).on('error', (error) => {
|
||||||
|
fs.unlink(destination, () => {
|
||||||
|
resolve(''); // 下载失败时返回空字符串
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
29
src/util/python_installer/install_askcode.ts
Normal file
29
src/util/python_installer/install_askcode.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
Install DevChat with python=3.11.4
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { logger } from "../logger";
|
||||||
|
import { appInstall } from "./app_install"
|
||||||
|
|
||||||
|
|
||||||
|
// python version: 3.11.4
|
||||||
|
// pkg name: devchat
|
||||||
|
// return: path to devchat, devchat is located in the same directory as python
|
||||||
|
export async function installAskCode(): Promise<string> {
|
||||||
|
try {
|
||||||
|
logger.channel()?.info(`start installing AskCode with python=3.11.4 ...`);
|
||||||
|
const pythonCommand = await appInstall('devchat-ask', '3.11.4');
|
||||||
|
if (!pythonCommand) {
|
||||||
|
logger.channel()?.error(`failed to install devchat-ask with python=3.11.4`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.channel()?.info(`installed devchat-ask with python=3.11.4 at ${pythonCommand}`);
|
||||||
|
return pythonCommand;
|
||||||
|
} catch (error) {
|
||||||
|
logger.channel()?.error(`${error}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
55
src/util/python_installer/install_devchat.ts
Normal file
55
src/util/python_installer/install_devchat.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
Install DevChat with python=3.11.4
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { logger } from "../logger";
|
||||||
|
import { appInstall } from "./app_install"
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
|
||||||
|
let isDevChatInstalling: boolean | undefined = undefined;
|
||||||
|
|
||||||
|
export function isDevchatInstalling(): boolean {
|
||||||
|
if (isDevChatInstalling === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// python version: 3.11.4
|
||||||
|
// pkg name: devchat
|
||||||
|
// return: path to devchat, devchat is located in the same directory as python
|
||||||
|
export async function installDevchat(): Promise<string> {
|
||||||
|
try {
|
||||||
|
logger.channel()?.info(`start installing devchat with python=3.11.4 ...`);
|
||||||
|
isDevChatInstalling = true;
|
||||||
|
const pythonCommand = await appInstall('devchat', '3.11.4');
|
||||||
|
if (!pythonCommand) {
|
||||||
|
logger.channel()?.error(`failed to install devchat with python=3.11.4`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
isDevChatInstalling = false;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the directory of pythonCommand
|
||||||
|
const pythonDirectory = path.dirname(pythonCommand);
|
||||||
|
|
||||||
|
// Get the path of devchat
|
||||||
|
let devchatPath = path.join(pythonDirectory, 'devchat');
|
||||||
|
|
||||||
|
// Check if devchatPath exists, if not, try with 'Scripts' subdirectory
|
||||||
|
if (!fs.existsSync(devchatPath)) {
|
||||||
|
devchatPath = path.join(pythonDirectory, 'Scripts', 'devchat');
|
||||||
|
}
|
||||||
|
|
||||||
|
isDevChatInstalling = false;
|
||||||
|
return devchatPath;
|
||||||
|
} catch (error) {
|
||||||
|
logger.channel()?.error(`${error}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
isDevChatInstalling = false;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
41
src/util/python_installer/package_install.ts
Normal file
41
src/util/python_installer/package_install.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
Install specific version of package. e.g. devchat
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
import { logger } from '../logger';
|
||||||
|
|
||||||
|
// install specific version of package
|
||||||
|
// pythonCommand -m install pkgName
|
||||||
|
// if install success, return true
|
||||||
|
// else return false
|
||||||
|
export async function installPackage(pythonCommand: string, pkgName: string) : Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const cmd = pythonCommand;
|
||||||
|
const args = ['-m', 'pip', 'install', pkgName];
|
||||||
|
const child = spawn(cmd, args);
|
||||||
|
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
logger.channel()?.info(`${data}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on('data', (data) => {
|
||||||
|
console.error(`stderr: ${data}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (error) => {
|
||||||
|
logger.channel()?.error(`exec error: ${error}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
resolve(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
resolve(false);
|
||||||
|
} else {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
83
src/util/python_installer/python_install.ts
Normal file
83
src/util/python_installer/python_install.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { exec, spawn } from 'child_process';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { logger } from '../logger';
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Check if the environment already exists
|
||||||
|
export async function checkEnvExists(condaCommandPath: string, envName: string): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
const condaCommand = path.resolve(condaCommandPath);
|
||||||
|
const command = `${condaCommand} env list`;
|
||||||
|
exec(command, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
logger.channel()?.error(`Error checking environments`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
reject(false);
|
||||||
|
} else {
|
||||||
|
const envs = stdout.split('\n').map(line => line.split(' ')[0]);
|
||||||
|
resolve(envs.includes(envName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install env with specific python version
|
||||||
|
// conda create -n {envName} python={pythonVersion} --yes
|
||||||
|
// return: python in env path
|
||||||
|
export async function installPython(condaCommandPath: string, envName: string, pythonVersion: string): Promise<string> {
|
||||||
|
const envExists = await checkEnvExists(condaCommandPath, envName);
|
||||||
|
|
||||||
|
const condaCommand = path.resolve(condaCommandPath);
|
||||||
|
const envPath = path.resolve(condaCommand, '..', '..', 'envs', envName);
|
||||||
|
let pythonPath;
|
||||||
|
let pythonPath2;
|
||||||
|
if (os.platform() === 'win32') {
|
||||||
|
pythonPath = path.join(envPath, 'Scripts', 'python.exe');
|
||||||
|
pythonPath2 = path.join(envPath, 'python.exe');
|
||||||
|
} else {
|
||||||
|
pythonPath = path.join(envPath, 'bin', 'python');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (envExists) {
|
||||||
|
if (fs.existsSync(pythonPath)) {
|
||||||
|
return pythonPath;
|
||||||
|
} else if (pythonPath2 && fs.existsSync(pythonPath2)) {
|
||||||
|
return pythonPath2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
const cmd = condaCommand;
|
||||||
|
const args = ['create', '-n', envName, `python=${pythonVersion}`, '--yes'];
|
||||||
|
const child = spawn(cmd, args);
|
||||||
|
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
logger.channel()?.info(`${data}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on('data', (data) => {
|
||||||
|
console.error(`stderr: ${data}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (error) => {
|
||||||
|
logger.channel()?.error(`Error installing python ${pythonVersion} in env ${envName}`);
|
||||||
|
logger.channel()?.show();
|
||||||
|
reject('');
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
reject(new Error(`Command exited with code ${code}`));
|
||||||
|
} else {
|
||||||
|
if (fs.existsSync(pythonPath)) {
|
||||||
|
resolve(pythonPath);
|
||||||
|
} else if (pythonPath2 && fs.existsSync(pythonPath2)) {
|
||||||
|
resolve(pythonPath2);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`No Python found`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
12
src/util/python_installer/readme.md
Normal file
12
src/util/python_installer/readme.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Why conda?
|
||||||
|
Devchat-vscode support custom extension. Different extension may need different python version.
|
||||||
|
|
||||||
|
pyenv is also a python version manager, but it always download source of python, then build it to binary.
|
||||||
|
|
||||||
|
conda is a package manager, it can download binary of python, and install packages.
|
||||||
|
|
||||||
|
# Where to install?
|
||||||
|
Install conda to $USER_PROFILE/.devchat
|
||||||
|
|
||||||
|
Python will install inside $USER_PROFILE/.devchat/conda.
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user