mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-19 16:31:36 +00:00
add uv_python
This commit is contained in:
parent
ed96c29b2e
commit
886f96753d
4 changed files with 103 additions and 8 deletions
|
|
@ -29,7 +29,13 @@
|
||||||
{
|
{
|
||||||
"from": "resources/prebuilt",
|
"from": "resources/prebuilt",
|
||||||
"to": "prebuilt",
|
"to": "prebuilt",
|
||||||
"filter": ["**/*", "!cache/**/*", "!**/.npm-cache/**/*"]
|
"filter": [
|
||||||
|
"**/*",
|
||||||
|
"!cache/**/*",
|
||||||
|
"!**/.npm-cache/**/*",
|
||||||
|
"!uv_python/**/*.pyc",
|
||||||
|
"!uv_python/**/__pycache__"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"protocols": [
|
"protocols": [
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { getBackendPath, getBinaryPath, getCachePath, getVenvPath, getUvEnv, isBinaryExists, runInstallScript, killProcessByName } from "./utils/process";
|
import { getBackendPath, getBinaryPath, getCachePath, getVenvPath, getUvEnv, isBinaryExists, runInstallScript, killProcessByName, getPrebuiltPythonDir, getPrebuiltVenvPath } from "./utils/process";
|
||||||
import { spawn, exec } from 'child_process'
|
import { spawn, exec } from 'child_process'
|
||||||
import log from 'electron-log'
|
import log from 'electron-log'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
@ -225,21 +225,33 @@ export async function startBackend(setPort?: (port: number) => void): Promise<an
|
||||||
log.warn(`Failed to remove lock file: ${e}`);
|
log.warn(`Failed to remove lock file: ${e}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup corrupted python cache
|
// Cleanup corrupted python cache ONLY if it's not the bundled Python
|
||||||
|
// If we have bundled Python, we want to keep it and reuse it
|
||||||
|
const prebuiltPythonDir = getPrebuiltPythonDir();
|
||||||
try {
|
try {
|
||||||
const pythonCacheDir = getCachePath('uv_python');
|
const pythonCacheDir = getCachePath('uv_python');
|
||||||
if (fs.existsSync(pythonCacheDir)) {
|
// Only remove if it's NOT the prebuilt Python directory
|
||||||
|
if (fs.existsSync(pythonCacheDir) && pythonCacheDir !== prebuiltPythonDir) {
|
||||||
log.info(`Removing potentially corrupted Python cache: ${pythonCacheDir}`);
|
log.info(`Removing potentially corrupted Python cache: ${pythonCacheDir}`);
|
||||||
fs.rmSync(pythonCacheDir, { recursive: true, force: true });
|
fs.rmSync(pythonCacheDir, { recursive: true, force: true });
|
||||||
|
} else if (prebuiltPythonDir) {
|
||||||
|
log.info(`Preserving bundled Python at: ${prebuiltPythonDir}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warn(`Failed to remove Python cache: ${e}`);
|
log.warn(`Failed to remove Python cache: ${e}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup corrupted venv (pyvenv.cfg may reference non-existent Python version)
|
// Cleanup corrupted venv (pyvenv.cfg may reference non-existent Python version)
|
||||||
|
// This is especially important for prebuilt venvs with hardcoded paths from CI
|
||||||
|
const prebuiltVenvPath = getPrebuiltVenvPath();
|
||||||
try {
|
try {
|
||||||
|
// If the broken venv is the prebuilt venv, we need to remove it
|
||||||
|
// and let UV recreate it from the bundled Python
|
||||||
if (fs.existsSync(venvPath)) {
|
if (fs.existsSync(venvPath)) {
|
||||||
log.info(`Removing potentially corrupted venv: ${venvPath}`);
|
log.info(`Removing potentially corrupted venv: ${venvPath}`);
|
||||||
|
if (venvPath === prebuiltVenvPath) {
|
||||||
|
log.info(`This is the prebuilt venv with hardcoded paths - will recreate from bundled Python`);
|
||||||
|
}
|
||||||
fs.rmSync(venvPath, { recursive: true, force: true });
|
fs.rmSync(venvPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,23 @@ export async function isBinaryExists(name: string): Promise<boolean> {
|
||||||
return fs.existsSync(cmd);
|
return fs.existsSync(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get path to prebuilt Python installation (if available in packaged app)
|
||||||
|
*/
|
||||||
|
export function getPrebuiltPythonDir(): string | null {
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prebuiltPythonDir = path.join(process.resourcesPath, 'prebuilt', 'uv_python');
|
||||||
|
if (fs.existsSync(prebuiltPythonDir)) {
|
||||||
|
log.info(`Using prebuilt Python: ${prebuiltPythonDir}`);
|
||||||
|
return prebuiltPythonDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get unified UV environment variables for consistent Python environment management.
|
* Get unified UV environment variables for consistent Python environment management.
|
||||||
* This ensures both installation and runtime use the same paths.
|
* This ensures both installation and runtime use the same paths.
|
||||||
|
|
@ -292,8 +309,12 @@ export async function isBinaryExists(name: string): Promise<boolean> {
|
||||||
* @returns Environment variables for UV commands
|
* @returns Environment variables for UV commands
|
||||||
*/
|
*/
|
||||||
export function getUvEnv(version: string): Record<string, string> {
|
export function getUvEnv(version: string): Record<string, string> {
|
||||||
|
// Use prebuilt Python if available (packaged app)
|
||||||
|
const prebuiltPython = getPrebuiltPythonDir();
|
||||||
|
const pythonInstallDir = prebuiltPython || getCachePath('uv_python');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
UV_PYTHON_INSTALL_DIR: getCachePath('uv_python'),
|
UV_PYTHON_INSTALL_DIR: pythonInstallDir,
|
||||||
UV_TOOL_DIR: getCachePath('uv_tool'),
|
UV_TOOL_DIR: getCachePath('uv_tool'),
|
||||||
UV_PROJECT_ENVIRONMENT: getVenvPath(version),
|
UV_PROJECT_ENVIRONMENT: getVenvPath(version),
|
||||||
UV_HTTP_TIMEOUT: '300',
|
UV_HTTP_TIMEOUT: '300',
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,9 @@ const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
const projectRoot = path.resolve(__dirname, '..');
|
const projectRoot = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
const BIN_DIR = path.join(projectRoot, 'resources', 'prebuilt', 'bin');
|
const PREBUILT_DIR = path.join(projectRoot, 'resources', 'prebuilt');
|
||||||
const VENV_DIR = path.join(projectRoot, 'resources', 'prebuilt', 'venv');
|
const BIN_DIR = path.join(PREBUILT_DIR, 'bin');
|
||||||
|
const VENV_DIR = path.join(PREBUILT_DIR, 'venv');
|
||||||
const BACKEND_DIR = path.join(projectRoot, 'backend');
|
const BACKEND_DIR = path.join(projectRoot, 'backend');
|
||||||
|
|
||||||
console.log('🚀 Starting pre-installation of dependencies...');
|
console.log('🚀 Starting pre-installation of dependencies...');
|
||||||
|
|
@ -197,6 +198,32 @@ async function downloadFileWithValidation(urlsToTry, dest, validateFn, fileType
|
||||||
throw new Error(`Failed to download ${fileType} from all sources`);
|
throw new Error(`Failed to download ${fileType} from all sources`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively copy directory
|
||||||
|
*/
|
||||||
|
function copyDirRecursiveSync(src, dest) {
|
||||||
|
if (!fs.existsSync(src)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create destination directory
|
||||||
|
fs.mkdirSync(dest, { recursive: true });
|
||||||
|
|
||||||
|
// Get all files and directories
|
||||||
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const srcPath = path.join(src, entry.name);
|
||||||
|
const destPath = path.join(dest, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
copyDirRecursiveSync(srcPath, destPath);
|
||||||
|
} else {
|
||||||
|
fs.copyFileSync(srcPath, destPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Bun download URL list
|
* Get Bun download URL list
|
||||||
*/
|
*/
|
||||||
|
|
@ -625,8 +652,10 @@ async function installPythonDeps(uvPath) {
|
||||||
console.log('⚠️ Python install command failed, continuing with sync (Python may already be installed)...');
|
console.log('⚠️ Python install command failed, continuing with sync (Python may already be installed)...');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use --python-preference only-managed to ensure uv uses its own managed Python
|
||||||
|
// This makes the venv more portable
|
||||||
execSync(
|
execSync(
|
||||||
`"${uvPath}" sync --no-dev --cache-dir "${cacheDir}"`,
|
`"${uvPath}" sync --no-dev --cache-dir "${cacheDir}" --python-preference only-managed`,
|
||||||
{ cwd: BACKEND_DIR, env: env, stdio: 'inherit' }
|
{ cwd: BACKEND_DIR, env: env, stdio: 'inherit' }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -644,6 +673,33 @@ async function installPythonDeps(uvPath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✅ Python executable verified: ${pythonExePath}`);
|
console.log(`✅ Python executable verified: ${pythonExePath}`);
|
||||||
|
|
||||||
|
// Bundle the actual Python installation from UV cache into prebuilt
|
||||||
|
console.log('📦 Bundling Python installation...');
|
||||||
|
try {
|
||||||
|
const uvPythonDir = pythonCacheDir;
|
||||||
|
const prebuiltPythonDir = path.join(PREBUILT_DIR, 'uv_python');
|
||||||
|
|
||||||
|
if (fs.existsSync(uvPythonDir)) {
|
||||||
|
console.log(` Copying from: ${uvPythonDir}`);
|
||||||
|
console.log(` Copying to: ${prebuiltPythonDir}`);
|
||||||
|
|
||||||
|
// Remove existing python dir if it exists
|
||||||
|
if (fs.existsSync(prebuiltPythonDir)) {
|
||||||
|
fs.rmSync(prebuiltPythonDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the Python installation
|
||||||
|
copyDirRecursiveSync(uvPythonDir, prebuiltPythonDir);
|
||||||
|
console.log('✅ Python installation bundled');
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ UV Python cache not found, venv may not be portable');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`⚠️ Failed to bundle Python: ${error.message}`);
|
||||||
|
console.log(' The app may fail to start without internet connection');
|
||||||
|
}
|
||||||
|
|
||||||
console.log('✅ Python dependencies installed');
|
console.log('✅ Python dependencies installed');
|
||||||
|
|
||||||
console.log('📝 Compiling babel...');
|
console.log('📝 Compiling babel...');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue