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