Skip to content

Commit eeeb9dc

Browse files
authored
Merge pull request #24 from alex-feel/alex-feel-dev
Properly handle SSL errors and npm path resolution
2 parents 37fa262 + d47ccee commit eeeb9dc

File tree

2 files changed

+109
-61
lines changed

2 files changed

+109
-61
lines changed

scripts/install-claude.py

Lines changed: 87 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import sys
1515
import tempfile
1616
import time
17+
import urllib.error
18+
import urllib.request
1719
from pathlib import Path
1820
from typing import Any
1921
from urllib.request import urlopen
@@ -83,14 +85,21 @@ def banner() -> None:
8385
def run_command(cmd: list, capture_output: bool = True, **kwargs: Any) -> subprocess.CompletedProcess:
8486
"""Run a command and return the result."""
8587
try:
88+
# Debug: print command being run if not capturing output
89+
if not capture_output:
90+
info(f'Executing: {", ".join(cmd)}')
8691
return subprocess.run(
8792
cmd,
8893
capture_output=capture_output,
8994
text=True,
9095
**kwargs,
9196
)
92-
except FileNotFoundError:
97+
except FileNotFoundError as e:
98+
error(f'Command not found: {cmd[0]} - {e}')
9399
return subprocess.CompletedProcess(cmd, 1, '', f'Command not found: {cmd[0]}')
100+
except Exception as e:
101+
error(f'Error running command {cmd[0]}: {e}')
102+
return subprocess.CompletedProcess(cmd, 1, '', str(e))
94103

95104

96105
def is_admin() -> bool:
@@ -197,13 +206,16 @@ def install_git_windows_download() -> bool:
197206
try:
198207
with urlopen(GIT_WINDOWS_URL) as response:
199208
html = response.read().decode('utf-8')
200-
except ssl.SSLError:
201-
warning('SSL certificate verification failed, trying with unverified context')
202-
ctx = ssl.create_default_context()
203-
ctx.check_hostname = False
204-
ctx.verify_mode = ssl.CERT_NONE
205-
with urlopen(GIT_WINDOWS_URL, context=ctx) as response:
206-
html = response.read().decode('utf-8')
209+
except urllib.error.URLError as e:
210+
if 'SSL' in str(e) or 'certificate' in str(e).lower():
211+
warning('SSL certificate verification failed, trying with unverified context')
212+
ctx = ssl.create_default_context()
213+
ctx.check_hostname = False
214+
ctx.verify_mode = ssl.CERT_NONE
215+
with urlopen(GIT_WINDOWS_URL, context=ctx) as response:
216+
html = response.read().decode('utf-8')
217+
else:
218+
raise
207219

208220
# Find the installer link
209221
match = re.search(r'href="([^"]+Git-[\d.]+-64-bit\.exe)"', html)
@@ -221,15 +233,17 @@ def install_git_windows_download() -> bool:
221233
info(f'Downloading {installer_url}')
222234
try:
223235
urlretrieve(installer_url, temp_path)
224-
except ssl.SSLError:
225-
warning('SSL certificate verification failed, trying with unverified context')
226-
ctx = ssl.create_default_context()
227-
ctx.check_hostname = False
228-
ctx.verify_mode = ssl.CERT_NONE
229-
import urllib.request
230-
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
231-
urllib.request.install_opener(opener)
232-
urlretrieve(installer_url, temp_path)
236+
except urllib.error.URLError as e:
237+
if 'SSL' in str(e) or 'certificate' in str(e).lower():
238+
warning('SSL certificate verification failed, trying with unverified context')
239+
ctx = ssl.create_default_context()
240+
ctx.check_hostname = False
241+
ctx.verify_mode = ssl.CERT_NONE
242+
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
243+
urllib.request.install_opener(opener)
244+
urlretrieve(installer_url, temp_path)
245+
else:
246+
raise
233247

