Files
project-scaffold-cli/project_scaffold_cli/gitignore.py

204 lines
3.8 KiB
Python

"""Gitignore generator for Project Scaffold CLI."""
import os
from pathlib import Path
from typing import Dict, Optional, Set
class GitignoreGenerator:
"""Generate language-specific .gitignore files."""
SUPPORTED_LANGUAGES = ["python", "nodejs", "go", "rust"]
GITIGNORE_TEMPLATES = {
"python": """__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
venv/
ENV/
env/
.venv/
.pytest_cache/
.coverage
.coverage.*
htmlcov/
.tox/
.nox/
*.manifest
*.spec
""",
"nodejs": """node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.log
.DS_Store
dist/
build/
.nyc_output/
coverage/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
""",
"go": """# Binaries
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary
*.test
# Output of go coverage
*.out
# Go workspace
go.work
# Vendor directory
vendor/
# IDE
.idea/
.vscode/
*.swp
*.swo
""",
"rust": """# Cargo
target/
Cargo.lock
# IDE
.idea/
.vscode/
*.swp
*.swo
# Backup files
*~
#*#
#*
.backup
# Build artifacts
*.rlib
*.dylib
*.dll
# IDE
.vscode/
.idea/
""",
}
ADDITIONAL_PATTERNS = {
"python": """*.pyc
*.pyo
.env
.env.local
.vscode/
.idea/
""",
"nodejs": """package-lock.json
yarn.lock
.env
.env.local
""",
"go": """*.out
*.test
coverage.txt
""",
"rust": """**/*.rs.bk
Cargo.lock
""",
}
def __init__(self):
self.templates_dir = self._get_templates_dir()
def _get_templates_dir(self) -> Path:
"""Get the directory containing gitignore templates."""
return Path(__file__).parent / "templates" / "gitignore"
def generate(
self, language: str, output_path: Path, extra_patterns: Optional[Set[str]] = None
) -> None:
"""Generate a .gitignore file for the specified language."""
if language not in self.SUPPORTED_LANGUAGES:
raise ValueError(
f"Unsupported language: {language}. "
f"Supported: {', '.join(self.SUPPORTED_LANGUAGES)}"
)
content = self.GITIGNORE_TEMPLATES.get(language, "")
extra = self.ADDITIONAL_PATTERNS.get(language, "")
if extra:
content += extra
if extra_patterns:
content += "\n".join(sorted(extra_patterns)) + "\n"
content += "\n# Editor directories\n.idea/\n.vscode/\n*.swp\n*.swo\n*~\n"
output_path.write_text(content)
def generate_from_template(self, template_name: str, output_path: Path) -> None:
"""Generate .gitignore from a template file."""
template_path = self.templates_dir / template_name
if not template_path.exists():
raise FileNotFoundError(
f"Gitignore template not found: {template_path}"
)
content = template_path.read_text()
output_path.write_text(content)
def list_available_templates(self) -> list[str]:
"""List available gitignore templates."""
if not self.templates_dir.exists():
return []
return [
f.stem
for f in self.templates_dir.iterdir()
if f.is_file() and f.suffix in (".gitignore", ".txt", "")
]
def get_template_content(self, language: str) -> str:
"""Get the raw template content for a language."""
return self.GITIGNORE_TEMPLATES.get(language, "")
def append_patterns(self, gitignore_path: Path, patterns: Set[str]) -> None:
"""Append additional patterns to an existing .gitignore file."""
if gitignore_path.exists():
content = gitignore_path.read_text()
if not content.endswith("\n"):
content += "\n"
else:
content = ""
content += "\n" + "\n".join(sorted(patterns)) + "\n"
gitignore_path.write_text(content)