This commit is contained in:
sankalp committed 2025-06-04 09:39:28 +05:45
1 parent 61849e7645
commit 1814fdf298
23 files changed
+2117

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
"""
Gitignore generator module.
"""
from .cli import cli
from .generator import GitignoreGenerator
from .detector import ProjectDetector
__all__ = [
"cli",
"GitignoreGenerator",
"ProjectDetector"
]
+84
View File
@@ -0,0 +1,84 @@
"""
Gitignore generation CLI.
"""
import os
import click
from rich.console import Console
from rich.prompt import Confirm
from rich.progress import Progress, SpinnerColumn, TextColumn
from .generator import GitignoreGenerator
from .detector import ProjectDetector
console = Console()
@click.group()
def cli():
"""Generate .gitignore files."""
pass
@cli.command()
@click.option("--directory", "-d", default=".", help="Directory to scan")
@click.option("--output", "-o", default=".gitignore", help="Output file path")
def auto(directory: str, output: str):
"""Automatically detect project types and generate .gitignore."""
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console
) as progress:
# Initialize services
progress.add_task("Initializing...", total=None)
detector = ProjectDetector(directory)
generator = GitignoreGenerator()
# Detect project types
progress.add_task("Detecting project types...", total=None)
detected_types = detector.detect_project_types()
if not detected_types:
console.print("[yellow]No project types detected. Using generic .gitignore.")
else:
console.print(f"\n[green]Detected project types:[/] {', '.join(detected_types)}")
# Check if output file exists
if os.path.exists(output):
if not Confirm.ask(f"\n[yellow]File {output} already exists. Overwrite?"):
return
# Generate .gitignore
progress.add_task("Generating .gitignore...", total=None)
generator.update_gitignore_file(detected_types, output)
console.print(f"\n[green]Generated .gitignore at {output}")
@cli.command()
@click.argument("types", nargs=-1, required=True)
@click.option("--output", "-o", default=".gitignore", help="Output file path")
def generate(types: tuple, output: str):
"""Generate .gitignore for specified types."""
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console
) as progress:
# Initialize services
progress.add_task("Initializing...", total=None)
generator = GitignoreGenerator()
# Check if output file exists
if os.path.exists(output):
if not Confirm.ask(f"\n[yellow]File {output} already exists. Overwrite?"):
return
# Generate .gitignore
progress.add_task("Generating .gitignore...", total=None)
generator.update_gitignore_file(list(types), output)
console.print(f"\n[green]Generated .gitignore at {output}")
# Add command aliases
cli.add_command(auto, name="a")
cli.add_command(generate, name="g")
if __name__ == "__main__":
cli()
+161
View File
@@ -0,0 +1,161 @@
"""
Project type detection for gitignore generation.
"""
import os
from typing import List, Dict, Set
class ProjectDetector:
"""Detect project types based on file patterns."""
# File patterns for different project types
PROJECT_PATTERNS = {
# Languages
"python": ["*.py", "requirements.txt", "setup.py", "Pipfile", "pyproject.toml"],
"javascript": ["package.json", "*.js", "*.jsx", "*.ts", "*.tsx"],
"typescript": ["tsconfig.json", "*.ts", "*.tsx"],
"java": ["*.java", "pom.xml", "build.gradle", "*.class"],
"kotlin": ["*.kt", "*.kts"],
"go": ["go.mod", "go.sum", "*.go"],
"rust": ["Cargo.toml", "Cargo.lock", "*.rs"],
"ruby": ["Gemfile", "*.rb", "*.gemspec"],
"php": ["composer.json", "*.php"],
"csharp": ["*.cs", "*.csproj", "*.sln"],
"swift": ["*.swift", "*.xcodeproj"],
"objective-c": ["*.m", "*.h", "*.mm"],
# Frameworks
"django": ["manage.py", "wsgi.py", "asgi.py"],
"flask": ["app.py", "flask_app.py"],
"fastapi": ["main.py", "app.py"],
"react": ["src/App.js", "src/App.tsx"],
"vue": ["vue.config.js", "*.vue"],
"angular": ["angular.json", "tsconfig.json"],
"spring": ["application.properties", "application.yml"],
"laravel": ["artisan", "composer.json"],
"rails": ["config/routes.rb", "Gemfile"],
"express": ["app.js", "server.js"],
"next": ["next.config.js"],
"nuxt": ["nuxt.config.js"],
# Build Tools
"maven": ["pom.xml"],
"gradle": ["build.gradle", "settings.gradle"],
"npm": ["package.json"],
"yarn": ["yarn.lock"],
"pip": ["requirements.txt"],
"poetry": ["pyproject.toml"],
"cargo": ["Cargo.toml"],
"composer": ["composer.json"],
# IDEs and Editors
"vscode": [".vscode/settings.json"],
"intellij": [".idea/workspace.xml"],
"eclipse": [".project", ".classpath"],
"vim": [".vimrc", ".vim/"],
"emacs": [".emacs", ".emacs.d/"],
"sublime": ["*.sublime-project", "*.sublime-workspace"],
"atom": [".atom/"],
# Operating Systems
"windows": ["*.exe", "*.dll", "*.sys"],
"macos": [".DS_Store", "*.app"],
"linux": ["*.so", "*.o"],
# Testing Frameworks
"jest": ["jest.config.js"],
"pytest": ["pytest.ini", "conftest.py"],
"junit": ["junit.xml"],
"mocha": ["mocha.opts"],
# Documentation
"sphinx": ["docs/conf.py"],
"mkdocs": ["mkdocs.yml"],
"docusaurus": ["docusaurus.config.js"],
# Databases
"postgresql": ["*.sql", "*.dump"],
"mysql": ["*.sql", "*.dump"],
"mongodb": ["*.bson"],
"sqlite": ["*.db", "*.sqlite"],
# Cloud Platforms
"aws": [".aws/", "aws.json"],
"azure": ["azure.yaml", "azure.json"],
"gcp": ["gcloud.json"],
# Containerization
"docker": ["Dockerfile", "docker-compose.yml"],
"kubernetes": ["k8s/", "*.yaml"],
# CI/CD
"github-actions": [".github/workflows/"],
"gitlab-ci": [".gitlab-ci.yml"],
"jenkins": ["Jenkinsfile"],
"travis": [".travis.yml"],
"circleci": [".circleci/config.yml"],
# Static Site Generators
"jekyll": ["_config.yml"],
"hugo": ["config.toml", "config.yaml"],
"gatsby": ["gatsby-config.js"],
# Game Development
"unity": ["*.unity", "*.unityproj"],
"unreal": ["*.uproject"],
# Machine Learning
"tensorflow": ["*.pb", "*.h5"],
"pytorch": ["*.pt", "*.pth"],
"scikit-learn": ["*.joblib"]
}
def __init__(self, directory: str = "."):
"""Initialize project detector.
Args:
directory: Directory to scan
"""
self.directory = directory
def detect_project_types(self) -> List[str]:
"""Detect project types in the directory.
Returns:
List of detected project types
"""
detected_types = set()
# Scan directory for files
for root, _, files in os.walk(self.directory):
for file in files:
file_path = os.path.join(root, file)
rel_path = os.path.relpath(file_path, self.directory)
# Check each project type's patterns
for type_, patterns in self.PROJECT_PATTERNS.items():
if any(self._matches_pattern(rel_path, pattern) for pattern in patterns):
detected_types.add(type_)
return sorted(list(detected_types))
def _matches_pattern(self, path: str, pattern: str) -> bool:
"""Check if a path matches a pattern.
Args:
path: Path to check
pattern: Pattern to match
Returns:
True if path matches pattern
"""
# Handle directory patterns
if pattern.endswith("/"):
return path.startswith(pattern[:-1])
# Handle wildcard patterns
if "*" in pattern:
import fnmatch
return fnmatch.fnmatch(path, pattern)
# Handle exact matches
return path == pattern
+106
View File
@@ -0,0 +1,106 @@
"""
Gitignore generator using gitignore.io API.
"""
import os
import requests
from typing import List, Dict
class GitignoreGenerator:
"""Gitignore generator using gitignore.io API."""
def __init__(self):
"""Initialize gitignore generator."""
self.api_url = "https://www.toptal.com/developers/gitignore/api"
def generate_gitignore(self, types: List[str]) -> str:
"""Generate .gitignore content for specified types.
Args:
types: List of project types
Returns:
Generated .gitignore content
"""
if not types:
return self._get_generic_gitignore()
try:
# Get gitignore content from API
response = requests.get(f"{self.api_url}/{','.join(types)}")
response.raise_for_status()
# Add header
header = "# Generated by devtools gitignore generator\n"
header += f"# Types: {', '.join(types)}\n\n"
return header + response.text
except requests.RequestException:
# Fallback to generic gitignore if API fails
return self._get_generic_gitignore()
def _get_generic_gitignore(self) -> str:
"""Get a generic .gitignore template.
Returns:
Generic .gitignore content
"""
return """# Generic .gitignore
# Generated by devtools gitignore generator
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDE files
.idea/
.vscode/
*.swp
*.swo
*~
# Build directories
build/
dist/
out/
target/
# Dependencies
node_modules/
venv/
env/
__pycache__/
*.pyc
# Logs
*.log
logs/
# Environment variables
.env
.env.local
.env.*.local
# Temporary files
*.tmp
*.temp
*.bak
"""
def update_gitignore_file(self, types: List[str], output_file: str) -> None:
"""Generate and update a .gitignore file.
Args:
types: List of project types
output_file: Path to the .gitignore file
"""
content = self.generate_gitignore(types)
# Write to file
with open(output_file, "w") as f:
f.write(content)