mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-28 17:55:55 +00:00
python to 3.10
This commit is contained in:
parent
433973ece5
commit
6f58bb5076
5 changed files with 309 additions and 42 deletions
2
.github/workflows/build-view.yml
vendored
2
.github/workflows/build-view.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install Python Dependencies
|
||||
run: |
|
||||
|
|
|
|||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
|
@ -44,7 +44,7 @@ jobs:
|
|||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.11"
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install Python Dependencies
|
||||
run: |
|
||||
|
|
@ -158,4 +158,4 @@ jobs:
|
|||
release/mac-arm64/*
|
||||
release/win-x64/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ exports.default = async function afterPack(context) {
|
|||
: ['python', 'python3', 'python3.10', 'python3.11', 'python3.12'];
|
||||
const bundlePath = path.resolve(appPath);
|
||||
|
||||
// First, clean invalid symlinks
|
||||
for (const pythonName of pythonNames) {
|
||||
const pythonPath = path.join(venvBinDir, pythonName);
|
||||
|
||||
|
|
@ -113,8 +114,8 @@ exports.default = async function afterPack(context) {
|
|||
const target = fs.readlinkSync(pythonPath);
|
||||
const resolvedPath = path.resolve(path.dirname(pythonPath), target);
|
||||
|
||||
// If symlink points outside bundle, remove it
|
||||
if (!resolvedPath.startsWith(bundlePath)) {
|
||||
// If symlink points outside bundle or target doesn't exist, remove it
|
||||
if (!resolvedPath.startsWith(bundlePath) || !fs.existsSync(resolvedPath)) {
|
||||
console.log(`Removing invalid ${pythonName} symlink: ${target}`);
|
||||
fs.unlinkSync(pythonPath);
|
||||
}
|
||||
|
|
@ -125,6 +126,80 @@ exports.default = async function afterPack(context) {
|
|||
}
|
||||
}
|
||||
|
||||
// Fix Python symlinks by finding actual Python in cache
|
||||
const pythonCacheDir = path.join(prebuiltPath, 'cache', 'uv_python');
|
||||
if (fs.existsSync(pythonCacheDir)) {
|
||||
try {
|
||||
const entries = fs.readdirSync(pythonCacheDir);
|
||||
const pythonDirs = entries
|
||||
.filter(name => name.startsWith('cpython-'))
|
||||
.map(name => {
|
||||
const binDir = path.join(pythonCacheDir, name, 'bin');
|
||||
const pythonExe = isWindows
|
||||
? path.join(binDir, 'python.exe')
|
||||
: path.join(binDir, 'python3.10');
|
||||
return { name, binDir, pythonExe };
|
||||
})
|
||||
.filter(({ pythonExe }) => fs.existsSync(pythonExe));
|
||||
|
||||
if (pythonDirs.length > 0) {
|
||||
// Use the first available Python (usually the latest)
|
||||
const { pythonExe, binDir } = pythonDirs[0];
|
||||
const mainPythonName = isWindows ? 'python.exe' : 'python';
|
||||
const mainPythonPath = path.join(venvBinDir, mainPythonName);
|
||||
|
||||
// Check if main Python symlink exists and is valid
|
||||
let needsFix = true;
|
||||
if (fs.existsSync(mainPythonPath)) {
|
||||
try {
|
||||
const stats = fs.lstatSync(mainPythonPath);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const target = fs.readlinkSync(mainPythonPath);
|
||||
const resolvedPath = path.resolve(path.dirname(mainPythonPath), target);
|
||||
if (fs.existsSync(resolvedPath) && resolvedPath === pythonExe) {
|
||||
needsFix = false;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Error reading symlink, needs fix
|
||||
}
|
||||
}
|
||||
|
||||
if (needsFix) {
|
||||
// Remove old symlink if exists
|
||||
if (fs.existsSync(mainPythonPath)) {
|
||||
fs.unlinkSync(mainPythonPath);
|
||||
}
|
||||
|
||||
// Create new symlink using relative path
|
||||
const relativePath = path.relative(venvBinDir, pythonExe);
|
||||
fs.symlinkSync(relativePath, mainPythonPath);
|
||||
console.log(`✅ Fixed Python symlink: ${mainPythonPath} -> ${relativePath}`);
|
||||
|
||||
// Also fix python3 and python3.10 symlinks on Unix
|
||||
if (!isWindows) {
|
||||
const python3Path = path.join(venvBinDir, 'python3');
|
||||
const python310Path = path.join(venvBinDir, 'python3.10');
|
||||
|
||||
if (fs.existsSync(python3Path)) {
|
||||
fs.unlinkSync(python3Path);
|
||||
}
|
||||
fs.symlinkSync('python', python3Path);
|
||||
|
||||
if (fs.existsSync(python310Path)) {
|
||||
fs.unlinkSync(python310Path);
|
||||
}
|
||||
fs.symlinkSync('python', python310Path);
|
||||
}
|
||||
} else {
|
||||
console.log(`✅ Python symlink is valid: ${mainPythonPath}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not fix Python symlinks: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// On Windows, verify Python executable exists and is accessible
|
||||
if (isWindows) {
|
||||
const pythonExe = path.join(venvBinDir, 'python.exe');
|
||||
|
|
@ -134,6 +209,31 @@ exports.default = async function afterPack(context) {
|
|||
} else {
|
||||
console.log(`✅ Python executable verified: ${pythonExe}`);
|
||||
}
|
||||
} else {
|
||||
// On Unix, verify Python executable
|
||||
const pythonExe = path.join(venvBinDir, 'python');
|
||||
if (!fs.existsSync(pythonExe)) {
|
||||
console.warn(`⚠️ Warning: Python executable not found at: ${pythonExe}`);
|
||||
console.warn(` This may cause runtime errors. Ensure Python cache (uv_python) is included in the build.`);
|
||||
} else {
|
||||
// Verify symlink is valid
|
||||
try {
|
||||
const stats = fs.lstatSync(pythonExe);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const target = fs.readlinkSync(pythonExe);
|
||||
const resolvedPath = path.resolve(path.dirname(pythonExe), target);
|
||||
if (fs.existsSync(resolvedPath)) {
|
||||
console.log(`✅ Python executable verified: ${pythonExe} -> ${resolvedPath}`);
|
||||
} else {
|
||||
console.warn(`⚠️ Warning: Python symlink target does not exist: ${target}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`✅ Python executable verified: ${pythonExe}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Warning: Could not verify Python executable: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ export function getCachePath(folder: string): string {
|
|||
|
||||
/**
|
||||
* Get path to prebuilt venv (if available in packaged app)
|
||||
* Attempts to fix Python symlinks if they are broken
|
||||
*/
|
||||
export function getPrebuiltVenvPath(): string | null {
|
||||
if (!app.isPackaged) {
|
||||
|
|
@ -151,18 +152,95 @@ export function getPrebuiltVenvPath(): string | null {
|
|||
if (fs.existsSync(pyvenvCfg)) {
|
||||
// Verify Python executable exists (Windows: Scripts/python.exe, Unix: bin/python)
|
||||
const isWindows = process.platform === 'win32';
|
||||
const venvBinDir = isWindows
|
||||
? path.join(prebuiltVenvPath, 'Scripts')
|
||||
: path.join(prebuiltVenvPath, 'bin');
|
||||
const pythonExePath = isWindows
|
||||
? path.join(prebuiltVenvPath, 'Scripts', 'python.exe')
|
||||
: path.join(prebuiltVenvPath, 'bin', 'python');
|
||||
? path.join(venvBinDir, 'python.exe')
|
||||
: path.join(venvBinDir, 'python');
|
||||
|
||||
// Check if Python executable exists and is valid
|
||||
let pythonValid = false;
|
||||
if (fs.existsSync(pythonExePath)) {
|
||||
try {
|
||||
const stats = fs.lstatSync(pythonExePath);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const target = fs.readlinkSync(pythonExePath);
|
||||
const resolvedPath = path.resolve(path.dirname(pythonExePath), target);
|
||||
pythonValid = fs.existsSync(resolvedPath);
|
||||
} else {
|
||||
pythonValid = true; // Regular file, assume valid
|
||||
}
|
||||
} catch (error) {
|
||||
pythonValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (pythonValid) {
|
||||
log.info(`Using prebuilt venv: ${prebuiltVenvPath}`);
|
||||
return prebuiltVenvPath;
|
||||
} else {
|
||||
// Try to fix the Python symlink before falling back
|
||||
log.warn(
|
||||
`Prebuilt venv found but Python executable missing at: ${pythonExePath}. ` +
|
||||
`Falling back to user venv.`
|
||||
`Prebuilt venv found but Python executable missing or invalid at: ${pythonExePath}. ` +
|
||||
`Attempting to fix...`
|
||||
);
|
||||
|
||||
const pythonCacheDir = path.join(process.resourcesPath, 'prebuilt', 'cache', 'uv_python');
|
||||
if (fs.existsSync(pythonCacheDir)) {
|
||||
try {
|
||||
const entries = fs.readdirSync(pythonCacheDir);
|
||||
const pythonDirs = entries
|
||||
.filter(name => name.startsWith('cpython-3.10'))
|
||||
.map(name => {
|
||||
const binDir = path.join(pythonCacheDir, name, 'bin');
|
||||
const pythonExe = isWindows
|
||||
? path.join(binDir, 'python.exe')
|
||||
: path.join(binDir, 'python3.10');
|
||||
return { name, binDir, pythonExe };
|
||||
})
|
||||
.filter(({ pythonExe }) => fs.existsSync(pythonExe));
|
||||
|
||||
if (pythonDirs.length > 0) {
|
||||
const { pythonExe } = pythonDirs[0];
|
||||
const relativePath = path.relative(venvBinDir, pythonExe);
|
||||
|
||||
// Remove old symlink if exists
|
||||
if (fs.existsSync(pythonExePath)) {
|
||||
fs.unlinkSync(pythonExePath);
|
||||
}
|
||||
|
||||
// Create new symlink
|
||||
fs.symlinkSync(relativePath, pythonExePath);
|
||||
log.info(`Fixed Python symlink: ${pythonExePath} -> ${relativePath}`);
|
||||
|
||||
// On Unix, also fix python3 and python3.10
|
||||
if (!isWindows) {
|
||||
const python3Path = path.join(venvBinDir, 'python3');
|
||||
const python310Path = path.join(venvBinDir, 'python3.10');
|
||||
|
||||
if (fs.existsSync(python3Path)) {
|
||||
fs.unlinkSync(python3Path);
|
||||
}
|
||||
fs.symlinkSync('python', python3Path);
|
||||
|
||||
if (fs.existsSync(python310Path)) {
|
||||
fs.unlinkSync(python310Path);
|
||||
}
|
||||
fs.symlinkSync('python', python310Path);
|
||||
}
|
||||
|
||||
log.info(`Using prebuilt venv (after fix): ${prebuiltVenvPath}`);
|
||||
return prebuiltVenvPath;
|
||||
} else {
|
||||
log.warn('No valid Python executable found in cache, falling back to user venv.');
|
||||
}
|
||||
} catch (error: any) {
|
||||
log.warn(`Failed to fix Python symlink: ${error?.message || String(error)}. Falling back to user venv.`);
|
||||
}
|
||||
} else {
|
||||
log.warn('Python cache directory not found, falling back to user venv.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@ function isValidZip(filePath) {
|
|||
try {
|
||||
const buffer = fs.readFileSync(filePath);
|
||||
return buffer.length > 4 &&
|
||||
buffer[0] === 0x50 &&
|
||||
buffer[1] === 0x4B &&
|
||||
buffer[2] === 0x03 &&
|
||||
buffer[3] === 0x04;
|
||||
buffer[0] === 0x50 &&
|
||||
buffer[1] === 0x4B &&
|
||||
buffer[2] === 0x03 &&
|
||||
buffer[3] === 0x04;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -52,8 +52,8 @@ function isValidTarGz(filePath) {
|
|||
try {
|
||||
const buffer = fs.readFileSync(filePath);
|
||||
return buffer.length > 2 &&
|
||||
buffer[0] === 0x1F &&
|
||||
buffer[1] === 0x8B;
|
||||
buffer[0] === 0x1F &&
|
||||
buffer[1] === 0x8B;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ async function downloadFileWithValidation(urlsToTry, dest, validateFn, fileType
|
|||
}, (response) => {
|
||||
// Handle redirects (301, 302, 307, 308)
|
||||
if (response.statusCode === 301 || response.statusCode === 302 ||
|
||||
response.statusCode === 307 || response.statusCode === 308) {
|
||||
response.statusCode === 307 || response.statusCode === 308) {
|
||||
redirectCount++;
|
||||
if (redirectCount > maxRedirects) {
|
||||
reject(new Error(`Too many redirects (${redirectCount})`));
|
||||
|
|
@ -300,21 +300,21 @@ async function installUv() {
|
|||
// Find installed uv
|
||||
const possiblePaths = process.platform === 'win32'
|
||||
? [
|
||||
path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Python', 'Python311', 'Scripts', 'uv.exe'),
|
||||
path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Python', 'Python312', 'Scripts', 'uv.exe'),
|
||||
path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Python', 'Python313', 'Scripts', 'uv.exe'),
|
||||
path.join(os.homedir(), '.local', 'bin', 'uv.exe'),
|
||||
'C:\\Python311\\Scripts\\uv.exe',
|
||||
'C:\\Python312\\Scripts\\uv.exe',
|
||||
'C:\\Python313\\Scripts\\uv.exe',
|
||||
]
|
||||
path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Python', 'Python311', 'Scripts', 'uv.exe'),
|
||||
path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Python', 'Python312', 'Scripts', 'uv.exe'),
|
||||
path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'Python', 'Python313', 'Scripts', 'uv.exe'),
|
||||
path.join(os.homedir(), '.local', 'bin', 'uv.exe'),
|
||||
'C:\\Python311\\Scripts\\uv.exe',
|
||||
'C:\\Python312\\Scripts\\uv.exe',
|
||||
'C:\\Python313\\Scripts\\uv.exe',
|
||||
]
|
||||
: [
|
||||
path.join(os.homedir(), '.local', 'bin', 'uv'),
|
||||
path.join(os.homedir(), 'Library', 'Python', '3.11', 'bin', 'uv'),
|
||||
path.join(os.homedir(), 'Library', 'Python', '3.12', 'bin', 'uv'),
|
||||
path.join(os.homedir(), 'Library', 'Python', '3.13', 'bin', 'uv'),
|
||||
'/usr/local/bin/uv',
|
||||
];
|
||||
path.join(os.homedir(), '.local', 'bin', 'uv'),
|
||||
path.join(os.homedir(), 'Library', 'Python', '3.11', 'bin', 'uv'),
|
||||
path.join(os.homedir(), 'Library', 'Python', '3.12', 'bin', 'uv'),
|
||||
path.join(os.homedir(), 'Library', 'Python', '3.13', 'bin', 'uv'),
|
||||
'/usr/local/bin/uv',
|
||||
];
|
||||
|
||||
let foundUvPath = null;
|
||||
try {
|
||||
|
|
@ -592,15 +592,28 @@ async function installPythonDeps(uvPath) {
|
|||
}
|
||||
|
||||
// Ensure Python is installed before syncing
|
||||
// This is critical for Windows where Python might not be in the venv
|
||||
console.log('🐍 Ensuring Python is installed...');
|
||||
// Use fixed Python version to ensure consistency across builds
|
||||
// This prevents version mismatch issues when packaging
|
||||
console.log('🐍 Ensuring Python is installed (using fixed version 3.10.15)...');
|
||||
try {
|
||||
// Use fixed minor version to ensure consistency
|
||||
// This prevents issues where different builds might get different patch versions
|
||||
execSync(
|
||||
`"${uvPath}" python install 3.10`,
|
||||
`"${uvPath}" python install 3.10.15`,
|
||||
{ cwd: BACKEND_DIR, env: env, stdio: 'inherit' }
|
||||
);
|
||||
console.log('✅ Python 3.10.15 installed');
|
||||
} catch (error) {
|
||||
console.log('⚠️ Python install command failed, continuing with sync (Python may already be installed)...');
|
||||
console.log('⚠️ Python 3.10.15 install failed, trying 3.10 (latest)...');
|
||||
try {
|
||||
execSync(
|
||||
`"${uvPath}" python install 3.10`,
|
||||
{ cwd: BACKEND_DIR, env: env, stdio: 'inherit' }
|
||||
);
|
||||
console.log('✅ Python 3.10 (latest) installed');
|
||||
} catch (error2) {
|
||||
console.log('⚠️ Python install command failed, continuing with sync (Python may already be installed)...');
|
||||
}
|
||||
}
|
||||
|
||||
execSync(
|
||||
|
|
@ -608,20 +621,96 @@ async function installPythonDeps(uvPath) {
|
|||
{ cwd: BACKEND_DIR, env: env, stdio: 'inherit' }
|
||||
);
|
||||
|
||||
// Verify Python executable exists in the virtual environment
|
||||
// Verify and fix Python executable symlinks in the virtual environment
|
||||
const isWindows = process.platform === 'win32';
|
||||
const venvBinDir = isWindows ? path.join(venvPath, 'Scripts') : path.join(venvPath, 'bin');
|
||||
const pythonExePath = isWindows
|
||||
? path.join(venvPath, 'Scripts', 'python.exe')
|
||||
: path.join(venvPath, 'bin', 'python');
|
||||
? path.join(venvBinDir, 'python.exe')
|
||||
: path.join(venvBinDir, 'python');
|
||||
|
||||
if (!fs.existsSync(pythonExePath)) {
|
||||
throw new Error(
|
||||
`Python executable not found in virtual environment at: ${pythonExePath}\n` +
|
||||
`Virtual environment may be corrupted. Please ensure uv sync completed successfully.`
|
||||
);
|
||||
// Try to find Python in cache and create symlink
|
||||
console.log('⚠️ Python executable not found, attempting to fix symlink...');
|
||||
|
||||
if (fs.existsSync(pythonCacheDir)) {
|
||||
try {
|
||||
const entries = fs.readdirSync(pythonCacheDir);
|
||||
const pythonDirs = entries
|
||||
.filter(name => name.startsWith('cpython-3.10'))
|
||||
.map(name => {
|
||||
const binDir = path.join(pythonCacheDir, name, 'bin');
|
||||
const pythonExe = isWindows
|
||||
? path.join(binDir, 'python.exe')
|
||||
: path.join(binDir, 'python3.10');
|
||||
return { name, binDir, pythonExe };
|
||||
})
|
||||
.filter(({ pythonExe }) => fs.existsSync(pythonExe));
|
||||
|
||||
if (pythonDirs.length > 0) {
|
||||
const { pythonExe, binDir } = pythonDirs[0];
|
||||
console.log(`Found Python at: ${pythonExe}`);
|
||||
|
||||
// Create symlink using relative path
|
||||
const relativePath = path.relative(venvBinDir, pythonExe);
|
||||
|
||||
if (fs.existsSync(pythonExePath)) {
|
||||
fs.unlinkSync(pythonExePath);
|
||||
}
|
||||
fs.symlinkSync(relativePath, pythonExePath);
|
||||
console.log(`✅ Created Python symlink: ${pythonExePath} -> ${relativePath}`);
|
||||
|
||||
// On Unix, also create python3 and python3.10 symlinks
|
||||
if (!isWindows) {
|
||||
const python3Path = path.join(venvBinDir, 'python3');
|
||||
const python310Path = path.join(venvBinDir, 'python3.10');
|
||||
|
||||
if (fs.existsSync(python3Path)) {
|
||||
fs.unlinkSync(python3Path);
|
||||
}
|
||||
fs.symlinkSync('python', python3Path);
|
||||
|
||||
if (fs.existsSync(python310Path)) {
|
||||
fs.unlinkSync(python310Path);
|
||||
}
|
||||
fs.symlinkSync('python', python310Path);
|
||||
}
|
||||
} else {
|
||||
throw new Error('No Python executable found in cache');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Python executable not found in virtual environment at: ${pythonExePath}\n` +
|
||||
`Virtual environment may be corrupted. Please ensure uv sync completed successfully.\n` +
|
||||
`Error: ${error.message}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`Python executable not found in virtual environment at: ${pythonExePath}\n` +
|
||||
`Virtual environment may be corrupted. Please ensure uv sync completed successfully.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the symlink is valid
|
||||
try {
|
||||
const stats = fs.lstatSync(pythonExePath);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const target = fs.readlinkSync(pythonExePath);
|
||||
const resolvedPath = path.resolve(path.dirname(pythonExePath), target);
|
||||
if (fs.existsSync(resolvedPath)) {
|
||||
console.log(`✅ Python executable verified: ${pythonExePath} -> ${resolvedPath}`);
|
||||
} else {
|
||||
console.warn(`⚠️ Warning: Python symlink target does not exist: ${target}`);
|
||||
console.warn(` This may cause issues during packaging.`);
|
||||
}
|
||||
} else {
|
||||
console.log(`✅ Python executable verified: ${pythonExePath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Warning: Could not verify Python executable: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log(`✅ Python executable verified: ${pythonExePath}`);
|
||||
console.log('✅ Python dependencies installed');
|
||||
|
||||
console.log('📝 Compiling babel...');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue