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:
Navid
2026-01-02 01:08:11 +03:00
committed by GitHub
parent 4da8cd66c6
commit 8d58dd6fe4
3 changed files with 88 additions and 40 deletions
+6 -27
View File
@@ -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]'
);
}
/**
+5 -13
View File
@@ -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;
}