fix: prefer versioned Homebrew Python over system python3 (#494)
* Enhance Python detection to find versioned Homebrew installations Fixes issue where users with Python 3.9.6 at /usr/bin/python3 would get "Auto Claude requires Python 3.10 or higher" error even when they had newer Python versions installed via Homebrew. Changes: - Updated findHomebrewPython() to check for versioned Python installations - Now searches for python3.13, python3.12, python3.11, python3.10 in addition to generic python3 - Validates each found Python to ensure it meets version requirements - Checks both Apple Silicon (/opt/homebrew/bin) and Intel Mac (/usr/local/bin) locations This ensures the app automatically finds and uses the latest compatible Python version instead of falling back to the potentially outdated system Python. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Update apps/frontend/src/main/python-detector.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Address PR review findings for Python detection enhancement Addresses all review findings from Auto Claude PR Review: 1. [HIGH] Align version ordering between python-detector.ts and cli-tool-manager.ts - Both now use consistent order: versioned first (3.13→3.10), then generic python3 - This ensures different parts of the app use the same Python version - Added validation in cli-tool-manager.ts (was missing before) 2. [MEDIUM] Add try/catch around validatePythonVersion calls - Wrapped validation in try/catch to handle timeouts and permission errors - Follows same pattern as findPythonCommand() - Ensures graceful fallback to next candidate on validation failure 3. [LOW] Add debug logging for Python detection - Added console.log for successful detection with version info - Added console.warn for rejected candidates with reason - Added logging when no valid Python found - Improves troubleshooting of user Python detection issues 4. [LOW] Document maintenance requirement for version list - Added JSDoc note about updating list for new Python releases - Added TODO comment for Python 3.14+ updates - Applied to both files for consistency Additional improvements: - Fixed bug in cli-tool-manager.ts that returned first found Python without validation - Both detection systems now validate Python version requirements (3.10+) - Consistent logging format between both detection systems 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Add Python 3.14 support to version detection Python 3.14 was released, so adding it to the detection lists: - Updated pythonNames arrays in both python-detector.ts and cli-tool-manager.ts - Added python3.14 to SAFE_PYTHON_COMMANDS set - Updated JSDoc comments to reflect Python 3.14 support - Removed TODO about Python 3.14 (now implemented) This ensures the app can detect and use Python 3.14 installations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Refactor: Extract shared Homebrew Python detection logic Eliminated code duplication by extracting shared Python detection logic into a reusable utility module. Changes: - Created apps/frontend/src/main/utils/homebrew-python.ts - Exported findHomebrewPython() utility function - Accepts validation function and log prefix as parameters - Contains all shared detection logic (version list, validation, logging) - Updated python-detector.ts - Removed duplicate findHomebrewPython() implementation (45 lines) - Now imports and delegates to shared utility - Maintains identical behavior and error semantics - Updated cli-tool-manager.ts - Removed duplicate findHomebrewPython() implementation (52 lines) - Now imports and delegates to shared utility - Maintains identical behavior and error semantics Benefits: - Single source of truth for Homebrew Python detection - Easier to maintain (update version list in one place) - Consistent behavior across the application - Reduced code duplication (~90 lines eliminated) The refactored code maintains 100% backward compatibility with identical return values, logging behavior, and error handling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Andy <119136210+AndyMik90@users.noreply.github.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import os from 'os';
|
||||
import { app } from 'electron';
|
||||
import { findExecutable } from './env-utils';
|
||||
import type { ToolDetectionResult } from '../shared/types';
|
||||
import { findHomebrewPython as findHomebrewPythonUtil } from './utils/homebrew-python';
|
||||
|
||||
/**
|
||||
* Supported CLI tools managed by this system
|
||||
@@ -731,37 +732,15 @@ class CLIToolManager {
|
||||
|
||||
/**
|
||||
* Find Homebrew Python on macOS
|
||||
*
|
||||
* Checks both Apple Silicon and Intel Homebrew locations.
|
||||
* Searches for python3, python3.13, python3.12, etc. in order.
|
||||
* Delegates to shared utility function.
|
||||
*
|
||||
* @returns Path to Homebrew Python or null if not found
|
||||
*/
|
||||
private findHomebrewPython(): string | null {
|
||||
const homebrewDirs = [
|
||||
'/opt/homebrew/bin', // Apple Silicon
|
||||
'/usr/local/bin', // Intel Mac
|
||||
];
|
||||
|
||||
// Check for generic python3 first, then specific versions (newest first)
|
||||
const pythonNames = [
|
||||
'python3',
|
||||
'python3.13',
|
||||
'python3.12',
|
||||
'python3.11',
|
||||
'python3.10',
|
||||
];
|
||||
|
||||
for (const dir of homebrewDirs) {
|
||||
for (const name of pythonNames) {
|
||||
const pythonPath = path.join(dir, name);
|
||||
if (existsSync(pythonPath)) {
|
||||
return pythonPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return findHomebrewPythonUtil(
|
||||
(pythonPath) => this.validatePython(pythonPath),
|
||||
'[CLI Tools]'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ import { execSync, execFileSync } from 'child_process';
|
||||
import { existsSync, accessSync, constants } from 'fs';
|
||||
import path from 'path';
|
||||
import { app } from 'electron';
|
||||
import { findHomebrewPython as findHomebrewPythonUtil } from './utils/homebrew-python';
|
||||
|
||||
/**
|
||||
* Get the path to the bundled Python executable.
|
||||
@@ -34,23 +35,12 @@ export function getBundledPythonPath(): string | null {
|
||||
|
||||
/**
|
||||
* Find the first existing Homebrew Python installation.
|
||||
* Checks common Homebrew paths for Python 3.
|
||||
* Delegates to shared utility function.
|
||||
*
|
||||
* @returns The path to Homebrew Python, or null if not found
|
||||
*/
|
||||
function findHomebrewPython(): string | null {
|
||||
const homebrewPaths = [
|
||||
'/opt/homebrew/bin/python3', // Apple Silicon (M1/M2/M3)
|
||||
'/usr/local/bin/python3' // Intel Mac
|
||||
];
|
||||
|
||||
for (const pythonPath of homebrewPaths) {
|
||||
if (existsSync(pythonPath)) {
|
||||
return pythonPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return findHomebrewPythonUtil(validatePythonVersion, '[Python]');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,6 +298,7 @@ const ALLOWED_PATH_PATTERNS: RegExp[] = [
|
||||
/**
|
||||
* Known safe Python commands (not full paths).
|
||||
* These are resolved by the shell/OS and are safe.
|
||||
* Note: Update this list when new Python versions are released.
|
||||
*/
|
||||
const SAFE_PYTHON_COMMANDS = new Set([
|
||||
'python',
|
||||
@@ -316,6 +307,7 @@ const SAFE_PYTHON_COMMANDS = new Set([
|
||||
'python3.11',
|
||||
'python3.12',
|
||||
'python3.13',
|
||||
'python3.14',
|
||||
'py',
|
||||
'py -3',
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Homebrew Python Detection Utility
|
||||
*
|
||||
* Shared logic for finding Python installations in Homebrew directories.
|
||||
* Used by both python-detector.ts and cli-tool-manager.ts to ensure
|
||||
* consistent Python detection across the application.
|
||||
*/
|
||||
|
||||
import { existsSync } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* Validation result for a Python installation.
|
||||
*/
|
||||
export interface PythonValidation {
|
||||
valid: boolean;
|
||||
version?: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first valid Homebrew Python installation.
|
||||
* Checks common Homebrew paths for Python 3, including versioned installations.
|
||||
* Prioritizes newer Python versions (3.14, 3.13, 3.12, 3.11, 3.10).
|
||||
*
|
||||
* Note: This list should be updated when new Python versions are released.
|
||||
* Check for specific versions first to ensure we find the latest available version.
|
||||
*
|
||||
* @param validateFn - Function to validate a Python path and return validation result
|
||||
* @param logPrefix - Prefix for log messages (e.g., '[Python]', '[CLI Tools]')
|
||||
* @returns The path to Homebrew Python, or null if not found
|
||||
*/
|
||||
export function findHomebrewPython(
|
||||
validateFn: (pythonPath: string) => PythonValidation,
|
||||
logPrefix: string
|
||||
): string | null {
|
||||
const homebrewDirs = [
|
||||
'/opt/homebrew/bin', // Apple Silicon (M1/M2/M3)
|
||||
'/usr/local/bin' // Intel Mac
|
||||
];
|
||||
|
||||
// Check for specific Python versions first (newest to oldest), then fall back to generic python3.
|
||||
// This ensures we find the latest available version that meets our requirements.
|
||||
const pythonNames = [
|
||||
'python3.14',
|
||||
'python3.13',
|
||||
'python3.12',
|
||||
'python3.11',
|
||||
'python3.10',
|
||||
'python3',
|
||||
];
|
||||
|
||||
for (const dir of homebrewDirs) {
|
||||
for (const name of pythonNames) {
|
||||
const pythonPath = path.join(dir, name);
|
||||
if (existsSync(pythonPath)) {
|
||||
try {
|
||||
// Validate that this Python meets version requirements
|
||||
const validation = validateFn(pythonPath);
|
||||
if (validation.valid) {
|
||||
console.log(`${logPrefix} Found valid Homebrew Python: ${pythonPath} (${validation.version})`);
|
||||
return pythonPath;
|
||||
} else {
|
||||
console.warn(`${logPrefix} ${pythonPath} rejected: ${validation.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
// Version check failed (e.g., timeout, permission issue), try next candidate
|
||||
console.warn(`${logPrefix} Failed to validate ${pythonPath}: ${error}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`${logPrefix} No valid Homebrew Python found in ${homebrewDirs.join(', ')}`);
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user