234248
# Run installer silently
235249
info('Running Git installer silently...')
@@ -383,13 +397,16 @@ def install_nodejs_direct() -> bool:
383397
try:
384398
with urlopen(NODE_LTS_API) as response:
385399
versions = json.loads(response.read())
386-
except ssl.SSLError:
387-
warning('SSL certificate verification failed, trying with unverified context')
388-
ctx = ssl.create_default_context()
389-
ctx.check_hostname = False
390-
ctx.verify_mode = ssl.CERT_NONE
391-
with urlopen(NODE_LTS_API, context=ctx) as response:
392-
versions = json.loads(response.read())
400+
except urllib.error.URLError as e:
401+
if 'SSL' in str(e) or 'certificate' in str(e).lower():
402+
warning('SSL certificate verification failed, trying with unverified context')
403+
ctx = ssl.create_default_context()
404+
ctx.check_hostname = False
405+
ctx.verify_mode = ssl.CERT_NONE
406+
with urlopen(NODE_LTS_API, context=ctx) as response:
407+
versions = json.loads(response.read())
408+
else:
409+
raise
393410

394411
lts_version = None
395412
for v in versions:
@@ -424,15 +441,17 @@ def install_nodejs_direct() -> bool:
424441
info(f'Downloading {installer_url}')
425442
try:
426443
urlretrieve(installer_url, temp_path)
427-
except ssl.SSLError:
428-
warning('SSL certificate verification failed, trying with unverified context')
429-
ctx = ssl.create_default_context()
430-
ctx.check_hostname = False
431-
ctx.verify_mode = ssl.CERT_NONE
432-
import urllib.request
433-
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
434-
urllib.request.install_opener(opener)
435-
urlretrieve(installer_url, temp_path)
444+
except urllib.error.URLError as e:
445+
if 'SSL' in str(e) or 'certificate' in str(e).lower():
446+
warning('SSL certificate verification failed, trying with unverified context')
447+
ctx = ssl.create_default_context()
448+
ctx.check_hostname = False
449+
ctx.verify_mode = ssl.CERT_NONE
450+
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
451+
urllib.request.install_opener(opener)
452+
urlretrieve(installer_url, temp_path)
453+
else:
454+
raise
436455

437456
# Install based on OS
438457
if system == 'Windows':
@@ -602,22 +621,40 @@ def ensure_nodejs() -> bool:
602621
def install_claude_npm() -> bool:
603622
"""Install Claude Code using npm."""
604623
# On Windows, check if npm is in Program Files even if not in PATH
605-
if platform.system() == 'Windows' and not find_command('npm'):
624+
if platform.system() == 'Windows':
606625
nodejs_path = r'C:\Program Files\nodejs'
607626
if Path(nodejs_path).exists():
608627
current_path = os.environ.get('PATH', '')
609628
if nodejs_path not in current_path:
610629
os.environ['PATH'] = f'{nodejs_path};{current_path}'
611630
info(f'Added {nodejs_path} to PATH for npm access')
612631

613-
if not find_command('npm'):
614-
error('npm not found. Please install Node.js with npm')
615-
return False
632+
# Also check for npm.cmd specifically on Windows
633+
npm_cmd = Path(nodejs_path) / 'npm.cmd'
634+
if npm_cmd.exists():
635+
info(f'Found npm.cmd at {npm_cmd}')
636+
637+
npm_path = find_command('npm')
638+
if not npm_path:
639+
# On Windows, try to find npm.cmd explicitly
640+
if platform.system() == 'Windows':
641+
npm_cmd_path = Path(r'C:\Program Files\nodejs\npm.cmd')
642+
if npm_cmd_path.exists():
643+
npm_path = str(npm_cmd_path)
644+
info(f'Using npm.cmd at {npm_path}')
645+
else:
646+
error('npm not found. Please install Node.js with npm')
647+
return False
648+
else:
649+
error('npm not found. Please install Node.js with npm')
650+
return False
616651

617-
info('Installing Claude Code CLI via npm...')
652+
info(f'Installing Claude Code CLI via npm (npm path: {npm_path})...')
618653

