mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
fix: Autodetect pwsh.exe & DLL path (Win/non-WSL) (#11044)
This commit is contained in:
@@ -5,6 +5,8 @@ way to manage PowerShell processes compared to using temporary script files.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
@@ -45,39 +47,121 @@ except Exception as coreclr_ex:
|
|||||||
logger.error(f'{error_msg} Details: {details}')
|
logger.error(f'{error_msg} Details: {details}')
|
||||||
raise DotNetMissingError(error_msg, details)
|
raise DotNetMissingError(error_msg, details)
|
||||||
|
|
||||||
|
|
||||||
|
def find_latest_pwsh_sdk_path(
|
||||||
|
executable_name='pwsh.exe',
|
||||||
|
dll_name='System.Management.Automation.dll',
|
||||||
|
min_version=(7, 0, 0),
|
||||||
|
env_var='PWSH_DIR',
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Checks PWSH_DIR environment variable first to find pwsh and DLL.
|
||||||
|
If not found or not suitable, scans all pwsh executables in PATH, runs --version to find latest >= min_version.
|
||||||
|
Returns full DLL path if found, else None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def parse_version(output):
|
||||||
|
# Extract semantic version from pwsh --version output
|
||||||
|
match = re.search(r'(\d+)\.(\d+)\.(\d+)', output)
|
||||||
|
if match:
|
||||||
|
return tuple(map(int, match.groups()))
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Try environment variable override first
|
||||||
|
pwsh_dir = os.environ.get(env_var)
|
||||||
|
if pwsh_dir:
|
||||||
|
pwsh_path = Path(pwsh_dir) / executable_name
|
||||||
|
dll_path = Path(pwsh_dir) / dll_name
|
||||||
|
if pwsh_path.is_file() and dll_path.is_file():
|
||||||
|
try:
|
||||||
|
completed = subprocess.run(
|
||||||
|
[str(pwsh_path), '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if completed.returncode == 0:
|
||||||
|
ver = parse_version(completed.stdout)
|
||||||
|
if ver and ver >= min_version:
|
||||||
|
logger.info(f'Found pwsh from env variable "{env_var}"')
|
||||||
|
return str(dll_path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Adjust executable_name for Windows if needed
|
||||||
|
if os.name == 'nt' and not executable_name.lower().endswith('.exe'):
|
||||||
|
executable_name += '.exe'
|
||||||
|
|
||||||
|
# Search PATH for all pwsh executables
|
||||||
|
paths = os.environ.get('PATH', '').split(os.pathsep)
|
||||||
|
candidates = []
|
||||||
|
for p in paths:
|
||||||
|
exe_path = Path(p) / executable_name
|
||||||
|
if exe_path.is_file() and os.access(str(exe_path), os.X_OK):
|
||||||
|
try:
|
||||||
|
completed = subprocess.run(
|
||||||
|
[str(exe_path), '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if completed.returncode == 0:
|
||||||
|
ver = parse_version(completed.stdout)
|
||||||
|
if ver:
|
||||||
|
candidates.append((ver, exe_path.resolve()))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Sort candidates by version descending
|
||||||
|
candidates.sort(key=lambda x: x[0], reverse=True)
|
||||||
|
|
||||||
|
for ver, exe_path in candidates:
|
||||||
|
if ver >= min_version:
|
||||||
|
dll_path = exe_path.parent / dll_name
|
||||||
|
if dll_path.is_file():
|
||||||
|
return str(dll_path)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# Attempt to load the PowerShell SDK assembly only if clr and System loaded
|
# Attempt to load the PowerShell SDK assembly only if clr and System loaded
|
||||||
ps_sdk_path = None
|
ps_sdk_path = None
|
||||||
try:
|
try:
|
||||||
# Prioritize PowerShell 7+ if available (adjust path if necessary)
|
# Attempt primary detection via helper function
|
||||||
pwsh7_path = (
|
ps_sdk_path = find_latest_pwsh_sdk_path()
|
||||||
Path(os.environ.get('ProgramFiles', 'C:\\Program Files'))
|
if ps_sdk_path:
|
||||||
/ 'PowerShell'
|
|
||||||
/ '7'
|
|
||||||
/ 'System.Management.Automation.dll'
|
|
||||||
)
|
|
||||||
if pwsh7_path.exists():
|
|
||||||
ps_sdk_path = str(pwsh7_path)
|
|
||||||
clr.AddReference(ps_sdk_path)
|
clr.AddReference(ps_sdk_path)
|
||||||
logger.info(f'Loaded PowerShell SDK (Core): {ps_sdk_path}')
|
logger.info(f'Loaded PowerShell SDK dynamically detected: {ps_sdk_path}')
|
||||||
else:
|
else:
|
||||||
# Fallback to Windows PowerShell 5.1 bundled with Windows
|
pwsh7_path = (
|
||||||
winps_path = (
|
Path(os.environ.get('ProgramFiles', 'C:\\Program Files'))
|
||||||
Path(os.environ.get('SystemRoot', 'C:\\Windows'))
|
/ 'PowerShell'
|
||||||
/ 'System32'
|
/ '7'
|
||||||
/ 'WindowsPowerShell'
|
|
||||||
/ 'v1.0'
|
|
||||||
/ 'System.Management.Automation.dll'
|
/ 'System.Management.Automation.dll'
|
||||||
)
|
)
|
||||||
if winps_path.exists():
|
if pwsh7_path.exists():
|
||||||
ps_sdk_path = str(winps_path)
|
ps_sdk_path = str(pwsh7_path)
|
||||||
clr.AddReference(ps_sdk_path)
|
clr.AddReference(ps_sdk_path)
|
||||||
logger.debug(f'Loaded PowerShell SDK (Desktop): {ps_sdk_path}')
|
logger.info(f'Loaded PowerShell SDK (Core): {ps_sdk_path}')
|
||||||
else:
|
else:
|
||||||
# Last resort: try loading by assembly name (might work if in GAC or path)
|
# Fallback to Windows PowerShell 5.1 bundled with Windows
|
||||||
clr.AddReference('System.Management.Automation')
|
winps_path = (
|
||||||
logger.info(
|
Path(os.environ.get('SystemRoot', 'C:\\Windows'))
|
||||||
'Attempted to load PowerShell SDK by name (System.Management.Automation)'
|
/ 'System32'
|
||||||
|
/ 'WindowsPowerShell'
|
||||||
|
/ 'v1.0'
|
||||||
|
/ 'System.Management.Automation.dll'
|
||||||
)
|
)
|
||||||
|
if winps_path.exists():
|
||||||
|
ps_sdk_path = str(winps_path)
|
||||||
|
clr.AddReference(ps_sdk_path)
|
||||||
|
logger.debug(f'Loaded PowerShell SDK (Desktop): {ps_sdk_path}')
|
||||||
|
else:
|
||||||
|
# Last resort: try loading by assembly name (might work if in GAC or path)
|
||||||
|
clr.AddReference('System.Management.Automation')
|
||||||
|
logger.info(
|
||||||
|
'Attempted to load PowerShell SDK by name (System.Management.Automation)'
|
||||||
|
)
|
||||||
|
|
||||||
from System.Management.Automation import JobState, PowerShell
|
from System.Management.Automation import JobState, PowerShell
|
||||||
from System.Management.Automation.Language import Parser
|
from System.Management.Automation.Language import Parser
|
||||||
|
|||||||
Reference in New Issue
Block a user