Files
local-commit-message-generator/src/config.py
7000pctAUTO 6a18cc19d9
Some checks failed
CI / build (push) Has been cancelled
CI / test (push) Has been cancelled
fix: Add Gitea Actions CI workflow and fix linting issues
2026-02-04 16:59:22 +00:00

125 lines
3.7 KiB
Python

"""Configuration management for local-commit-message-generator."""
from pathlib import Path
from typing import Any, Dict, Optional
import tomlkit
class ConfigError(Exception):
"""Raised when configuration errors occur."""
pass
DEFAULT_TYPE_RULES: Dict[str, list[str]] = {
"feat": ["src/", "lib/", "app/", "controllers/", "models/"],
"fix": ["src/", "lib/", "bug", "fix", "issue", "hotfix"],
"docs": [".md", ".rst", "docs/", "documentation/"],
"style": [".css", ".scss", ".sass", ".less", "styles/"],
"refactor": ["refactor/", "rewrite/", "restructure/"],
"test": ["test/", "tests/", "__tests__/", ".test.", ".spec."],
"chore": ["package.json", "pyproject.toml", "requirements", ".gitignore", "Makefile"],
"perf": ["performance/", "perf/", "optimize/", "optimization/"],
"ci": [".github/", ".gitlab-ci.yml", ".travis.yml", "Jenkinsfile", "tox.ini"],
"build": ["build/", "webpack/", "vite.config", "babel.config", "rollup.config"],
}
DEFAULT_CONFIG: Dict[str, Any] = {
"type_rules": DEFAULT_TYPE_RULES,
"template": "{type}{scope}: {description}",
"scopes": {},
"description_length": 72,
"max_files": 5,
"include_file_list": True,
"file_list_template": "\n\nFiles changed:\n{files}",
}
def get_config_path() -> Path:
"""Get the path to the user configuration file."""
home = Path.home()
return home / ".local_commit_gen.toml"
def load_config(config_path: Optional[Path] = None) -> Dict[str, Any]:
"""Load configuration from file.
Args:
config_path: Optional path to config file. If not provided, uses default path.
Returns:
Dictionary containing configuration.
"""
if config_path is None:
config_path = get_config_path()
if not config_path.exists():
return DEFAULT_CONFIG.copy()
try:
with open(config_path, "r") as f:
config = tomlkit.parse(f.read())
except tomlkit.exceptions.ParseError as e:
raise ConfigError(f"Invalid TOML syntax in config file: {e}")
merged = DEFAULT_CONFIG.copy()
for key, value in config.items():
if key == "type_rules" and isinstance(value, dict):
merged["type_rules"] = {**DEFAULT_TYPE_RULES, **value}
else:
merged[key] = value
return merged
def save_config(config: Dict[str, Any], config_path: Optional[Path] = None) -> None:
"""Save configuration to file.
Args:
config: Configuration dictionary to save.
config_path: Optional path to config file. If not provided, uses default path.
"""
if config_path is None:
config_path = get_config_path()
try:
with open(config_path, "w") as f:
tomlkit.dump(config, f)
except OSError as e:
raise ConfigError(f"Failed to write config file: {e}")
def ensure_config_exists() -> None:
"""Ensure default config file exists."""
config_path = get_config_path()
if not config_path.exists():
save_config(DEFAULT_CONFIG.copy())
def get_type_rules(config: Optional[Dict[str, Any]] = None) -> Dict[str, list[str]]:
"""Get type rules from configuration.
Args:
config: Optional configuration dictionary. If not provided, loads from file.
Returns:
Dictionary mapping commit types to patterns.
"""
if config is None:
config = load_config()
return config.get("type_rules", DEFAULT_TYPE_RULES)
def get_template(config: Optional[Dict[str, Any]] = None) -> str:
"""Get message template from configuration.
Args:
config: Optional configuration dictionary. If not provided, loads from file.
Returns:
Message template string.
"""
if config is None:
config = load_config()
return config.get("template", DEFAULT_CONFIG["template"])