619654
# Try without sudo first (show output for debugging)
620-
result = run_command(['npm', 'install', '-g', CLAUDE_NPM_PACKAGE], capture_output=False)
655+
cmd = [npm_path, 'install', '-g', CLAUDE_NPM_PACKAGE]
656+
info(f'Running command: {" ".join(cmd)}')
657+
result = run_command(cmd, capture_output=False)
621658

622659
if result.returncode == 0:
623660
success('Claude Code installed successfully')
@@ -647,14 +684,17 @@ def install_claude_native() -> bool:
647684
try:
648685
with urlopen(CLAUDE_INSTALLER_URL) as response:
649686
installer_script = response.read().decode('utf-8')
650-
except ssl.SSLError:
651-
# Fallback: create unverified SSL context for corporate environments
652-
warning('SSL certificate verification failed, trying with unverified context')
653-
ctx = ssl.create_default_context()
654-
ctx.check_hostname = False
655-
ctx.verify_mode = ssl.CERT_NONE
656-
with urlopen(CLAUDE_INSTALLER_URL, context=ctx) as response:
657-
installer_script = response.read().decode('utf-8')
687+
except urllib.error.URLError as e:
688+
if 'SSL' in str(e) or 'certificate' in str(e).lower():
689+
# Fallback: create unverified SSL context for corporate environments
690+
warning('SSL certificate verification failed, trying with unverified context')
691+
ctx = ssl.create_default_context()
692+
ctx.check_hostname = False
693+
ctx.verify_mode = ssl.CERT_NONE
694+
with urlopen(CLAUDE_INSTALLER_URL, context=ctx) as response:
695+
installer_script = response.read().decode('utf-8')
696+
else:
697+
raise
658698

659699
# Save to temp file and execute
660700
with tempfile.NamedTemporaryFile(suffix='.ps1', delete=False, mode='w') as tmp:

scripts/setup-python-environment.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import subprocess
1313
import sys
1414
import tempfile
15+
import urllib.error
16+
import urllib.request
1517
from pathlib import Path
1618
from typing import Any
1719
from urllib.request import urlopen
@@ -125,13 +127,16 @@ def download_file(url: str, destination: Path, force: bool = False) -> bool:
125127
try:
126128
response = urlopen(url)
127129
content = response.read()
128-
except ssl.SSLError:
129-
warning('SSL certificate verification failed, trying with unverified context')
130-
ctx = ssl.create_default_context()
131-
ctx.check_hostname = False
132-
ctx.verify_mode = ssl.CERT_NONE
133-
response = urlopen(url, context=ctx)
134-
content = response.read()
130+
except urllib.error.URLError as e:
131+
if 'SSL' in str(e) or 'certificate' in str(e).lower():
132+
warning('SSL certificate verification failed, trying with unverified context')
133+
ctx = ssl.create_default_context()
134+
ctx.check_hostname = False
135+
ctx.verify_mode = ssl.CERT_NONE
136+
response = urlopen(url, context=ctx)
137+
content = response.read()
138+
else:
139+
raise
135140

136141
destination.parent.mkdir(parents=True, exist_ok=True)
137142
destination.write_bytes(content)
@@ -156,13 +161,16 @@ def install_claude() -> bool:
156161
try:
157162
response = urlopen(installer_url)
158163
content = response.read().decode('utf-8')
159-
except ssl.SSLError:
160-
warning('SSL certificate verification failed, trying with unverified context')
161-
ctx = ssl.create_default_context()
162-
ctx.check_hostname = False
163-
ctx.verify_mode = ssl.CERT_NONE
164-
response = urlopen(installer_url, context=ctx)
165-
content = response.read().decode('utf-8')
164+
except urllib.error.URLError as e:
165+
if 'SSL' in str(e) or 'certificate' in str(e).lower():
166+
warning('SSL certificate verification failed, trying with unverified context')
167+
ctx = ssl.create_default_context()
168+
ctx.check_hostname = False
169+
ctx.verify_mode = ssl.CERT_NONE
170+
response = urlopen(installer_url, context=ctx)
171+
content = response.read().decode('utf-8')
172+
else:
173+
raise
166174
tmp.write(content)
167175
temp_installer = tmp.name
168176

0 commit comments

Comments
 (0)