Initial upload: gitignore-generator-cli v1.0.0 with CI/CD workflow
This commit is contained in:
125
gitignore_generator/api.py
Normal file
125
gitignore_generator/api.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""API client for gitignore.io."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
|
||||
BASE_URL = "https://www.toptal.com/developers/gitignore/api"
|
||||
CACHE_DIR = Path.home() / ".cache" / "gitignore-generator"
|
||||
TEMPLATES_DIR = Path(__file__).parent.parent / "templates"
|
||||
|
||||
|
||||
class GitignoreIOError(Exception):
|
||||
"""Exception raised for gitignore.io API errors."""
|
||||
pass
|
||||
|
||||
|
||||
def get_patterns(tech: str, force_refresh: bool = False) -> str:
|
||||
"""Fetch gitignore patterns for a single technology.
|
||||
|
||||
Args:
|
||||
tech: Technology name (e.g., 'node', 'python', 'django')
|
||||
force_refresh: Force refresh from API instead of using cache
|
||||
|
||||
Returns:
|
||||
Gitignore patterns as a string
|
||||
|
||||
Raises:
|
||||
GitignoreIOError: If the API request fails
|
||||
"""
|
||||
tech_lower = tech.lower()
|
||||
cache_file = CACHE_DIR / f"{tech_lower}.txt"
|
||||
|
||||
if not force_refresh and cache_file.exists():
|
||||
cache_data = cache_file.read_text()
|
||||
return cache_data
|
||||
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/{tech_lower}", timeout=30)
|
||||
response.raise_for_status()
|
||||
patterns = response.text
|
||||
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
cache_file.write_text(patterns)
|
||||
return patterns
|
||||
except requests.RequestException as e:
|
||||
fallback = get_local_template(tech_lower)
|
||||
if fallback:
|
||||
return fallback
|
||||
raise GitignoreIOError(f"Failed to fetch patterns for '{tech}': {e}")
|
||||
|
||||
|
||||
def get_patterns_batch(techs: list[str], force_refresh: bool = False) -> dict[str, str]:
|
||||
"""Fetch gitignore patterns for multiple technologies.
|
||||
|
||||
Args:
|
||||
techs: List of technology names
|
||||
force_refresh: Force refresh from API instead of using cache
|
||||
|
||||
Returns:
|
||||
Dictionary mapping technology names to their patterns
|
||||
"""
|
||||
patterns = {}
|
||||
for tech in techs:
|
||||
try:
|
||||
patterns[tech] = get_patterns(tech, force_refresh)
|
||||
except GitignoreIOError:
|
||||
patterns[tech] = ""
|
||||
return patterns
|
||||
|
||||
|
||||
def get_list(force_refresh: bool = False) -> list[str]:
|
||||
"""Get list of all available technology stacks from gitignore.io.
|
||||
|
||||
Args:
|
||||
force_refresh: Force refresh from API instead of using cache
|
||||
|
||||
Returns:
|
||||
List of available technology names
|
||||
"""
|
||||
cache_file = CACHE_DIR / "list.txt"
|
||||
|
||||
if not force_refresh and cache_file.exists():
|
||||
cache_data = cache_file.read_text()
|
||||
return cache_data.strip().split('\n')
|
||||
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/list", timeout=30)
|
||||
response.raise_for_status()
|
||||
lines = response.text.strip().split('\n')
|
||||
techs = [line.split(',')[0] for line in lines if line]
|
||||
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
cache_file.write_text(response.text)
|
||||
return techs
|
||||
except requests.RequestException as e:
|
||||
local_list = get_local_list()
|
||||
if local_list:
|
||||
return local_list
|
||||
raise GitignoreIOError(f"Failed to fetch list: {e}")
|
||||
|
||||
|
||||
def get_local_template(tech: str) -> Optional[str]:
|
||||
"""Get local template for a technology as fallback.
|
||||
|
||||
Args:
|
||||
tech: Technology name
|
||||
|
||||
Returns:
|
||||
Template content or None if not found
|
||||
"""
|
||||
template_file = TEMPLATES_DIR / f"{tech.lower()}.gitignore"
|
||||
if template_file.exists():
|
||||
return template_file.read_text()
|
||||
return None
|
||||
|
||||
|
||||
def get_local_list() -> Optional[list[str]]:
|
||||
"""Get list of locally available templates.
|
||||
|
||||
Returns:
|
||||
List of available technology names or None
|
||||
"""
|
||||
list_file = TEMPLATES_DIR / "list.txt"
|
||||
if list_file.exists():
|
||||
return list_file.read_text().strip().split('\n')
|
||||
return None
|
||||
Reference in New Issue
Block a user