* fix(python-bundling): verify critical packages exist, not just marker file (#416) When checking if bundled Python packages are already set up, the code only verified that the .bundled marker file existed. This meant that corrupted caches with missing packages would be incorrectly accepted, causing "ModuleNotFoundError: No module named 'claude_agent_sdk'" on Linux AppImage and Windows builds. Changes: - download-python.cjs: After verifying .bundled marker exists, also check that claude_agent_sdk and dotenv directories are present. If missing, force reinstall packages. - python-env-manager.ts: Changed package detection from OR (either package exists) to AND (both must exist). Added diagnostic logging to help identify which packages are missing. This fix ensures: 1. Build-time verification catches corrupted caches 2. Runtime detection won't falsely report bundled packages available 3. Better logging for debugging package issues Fixes #416 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address PR feedback - improve package validation - Refactor python-env-manager.ts to use loop pattern (matches download-python.cjs) - Add deeper validation by checking __init__.py exists (not just directory) - Include error details in catch block for better debugging - Add cross-reference comments noting list sync requirements Co-authored-by: CodeRabbit <coderabbit@users.noreply.github.com> Co-authored-by: Gemini Code Assist <gemini-code-assist@google.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: remove accidentally committed config.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: add /config.json to .gitignore Prevents accidental commits of worktree metadata files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: add post-install package verification and documentation - Add post-install verification to ensure packages exist before creating marker - Add flow control comment explaining fall-through behavior - Document PEP 420 namespace package assumption in validation code Addresses Auto Claude review findings NEW-003, NEW-004, NEW-005 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Alex <63423455+AlexMadera@users.noreply.github.com>
This commit is contained in:
@@ -62,6 +62,7 @@ lerna-debug.log*
|
||||
.auto-claude-status
|
||||
.claude_settings.json
|
||||
.update-metadata.json
|
||||
/config.json
|
||||
|
||||
# ===========================
|
||||
# Python (apps/backend)
|
||||
|
||||
@@ -702,9 +702,32 @@ async function downloadPython(targetPlatform, targetArch, options = {}) {
|
||||
try {
|
||||
const version = verifyPythonBinary(pythonBin);
|
||||
console.log(`[download-python] Verified: ${version}`);
|
||||
return { success: true, pythonPath: pythonBin, sitePackagesPath: sitePackagesDir };
|
||||
} catch {
|
||||
console.log(`[download-python] Existing installation is broken, re-downloading...`);
|
||||
|
||||
// Verify critical packages exist (fixes GitHub issue #416)
|
||||
// Without this check, corrupted caches with missing packages would be accepted
|
||||
// Note: Same list exists in python-env-manager.ts - keep them in sync
|
||||
// This validation assumes traditional Python packages with __init__.py (not PEP 420 namespace packages)
|
||||
const criticalPackages = ['claude_agent_sdk', 'dotenv'];
|
||||
const missingPackages = criticalPackages.filter(pkg => {
|
||||
const pkgPath = path.join(sitePackagesDir, pkg);
|
||||
// Check both directory and __init__.py for more robust validation
|
||||
const initFile = path.join(pkgPath, '__init__.py');
|
||||
return !fs.existsSync(pkgPath) || !fs.existsSync(initFile);
|
||||
});
|
||||
|
||||
if (missingPackages.length > 0) {
|
||||
console.log(`[download-python] Critical packages missing or incomplete: ${missingPackages.join(', ')}`);
|
||||
console.log(`[download-python] Reinstalling packages...`);
|
||||
// Remove site-packages to force reinstall, keep Python binary
|
||||
// Flow continues below to re-install packages (skipPackages check at line 794)
|
||||
fs.rmSync(sitePackagesDir, { recursive: true, force: true });
|
||||
} else {
|
||||
console.log(`[download-python] All critical packages verified`);
|
||||
return { success: true, pythonPath: pythonBin, sitePackagesPath: sitePackagesDir };
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||
console.log(`[download-python] Existing installation is broken: ${errorMsg}`);
|
||||
fs.rmSync(platformDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
@@ -784,6 +807,22 @@ async function downloadPython(targetPlatform, targetArch, options = {}) {
|
||||
// Install packages
|
||||
installPackages(pythonBin, requirementsPath, sitePackagesDir);
|
||||
|
||||
// Verify critical packages were installed before creating marker (fixes #416)
|
||||
// Note: Same list exists in python-env-manager.ts - keep them in sync
|
||||
// This validation assumes traditional Python packages with __init__.py (not PEP 420 namespace packages)
|
||||
const criticalPackages = ['claude_agent_sdk', 'dotenv'];
|
||||
const postInstallMissing = criticalPackages.filter(pkg => {
|
||||
const pkgPath = path.join(sitePackagesDir, pkg);
|
||||
const initFile = path.join(pkgPath, '__init__.py');
|
||||
return !fs.existsSync(pkgPath) || !fs.existsSync(initFile);
|
||||
});
|
||||
|
||||
if (postInstallMissing.length > 0) {
|
||||
throw new Error(`Package installation failed - missing critical packages: ${postInstallMissing.join(', ')}`);
|
||||
}
|
||||
|
||||
console.log(`[download-python] All critical packages verified after installation`);
|
||||
|
||||
// Create marker file to indicate successful bundling
|
||||
fs.writeFileSync(packagesMarker, JSON.stringify({
|
||||
bundledAt: new Date().toISOString(),
|
||||
|
||||
@@ -122,19 +122,36 @@ export class PythonEnvManager extends EventEmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for the marker file that indicates successful bundling
|
||||
const markerPath = path.join(sitePackagesPath, '.bundled');
|
||||
if (existsSync(markerPath)) {
|
||||
console.log(`[PythonEnvManager] Found bundle marker, using bundled packages`);
|
||||
return true;
|
||||
// Critical packages that must exist for proper functionality
|
||||
// This fixes GitHub issue #416 where marker exists but packages are missing
|
||||
// Note: Same list exists in download-python.cjs - keep them in sync
|
||||
// This validation assumes traditional Python packages with __init__.py (not PEP 420 namespace packages)
|
||||
const criticalPackages = ['claude_agent_sdk', 'dotenv'];
|
||||
|
||||
// Check each package exists with valid structure (directory + __init__.py)
|
||||
const missingPackages = criticalPackages.filter((pkg) => {
|
||||
const pkgPath = path.join(sitePackagesPath, pkg);
|
||||
const initPath = path.join(pkgPath, '__init__.py');
|
||||
// Package is valid if directory and __init__.py both exist
|
||||
return !existsSync(pkgPath) || !existsSync(initPath);
|
||||
});
|
||||
|
||||
// Log missing packages for debugging
|
||||
for (const pkg of missingPackages) {
|
||||
console.log(
|
||||
`[PythonEnvManager] Missing critical package: ${pkg} at ${path.join(sitePackagesPath, pkg)}`
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback: check if key packages exist
|
||||
// This handles cases where the marker might be missing but packages are there
|
||||
const claudeSdkPath = path.join(sitePackagesPath, 'claude_agent_sdk');
|
||||
const dotenvPath = path.join(sitePackagesPath, 'dotenv');
|
||||
if (existsSync(claudeSdkPath) || existsSync(dotenvPath)) {
|
||||
console.log(`[PythonEnvManager] Found key packages, using bundled packages`);
|
||||
// All packages must exist - don't rely solely on marker file
|
||||
if (missingPackages.length === 0) {
|
||||
// Also check marker for logging purposes
|
||||
const markerPath = path.join(sitePackagesPath, '.bundled');
|
||||
if (existsSync(markerPath)) {
|
||||
console.log(`[PythonEnvManager] Found bundle marker and all critical packages`);
|
||||
} else {
|
||||
console.log(`[PythonEnvManager] Found critical packages (marker missing)`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user