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

155 lines
4.9 KiB
Python

"""Configuration handling for Project Scaffold CLI."""
import os
from pathlib import Path
from typing import Any, Dict, List, Optional
import yaml
class Config:
"""Configuration management for project scaffold CLI."""
def __init__(
self,
author: Optional[str] = None,
email: Optional[str] = None,
license: Optional[str] = None,
description: Optional[str] = None,
default_language: Optional[str] = None,
default_ci: Optional[str] = None,
default_template: Optional[str] = None,
template_vars: Optional[Dict[str, Any]] = None,
custom_templates_dir: Optional[str] = None,
config_path: Optional[str] = None,
):
self.author = author
self.email = email
self.license = license
self.description = description
self.default_language = default_language
self.default_ci = default_ci
self.default_template = default_template
self.template_vars = template_vars or {}
self.custom_templates_dir = custom_templates_dir
self.config_path = config_path
@classmethod
def load(cls, config_path: Optional[str] = None) -> "Config":
"""Load configuration from file."""
if config_path:
config_file = Path(config_path)
if config_file.exists():
return cls._from_file(config_file)
search_paths = [
Path("project.yaml"),
Path(".project-scaffoldrc"),
Path.cwd() / "project.yaml",
Path.cwd() / ".project-scaffoldrc",
]
for path in search_paths:
if path.exists():
return cls._from_file(path)
home_config = Path.home() / ".config" / "project-scaffold" / "config.yaml"
if home_config.exists():
return cls._from_file(home_config)
return cls()
@classmethod
def _from_file(cls, path: Path) -> "Config":
"""Load configuration from a YAML file."""
try:
with open(path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f) or {}
except yaml.YAMLError as e:
raise ValueError(f"Invalid YAML in {path}: {e}")
project_config = data.get("project", {})
defaults = data.get("defaults", {})
template_vars = data.get("template_vars", {})
template_paths = data.get("template_paths", [])
custom_templates_dir = data.get("custom_templates_dir")
return cls(
author=project_config.get("author"),
email=project_config.get("email"),
license=project_config.get("license"),
description=project_config.get("description"),
default_language=defaults.get("language"),
default_ci=defaults.get("ci"),
default_template=defaults.get("template"),
template_vars=template_vars,
custom_templates_dir=custom_templates_dir,
config_path=str(path),
)
def save(self, path: Path) -> None:
"""Save configuration to a YAML file."""
data = {
"project": {
"author": self.author,
"email": self.email,
"license": self.license,
"description": self.description,
},
"defaults": {
"language": self.default_language,
"ci": self.default_ci,
"template": self.default_template,
},
"template_vars": self.template_vars,
}
if self.custom_templates_dir:
data["custom_templates_dir"] = self.custom_templates_dir
with open(path, "w", encoding="utf-8") as f:
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
def get_template_dirs(self) -> List[str]:
"""Get list of template directories to search."""
dirs = []
if self.custom_templates_dir:
dirs.append(self.custom_templates_dir)
home_templates = (
Path.home()
/ ".local"
/ "share"
/ "project-scaffold"
/ "templates"
)
dirs.append(str(home_templates))
return dirs
def get_custom_templates_dir(self) -> str:
"""Get the directory for custom templates."""
if self.custom_templates_dir:
return self.custom_templates_dir
custom_dir = (
Path.home() / ".config" / "project-scaffold" / "templates"
)
custom_dir.mkdir(parents=True, exist_ok=True)
return str(custom_dir)
def get_template_vars(self, language: str) -> Dict[str, Any]:
"""Get template variables for a specific language."""
return self.template_vars.get(language, {})
@property
def ci(self) -> Optional[str]:
"""Get default CI provider."""
return self.default_ci
@property
def template(self) -> Optional[str]:
"""Get default template."""
return self.default_template