mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-06-01 14:29:18 +00:00
Revert "enhance: Prebuilt terminal venv found but Python executable missing P…"
This reverts commit 3bb75a3dba.
This commit is contained in:
parent
3bb75a3dba
commit
e8035909ee
4 changed files with 133 additions and 103 deletions
|
|
@ -80,93 +80,67 @@ exports.default = async function afterPack(context) {
|
|||
}
|
||||
}
|
||||
|
||||
// Find prebuilt Python executable in uv_python directory
|
||||
function findPrebuiltPython() {
|
||||
const uvPythonDir = path.join(prebuiltPath, 'uv_python');
|
||||
if (!fs.existsSync(uvPythonDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// UV stores Python in cpython-* subdirectories
|
||||
try {
|
||||
const entries = fs.readdirSync(uvPythonDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && entry.name.startsWith('cpython-')) {
|
||||
const pythonPath = path.join(uvPythonDir, entry.name, 'install', 'bin', 'python');
|
||||
if (fs.existsSync(pythonPath)) {
|
||||
return pythonPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not search for prebuilt Python: ${error.message}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const prebuiltPython = findPrebuiltPython();
|
||||
if (prebuiltPython) {
|
||||
console.log(`Found prebuilt Python: ${prebuiltPython}`);
|
||||
}
|
||||
|
||||
// Clean and fix Python symlinks in a venv bin directory
|
||||
function fixPythonSymlinks(binDir, venvName) {
|
||||
if (!fs.existsSync(binDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean Python symlinks in venv/bin
|
||||
const venvBinDir = path.join(prebuiltPath, 'venv', 'bin');
|
||||
if (fs.existsSync(venvBinDir)) {
|
||||
const pythonNames = ['python', 'python3', 'python3.10', 'python3.11', 'python3.12'];
|
||||
const bundlePath = path.resolve(appPath);
|
||||
|
||||
for (const pythonName of pythonNames) {
|
||||
const pythonSymlink = path.join(binDir, pythonName);
|
||||
const pythonSymlink = path.join(venvBinDir, pythonName);
|
||||
|
||||
try {
|
||||
const stats = fs.lstatSync(pythonSymlink);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const target = fs.readlinkSync(pythonSymlink);
|
||||
const resolvedPath = path.resolve(path.dirname(pythonSymlink), target);
|
||||
if (fs.existsSync(pythonSymlink)) {
|
||||
try {
|
||||
const stats = fs.lstatSync(pythonSymlink);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const target = fs.readlinkSync(pythonSymlink);
|
||||
const resolvedPath = path.resolve(path.dirname(pythonSymlink), target);
|
||||
|
||||
// If symlink points outside bundle or is broken, remove and recreate it
|
||||
if (!resolvedPath.startsWith(bundlePath) || !fs.existsSync(resolvedPath)) {
|
||||
console.log(`Removing invalid ${venvName} ${pythonName} symlink: ${target}`);
|
||||
fs.unlinkSync(pythonSymlink);
|
||||
|
||||
// Recreate symlink pointing to prebuilt Python (only for main 'python')
|
||||
if (prebuiltPython && pythonName === 'python') {
|
||||
const relativePath = path.relative(binDir, prebuiltPython);
|
||||
fs.symlinkSync(relativePath, pythonSymlink);
|
||||
console.log(`Created ${venvName} ${pythonName} symlink -> ${relativePath}`);
|
||||
// If symlink points outside bundle, remove it
|
||||
if (!resolvedPath.startsWith(bundlePath)) {
|
||||
console.log(`Removing invalid ${pythonName} symlink: ${target}`);
|
||||
fs.unlinkSync(pythonSymlink);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Symlink doesn't exist, create it if this is the main python symlink
|
||||
if (error.code === 'ENOENT' && prebuiltPython && pythonName === 'python') {
|
||||
try {
|
||||
const relativePath = path.relative(binDir, prebuiltPython);
|
||||
fs.symlinkSync(relativePath, pythonSymlink);
|
||||
console.log(`Created missing ${venvName} ${pythonName} symlink -> ${relativePath}`);
|
||||
} catch (createError) {
|
||||
console.warn(`Warning: Could not create ${venvName} ${pythonName} symlink: ${createError.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not process ${pythonName} symlink: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix Python symlinks in both venv directories
|
||||
fixPythonSymlinks(path.join(prebuiltPath, 'venv', 'bin'), 'venv');
|
||||
fixPythonSymlinks(path.join(prebuiltPath, 'terminal_venv', 'bin'), 'terminal_venv');
|
||||
// Clean Python symlinks in terminal_venv/bin (same as venv/bin)
|
||||
const terminalVenvBinDir = path.join(prebuiltPath, 'terminal_venv', 'bin');
|
||||
if (fs.existsSync(terminalVenvBinDir)) {
|
||||
const pythonNames = ['python', 'python3', 'python3.10', 'python3.11', 'python3.12'];
|
||||
const bundlePath = path.resolve(appPath);
|
||||
|
||||
// Recursively clean other invalid symlinks (skip already-processed venv bin directories)
|
||||
const processedDirs = new Set([
|
||||
path.join(prebuiltPath, 'venv', 'bin'),
|
||||
path.join(prebuiltPath, 'terminal_venv', 'bin'),
|
||||
]);
|
||||
for (const pythonName of pythonNames) {
|
||||
const pythonSymlink = path.join(terminalVenvBinDir, pythonName);
|
||||
|
||||
if (fs.existsSync(pythonSymlink)) {
|
||||
try {
|
||||
const stats = fs.lstatSync(pythonSymlink);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const target = fs.readlinkSync(pythonSymlink);
|
||||
const resolvedPath = path.resolve(path.dirname(pythonSymlink), target);
|
||||
|
||||
// If symlink points outside bundle, remove it
|
||||
if (!resolvedPath.startsWith(bundlePath)) {
|
||||
console.log(`Removing invalid terminal_venv ${pythonName} symlink: ${target}`);
|
||||
fs.unlinkSync(pythonSymlink);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not process terminal_venv ${pythonName} symlink: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively clean other invalid symlinks
|
||||
function cleanSymlinks(dir, bundleRoot) {
|
||||
if (!fs.existsSync(dir) || processedDirs.has(dir)) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
getTerminalVenvPath,
|
||||
getPrebuiltTerminalVenvPath,
|
||||
getUvEnv,
|
||||
findPrebuiltPythonExecutable,
|
||||
getPrebuiltPythonDir,
|
||||
cleanupOldVenvs,
|
||||
isBinaryExists,
|
||||
runInstallScript,
|
||||
|
|
@ -507,6 +507,59 @@ const runInstall = (extraArgs: string[], version: string) => {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Find Python executable in prebuilt Python directory
|
||||
* UV stores Python installations in directories like: cpython-3.10.19+.../install/bin/python
|
||||
*/
|
||||
function findPrebuiltPythonExecutable(): string | null {
|
||||
const prebuiltPythonDir = getPrebuiltPythonDir();
|
||||
if (!prebuiltPythonDir) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Look for Python executable in the prebuilt directory
|
||||
// UV stores Python in subdirectories like: cpython-3.10.19+.../install/bin/python
|
||||
const possiblePaths: string[] = [];
|
||||
|
||||
// First, try common direct paths
|
||||
possiblePaths.push(
|
||||
path.join(prebuiltPythonDir, 'install', 'bin', 'python'),
|
||||
path.join(prebuiltPythonDir, 'install', 'python.exe'),
|
||||
path.join(prebuiltPythonDir, 'bin', 'python'),
|
||||
path.join(prebuiltPythonDir, 'python.exe'),
|
||||
);
|
||||
|
||||
// Then, search in subdirectories (UV stores Python in versioned directories)
|
||||
try {
|
||||
if (fs.existsSync(prebuiltPythonDir)) {
|
||||
const entries = fs.readdirSync(prebuiltPythonDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && entry.name.startsWith('cpython-')) {
|
||||
const subDir = path.join(prebuiltPythonDir, entry.name);
|
||||
possiblePaths.push(
|
||||
path.join(subDir, 'install', 'bin', 'python'),
|
||||
path.join(subDir, 'install', 'python.exe'),
|
||||
path.join(subDir, 'bin', 'python'),
|
||||
path.join(subDir, 'python.exe'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
log.warn('[DEPS INSTALL] Error searching for prebuilt Python:', error);
|
||||
}
|
||||
|
||||
for (const pythonPath of possiblePaths) {
|
||||
if (fs.existsSync(pythonPath)) {
|
||||
log.info(`[DEPS INSTALL] Found prebuilt Python executable: ${pythonPath}`);
|
||||
return pythonPath;
|
||||
}
|
||||
}
|
||||
|
||||
log.info('[DEPS INSTALL] Prebuilt Python directory found but executable not found, will use UV_PYTHON_INSTALL_DIR');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install terminal base venv with common packages for terminal tasks.
|
||||
* This is a lightweight venv separate from the backend venv.
|
||||
|
|
|
|||
|
|
@ -170,29 +170,39 @@ export function getPrebuiltVenvPath(): string | null {
|
|||
}
|
||||
|
||||
/**
|
||||
* Find Python executable in prebuilt Python directory
|
||||
* Find Python executable in prebuilt Python directory for terminal venv
|
||||
*/
|
||||
export function findPrebuiltPythonExecutable(): string | null {
|
||||
function findPythonForTerminalVenv(): string | null {
|
||||
const prebuiltPythonDir = getPrebuiltPythonDir();
|
||||
if (!prebuiltPythonDir) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
const pythonName = isWindows ? 'python.exe' : 'python';
|
||||
const binPath = isWindows ? '' : path.join('install', 'bin');
|
||||
// Look for Python executable in the prebuilt directory
|
||||
// UV stores Python in subdirectories like: cpython-3.10.19+.../install/bin/python
|
||||
const possiblePaths: string[] = [];
|
||||
|
||||
// UV stores Python in cpython-* subdirectories
|
||||
// First, try common direct paths
|
||||
possiblePaths.push(
|
||||
path.join(prebuiltPythonDir, 'install', 'bin', 'python'),
|
||||
path.join(prebuiltPythonDir, 'install', 'python.exe'),
|
||||
path.join(prebuiltPythonDir, 'bin', 'python'),
|
||||
path.join(prebuiltPythonDir, 'python.exe'),
|
||||
);
|
||||
|
||||
// Then, search in subdirectories (UV stores Python in versioned directories)
|
||||
try {
|
||||
const entries = fs.readdirSync(prebuiltPythonDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && entry.name.startsWith('cpython-')) {
|
||||
const pythonPath = isWindows
|
||||
? path.join(prebuiltPythonDir, entry.name, 'install', pythonName)
|
||||
: path.join(prebuiltPythonDir, entry.name, binPath, pythonName);
|
||||
if (fs.existsSync(pythonPath)) {
|
||||
log.info(`[PROCESS] Found prebuilt Python executable: ${pythonPath}`);
|
||||
return pythonPath;
|
||||
if (fs.existsSync(prebuiltPythonDir)) {
|
||||
const entries = fs.readdirSync(prebuiltPythonDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && entry.name.startsWith('cpython-')) {
|
||||
const subDir = path.join(prebuiltPythonDir, entry.name);
|
||||
possiblePaths.push(
|
||||
path.join(subDir, 'install', 'bin', 'python'),
|
||||
path.join(subDir, 'install', 'python.exe'),
|
||||
path.join(subDir, 'bin', 'python'),
|
||||
path.join(subDir, 'python.exe'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -200,7 +210,12 @@ export function findPrebuiltPythonExecutable(): string | null {
|
|||
log.warn('[PROCESS] Error searching for prebuilt Python:', error);
|
||||
}
|
||||
|
||||
log.info('[PROCESS] Prebuilt Python directory found but executable not found');
|
||||
for (const pythonPath of possiblePaths) {
|
||||
if (fs.existsSync(pythonPath)) {
|
||||
return pythonPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -226,13 +241,13 @@ export function getPrebuiltTerminalVenvPath(): string | null {
|
|||
log.info(`Using prebuilt terminal venv: ${prebuiltTerminalVenvPath}`);
|
||||
return prebuiltTerminalVenvPath;
|
||||
} else {
|
||||
// Try to fix the missing Python executable by creating a symlink to
|
||||
// prebuilt Python
|
||||
// Try to fix the missing Python executable by creating a symlink to prebuilt Python
|
||||
log.warn(
|
||||
`Prebuilt terminal venv found but Python executable missing at: ${pythonExePath}. ` +
|
||||
`Attempting to fix...`
|
||||
);
|
||||
const prebuiltPython = findPrebuiltPythonExecutable();
|
||||
|
||||
const prebuiltPython = findPythonForTerminalVenv();
|
||||
if (prebuiltPython && fs.existsSync(prebuiltPython)) {
|
||||
try {
|
||||
const binDir = isWindows
|
||||
|
|
|
|||
|
|
@ -426,14 +426,10 @@ export function createProcessUtilsMock() {
|
|||
getBinaryPath: vi.fn(),
|
||||
getCachePath: vi.fn(),
|
||||
getVenvPath: vi.fn(),
|
||||
getTerminalVenvPath: vi.fn(),
|
||||
getPrebuiltTerminalVenvPath: vi.fn(),
|
||||
findPrebuiltPythonExecutable: vi.fn(),
|
||||
getVenvsBaseDir: vi.fn(),
|
||||
cleanupOldVenvs: vi.fn(),
|
||||
isBinaryExists: vi.fn(),
|
||||
getUvEnv: vi.fn(),
|
||||
TERMINAL_BASE_PACKAGES: ['pandas', 'numpy', 'matplotlib', 'requests', 'openpyxl', 'beautifulsoup4', 'pillow'],
|
||||
mockState: {} as MockEnvironmentState,
|
||||
|
||||
setup: (mockState: MockEnvironmentState) => {
|
||||
|
|
@ -486,14 +482,6 @@ export function createProcessUtilsMock() {
|
|||
utilsMock.getVenvPath.mockImplementation((version: string) => {
|
||||
return `${mockState.system.homedir}/.eigent/venvs/backend-${version}`
|
||||
})
|
||||
|
||||
utilsMock.getTerminalVenvPath.mockImplementation((version: string) => {
|
||||
return `${mockState.system.homedir}/.eigent/venvs/terminal-${version}`
|
||||
})
|
||||
|
||||
utilsMock.getPrebuiltTerminalVenvPath.mockReturnValue(null)
|
||||
|
||||
utilsMock.findPrebuiltPythonExecutable.mockReturnValue(null)
|
||||
|
||||
utilsMock.getVenvsBaseDir.mockReturnValue(
|
||||
`${mockState.system.homedir}/.eigent/venvs`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue