Skip to content

Commit e21820f

Browse files
committed
Enhance HTTP client initialization with optional SSL verification and bump version to 0.0.3
1 parent 6e2af26 commit e21820f

File tree

2 files changed

+26
-21
lines changed

2 files changed

+26
-21
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "specify-cli"
3-
version = "0.0.2"
3+
version = "0.0.3"
44
description = "Setup tool for Specify spec-driven development projects"
55
requires-python = ">=3.11"
66
dependencies = [

src/specify_cli/__init__.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -390,12 +390,11 @@ def init_git_repo(project_path: Path, quiet: bool = False) -> bool:
390390
os.chdir(original_cwd)
391391

392392

393-
def download_template_from_github(ai_assistant: str, download_dir: Path, *, verbose: bool = True, show_progress: bool = True):
394-
"""Download the latest template release from GitHub using HTTP requests.
395-
Returns (zip_path, metadata_dict)
396-
"""
393+
def download_template_from_github(ai_assistant: str, download_dir: Path, *, verbose: bool = True, show_progress: bool = True, client: httpx.Client = None):
397394
repo_owner = "github"
398395
repo_name = "spec-kit"
396+
if client is None:
397+
client = httpx.Client(verify=ssl_context)
399398

400399
if verbose:
401400
console.print("[cyan]Fetching latest release information...[/cyan]")
@@ -405,7 +404,7 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, verb
405404
response = client.get(api_url, timeout=30, follow_redirects=True)
406405
response.raise_for_status()
407406
release_data = response.json()
408-
except client.RequestError as e:
407+
except httpx.RequestError as e:
409408
if verbose:
410409
console.print(f"[red]Error fetching release information:[/red] {e}")
411410
raise typer.Exit(1)
@@ -445,15 +444,12 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, verb
445444
with client.stream("GET", download_url, timeout=30, follow_redirects=True) as response:
446445
response.raise_for_status()
447446
total_size = int(response.headers.get('content-length', 0))
448-
449447
with open(zip_path, 'wb') as f:
450448
if total_size == 0:
451-
# No content-length header, download without progress
452449
for chunk in response.iter_bytes(chunk_size=8192):
453450
f.write(chunk)
454451
else:
455452
if show_progress:
456-
# Show progress bar
457453
with Progress(
458454
SpinnerColumn(),
459455
TextColumn("[progress.description]{task.description}"),
@@ -467,11 +463,9 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, verb
467463
downloaded += len(chunk)
468464
progress.update(task, completed=downloaded)
469465
else:
470-
# Silent download loop
471466
for chunk in response.iter_bytes(chunk_size=8192):
472467
f.write(chunk)
473-
474-
except client.RequestError as e:
468+
except httpx.RequestError as e:
475469
if verbose:
476470
console.print(f"[red]Error downloading template:[/red] {e}")
477471
if zip_path.exists():
@@ -488,7 +482,7 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, verb
488482
return zip_path, metadata
489483

490484

491-
def download_and_extract_template(project_path: Path, ai_assistant: str, is_current_dir: bool = False, *, verbose: bool = True, tracker: StepTracker | None = None) -> Path:
485+
def download_and_extract_template(project_path: Path, ai_assistant: str, is_current_dir: bool = False, *, verbose: bool = True, tracker: StepTracker | None = None, client: httpx.Client = None) -> Path:
492486
"""Download the latest release and extract it to create a new project.
493487
Returns project_path. Uses tracker if provided (with keys: fetch, download, extract, cleanup)
494488
"""
@@ -502,12 +496,13 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, is_curr
502496
ai_assistant,
503497
current_dir,
504498
verbose=verbose and tracker is None,
505-
show_progress=(tracker is None)
499+
show_progress=(tracker is None),
500+
client=client
506501
)
507502
if tracker:
508503
tracker.complete("fetch", f"release {meta['release']} ({meta['size']:,} bytes)")
509504
tracker.add("download", "Download template")
510-
tracker.complete("download", meta['filename']) # already downloaded inside helper
505+
tracker.complete("download", meta['filename'])
511506
except Exception as e:
512507
if tracker:
513508
tracker.error("fetch", str(e))
@@ -647,6 +642,7 @@ def init(
647642
ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for AI agent tools like Claude Code"),
648643
no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"),
649644
here: bool = typer.Option(False, "--here", help="Initialize project in the current directory instead of creating a new one"),
645+
skip_tls: bool = typer.Option(False, "--skip-tls", help="Skip SSL/TLS verification (not recommended)"),
650646
):
651647
"""
652648
Initialize a new Specify project from the latest template.
@@ -775,7 +771,12 @@ def init(
775771
with Live(tracker.render(), console=console, refresh_per_second=8, transient=True) as live:
776772
tracker.attach_refresh(lambda: live.update(tracker.render()))
777773
try:
778-
download_and_extract_template(project_path, selected_ai, here, verbose=False, tracker=tracker)
774+
# Create a httpx client with verify based on skip_tls
775+
verify = not skip_tls
776+
local_ssl_context = ssl_context if verify else False
777+
local_client = httpx.Client(verify=local_ssl_context)
778+
779+
download_and_extract_template(project_path, selected_ai, here, verbose=False, tracker=tracker, client=local_client)
779780

780781
# Git step
781782
if not no_git:
@@ -839,21 +840,25 @@ def init(
839840
# Removed farewell line per user request
840841

841842

843+
# Add skip_tls option to check
842844
@app.command()
843-
def check():
845+
def check(skip_tls: bool = typer.Option(False, "--skip-tls", help="Skip SSL/TLS verification (not recommended)")):
844846
"""Check that all required tools are installed."""
845847
show_banner()
846848
console.print("[bold]Checking Specify requirements...[/bold]\n")
847-
849+
848850
# Check if we have internet connectivity by trying to reach GitHub API
849851
console.print("[cyan]Checking internet connectivity...[/cyan]")
852+
verify = not skip_tls
853+
local_ssl_context = ssl_context if verify else False
854+
local_client = httpx.Client(verify=local_ssl_context)
850855
try:
851-
response = client.get("https://api.github.com", timeout=5, follow_redirects=True)
856+
response = local_client.get("https://api.github.com", timeout=5, follow_redirects=True)
852857
console.print("[green]✓[/green] Internet connection available")
853-
except client.RequestError:
858+
except httpx.RequestError:
854859
console.print("[red]✗[/red] No internet connection - required for downloading templates")
855860
console.print("[yellow]Please check your internet connection[/yellow]")
856-
861+
857862
console.print("\n[cyan]Optional tools:[/cyan]")
858863
git_ok = check_tool("git", "https://git-scm.com/downloads")
859864

0 commit comments

Comments
 (0)