Source code for roller.config
"""
Configuration file parsing for changes-roller.
"""
import configparser
from dataclasses import dataclass
from pathlib import Path
@dataclass
[docs]
class SeriesConfig:
"""Configuration for a patch series."""
[docs]
run_tests: bool = False
[docs]
tests_blocking: bool = False
# Branch switching options
[docs]
branch: str | None = None
[docs]
create_branch: bool = False
[docs]
stay_on_branch: bool = False
# Command execution options
[docs]
pre_commands: list[str] = None # type: ignore
[docs]
post_commands: list[str] = None # type: ignore
[docs]
continue_on_error: bool = False
def __post_init__(self) -> None:
"""Initialize mutable default values."""
if self.pre_commands is None:
self.pre_commands = []
if self.post_commands is None:
self.post_commands = []
[docs]
class ConfigParser:
"""Parse INI configuration files for patch series."""
def __init__(self, config_path: Path):
[docs]
self.config_path = config_path
self._parser = configparser.ConfigParser()
[docs]
def parse(self) -> SeriesConfig:
"""Parse the configuration file and return SeriesConfig."""
if not self.config_path.exists():
raise FileNotFoundError(f"Configuration file not found: {self.config_path}")
self._parser.read(self.config_path)
# Parse SERIE section
if "SERIE" not in self._parser:
raise ValueError("Configuration file must contain [SERIE] section")
serie = self._parser["SERIE"]
# Parse project list (comma-separated, possibly multi-line)
projects_str = serie.get("projects", "")
projects = [p.strip() for p in projects_str.split(",") if p.strip()]
if not projects:
raise ValueError("Configuration must specify at least one project")
commands = serie.get("commands", "")
if not commands:
raise ValueError("Configuration must specify commands path")
commit_msg = serie.get("commit_msg", "")
if not commit_msg:
raise ValueError("Configuration must specify commit_msg")
# Parse optional fields
topic = serie.get("topic", "")
commit = serie.getboolean("commit", fallback=True)
review = serie.getboolean("review", fallback=False)
# Parse TESTS section if it exists
run_tests = False
tests_blocking = False
test_command = ""
if "TESTS" in self._parser:
tests = self._parser["TESTS"]
run_tests = tests.getboolean("run", fallback=False)
tests_blocking = tests.getboolean("blocking", fallback=False)
test_command = tests.get("command", "tox")
# Parse branch switching options
branch = serie.get("branch", None)
create_branch = serie.getboolean("create_branch", fallback=False)
stay_on_branch = serie.getboolean("stay_on_branch", fallback=False)
# Parse command execution options
pre_commands_str = serie.get("pre_commands", "")
pre_commands = [
cmd.strip() for cmd in pre_commands_str.split("\n") if cmd.strip()
]
post_commands_str = serie.get("post_commands", "")
post_commands = [
cmd.strip() for cmd in post_commands_str.split("\n") if cmd.strip()
]
continue_on_error = serie.getboolean("continue_on_error", fallback=False)
dry_run = serie.getboolean("dry_run", fallback=False)
return SeriesConfig(
projects=projects,
commands=commands,
commit_msg=commit_msg,
topic=topic,
commit=commit,
review=review,
run_tests=run_tests,
tests_blocking=tests_blocking,
test_command=test_command,
branch=branch,
create_branch=create_branch,
stay_on_branch=stay_on_branch,
pre_commands=pre_commands,
post_commands=post_commands,
continue_on_error=continue_on_error,
dry_run=dry_run,
)