Skip to content

Commit 9bfac84

Browse files
ctruedenclaude
andcommitted
Implement the status subcommand's config section
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0835c0d commit 9bfac84

4 files changed

Lines changed: 105 additions & 12 deletions

File tree

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,20 @@ skip-tests = ["org.example:legacy-lib"]
167167
java-version = 11
168168
template = "tests/mega-melt-template.xml"
169169
excludes = ["org.example:problematic-artifact"]
170+
171+
[status]
172+
# Settings for the status command.
173+
rules = "rules.xml"
174+
projects = "projects.txt"
175+
badges = "ci-badges.txt"
176+
timestamps = "timestamps.txt"
177+
html = "index.html"
178+
header = "header.html"
179+
footer = "footer.html"
170180
```
171181

172-
Pass it with `--config pombast.toml`.
182+
If `pombast.toml` exists in the current directory it is loaded automatically.
183+
Pass `--config PATH` to use a different file.
173184

174185
---
175186

src/pombast/cli/_status.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
)
1818
from rich.table import Table
1919

20+
from pombast.config._settings import PombastConfig
2021
from pombast.core._filter import ComponentFilter
2122
from pombast.maven._bom import load_bom
2223
from pombast.maven._rules import RulesXML
@@ -55,6 +56,12 @@
5556
multiple=True,
5657
help="Additional remote Maven repository URL (repeatable). Optionally prefix with a name: name=URL.",
5758
)
59+
@click.option(
60+
"--config",
61+
type=click.Path(exists=True, path_type=Path),
62+
default=None,
63+
help="Path to pombast.toml configuration file.",
64+
)
5865
@click.option(
5966
"--rules",
6067
default=None,
@@ -130,6 +137,7 @@ def status_cmd(
130137
include: tuple[str, ...],
131138
exclude: tuple[str, ...],
132139
repository: tuple[str, ...],
140+
config: Path | None,
133141
rules: str | None,
134142
projects: str | None,
135143
badges: str | None,
@@ -153,6 +161,14 @@ def status_cmd(
153161
format="%(levelname)s %(name)s: %(message)s",
154162
)
155163

164+
sc = PombastConfig.load_default(config).status
165+
effective_rules = rules or (str(sc.rules) if sc.rules else None)
166+
effective_projects = projects or (str(sc.projects) if sc.projects else None)
167+
effective_badges = badges or (str(sc.badges) if sc.badges else None)
168+
effective_timestamps = timestamps or (str(sc.timestamps) if sc.timestamps else None)
169+
effective_html = html_path or sc.html
170+
effective_header = header or sc.header
171+
effective_footer = footer or sc.footer
156172
effective_max_age = 0 if refresh else max_age
157173

158174
console.print(f"[bold]BOM:[/bold] [cyan]{bom}[/cyan]")
@@ -174,15 +190,17 @@ def status_cmd(
174190
f"(use [bold]--refresh[/bold] to bypass)"
175191
)
176192

177-
if rules:
178-
console.print(f"Loading rules: [cyan]{rules}[/cyan]")
179-
rules_xml = RulesXML.load(rules)
193+
if effective_rules:
194+
console.print(f"Loading rules: [cyan]{effective_rules}[/cyan]")
195+
rules_xml = RulesXML.load(effective_rules)
180196
else:
181197
rules_xml = RulesXML.empty()
182198

183-
proj_ov = load_kv_file(projects) if projects else {}
184-
badge_ov = load_kv_file(badges) if badges else {}
185-
vetting_ov = load_timestamps_file(timestamps) if timestamps else {}
199+
proj_ov = load_kv_file(effective_projects) if effective_projects else {}
200+
badge_ov = load_kv_file(effective_badges) if effective_badges else {}
201+
vetting_ov = (
202+
load_timestamps_file(effective_timestamps) if effective_timestamps else {}
203+
)
186204

187205
cf = ComponentFilter(includes=list(include), excludes=list(exclude))
188206
total = len(cf.filter(bom_data.components))
@@ -226,18 +244,18 @@ def status_cmd(
226244
f"[dim]None {len(entries) - cuts - bumps}[/dim]"
227245
)
228246

229-
if html_path:
230-
header_html = header.read_text() if header else ""
231-
footer_html = footer.read_text() if footer else ""
232-
html_path.write_text(
247+
if effective_html:
248+
header_html = effective_header.read_text() if effective_header else ""
249+
footer_html = effective_footer.read_text() if effective_footer else ""
250+
effective_html.write_text(
233251
generate_html(
234252
entries,
235253
nexus_base=nexus_base or "",
236254
header_html=header_html,
237255
footer_html=footer_html,
238256
)
239257
)
240-
console.print(f"HTML report written to: [cyan]{html_path}[/cyan]")
258+
console.print(f"HTML report written to: [cyan]{effective_html}[/cyan]")
241259

242260

243261
def _print_status_table(entries: list[StatusEntry]) -> None:

src/pombast/config/_settings.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ class FilterConfig:
2020
excludes: list[str] = field(default_factory=list)
2121

2222

23+
@dataclass
24+
class StatusConfig:
25+
"""Configuration for the status command."""
26+
27+
rules: Path | None = None
28+
projects: Path | None = None
29+
badges: Path | None = None
30+
timestamps: Path | None = None
31+
html: Path | None = None
32+
header: Path | None = None
33+
footer: Path | None = None
34+
35+
2336
@dataclass
2437
class MegaMeltConfig:
2538
"""Configuration for the mega-melt BOM validation phase."""
@@ -41,6 +54,7 @@ class PombastConfig:
4154
build_properties: dict[str, str] = field(default_factory=dict)
4255
component_overrides: dict[str, dict[str, object]] = field(default_factory=dict)
4356
mega_melt: MegaMeltConfig = field(default_factory=MegaMeltConfig)
57+
status: StatusConfig = field(default_factory=StatusConfig)
4458

4559
@classmethod
4660
def load(cls, path: Path) -> PombastConfig:
@@ -70,6 +84,21 @@ def load(cls, path: Path) -> PombastConfig:
7084
),
7185
)
7286

87+
def resolve(section: dict, key: str) -> Path | None:
88+
s = section.get(key)
89+
return (path.parent / s).resolve() if s else None
90+
91+
status_data = data.get("status", {})
92+
status_config = StatusConfig(
93+
rules=resolve(status_data, "rules"),
94+
projects=resolve(status_data, "projects"),
95+
badges=resolve(status_data, "badges"),
96+
timestamps=resolve(status_data, "timestamps"),
97+
html=resolve(status_data, "html"),
98+
header=resolve(status_data, "header"),
99+
footer=resolve(status_data, "footer"),
100+
)
101+
73102
return cls(
74103
filter=filter_config,
75104
default_java=int(default_java) if default_java is not None else None,
@@ -79,6 +108,7 @@ def load(cls, path: Path) -> PombastConfig:
79108
build_properties=common_data.get("properties", {}),
80109
component_overrides={k: v for k, v in data.get("components", {}).items()},
81110
mega_melt=mega_melt_config,
111+
status=status_config,
82112
)
83113

84114
@classmethod

tests/unit/test_settings.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,40 @@ def test_falls_back_to_empty_when_no_file(self, tmp_path, monkeypatch):
9090
assert config.skip_tests == []
9191

9292

93+
class TestStatusConfig:
94+
def test_defaults(self):
95+
config = PombastConfig.empty()
96+
assert config.status.rules is None
97+
assert config.status.html is None
98+
99+
def test_load_status_section(self, tmp_path):
100+
(tmp_path / "rules.xml").write_text("<rules/>")
101+
(tmp_path / "projects.txt").write_text("")
102+
(tmp_path / "badges.txt").write_text("")
103+
(tmp_path / "timestamps.txt").write_text("")
104+
(tmp_path / "header.html").write_text("")
105+
(tmp_path / "footer.html").write_text("")
106+
toml_path = tmp_path / "pombast.toml"
107+
toml_path.write_text("""\
108+
[status]
109+
rules = "rules.xml"
110+
projects = "projects.txt"
111+
badges = "badges.txt"
112+
timestamps = "timestamps.txt"
113+
html = "index.html"
114+
header = "header.html"
115+
footer = "footer.html"
116+
""")
117+
config = PombastConfig.load(toml_path)
118+
assert config.status.rules == (tmp_path / "rules.xml").resolve()
119+
assert config.status.projects == (tmp_path / "projects.txt").resolve()
120+
assert config.status.badges == (tmp_path / "badges.txt").resolve()
121+
assert config.status.timestamps == (tmp_path / "timestamps.txt").resolve()
122+
assert config.status.html == (tmp_path / "index.html").resolve()
123+
assert config.status.header == (tmp_path / "header.html").resolve()
124+
assert config.status.footer == (tmp_path / "footer.html").resolve()
125+
126+
93127
class TestMegaMeltConfig:
94128
def test_defaults(self):
95129
config = PombastConfig.empty()

0 commit comments

Comments
 (0)