aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-10 11:06:57 +0200
committerDanilo M. <danix@danix.xyz>2026-09-10 11:06:57 +0200
commitfb45dbc03650bd9ed7f5629fd01fffdaa951f109 (patch)
treefa36ecfe0f0cbb6d939fbf076eff3e76f4f65041
parenta5bf22b9eefa32322b2587ac930e5680d97500be (diff)
downloadabusectl-fb45dbc03650bd9ed7f5629fd01fffdaa951f109.tar.gz
abusectl-fb45dbc03650bd9ed7f5629fd01fffdaa951f109.zip
fix: keep the trailing comment at the end of the config
The "Later parts add further sections here" block was emitted by build(), so preserved sections were appended below it and it became a comment about nothing, pointing at a table already underneath it. It is now a FOOTER constant that build() still appends, so a lone build() reads as a complete file, and write() strips before adding preserved text and puts back at the end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LByBnw83xr9YP85nskzkyE
-rw-r--r--abusectl/init.py26
-rw-r--r--tests/test_init.py17
2 files changed, 33 insertions, 10 deletions
diff --git a/abusectl/init.py b/abusectl/init.py
index 0e185c0..c702bc7 100644
--- a/abusectl/init.py
+++ b/abusectl/init.py
@@ -166,6 +166,19 @@ _REPORTER_FIELDS = (
)
+# Emitted LAST, after any preserved section, so it does not end up sitting
+# above a table it says will be added below it. build() appends it so a
+# lone build() still reads as a complete file; write() strips it before
+# adding preserved text and puts it back at the end.
+FOOTER = "\n".join([
+ "",
+ "# Later parts of abusectl add further sections here:",
+ "# [misp] - MISP instance URL and API key",
+ "# [vendors] - per-vendor threat-intel API keys",
+ "# [reporting] - abuse-desk reporting defaults",
+]) + "\n"
+
+
def build(answers: dict) -> str:
"""Render answers as config TOML for the [general] and [reporter] tables."""
relays = _validate_relays(answers.get("trusted_relays", []))
@@ -212,15 +225,7 @@ def build(answers: dict) -> str:
])
lines.extend(reporter)
- lines.extend([
- "",
- "# Later parts of abusectl add further sections here:",
- "# [misp] - MISP instance URL and API key",
- "# [vendors] - per-vendor threat-intel API keys",
- "# [reporting] - abuse-desk reporting defaults",
- ])
-
- return "\n".join(lines) + "\n"
+ return "\n".join(lines) + "\n" + FOOTER
def existing_summary(path: pathlib.Path) -> str:
@@ -349,7 +354,8 @@ def write(path: pathlib.Path, answers: dict, force: bool = False) -> pathlib.Pat
# set by hand instead of silently deleting it.
preserved = _preserved_sections(path, _rendered_sections(text))
if preserved:
- text = text.rstrip("\n") + "\n\n" + preserved + "\n"
+ body = text[: -len(FOOTER)] if text.endswith(FOOTER) else text
+ text = body.rstrip("\n") + "\n\n" + preserved + "\n" + FOOTER
back_up(path)
diff --git a/tests/test_init.py b/tests/test_init.py
index 55153f4..f1b20f8 100644
--- a/tests/test_init.py
+++ b/tests/test_init.py
@@ -369,3 +369,20 @@ class TestRepeatedInit(unittest.TestCase):
self.assertEqual(
tomllib.loads(text)["reporter"]["name"], "A Reporter"
)
+
+ def test_the_footer_stays_at_the_end_after_a_preserved_section(self):
+ # It says further sections are added below it, so a preserved table
+ # appended underneath made it a comment about nothing.
+ relays = {"trusted_relays": ["192.0.2.0/24"], "cases": ""}
+ answered = dict(relays, reporter_name="A Reporter",
+ reporter_email="r@example.org")
+ with tempfile.TemporaryDirectory() as tmp:
+ path = pathlib.Path(tmp) / "config.toml"
+ init.write(path, answered)
+ init.write(path, relays, force=True)
+
+ text = path.read_text().rstrip("\n")
+ self.assertTrue(
+ text.endswith("# [reporting] - abuse-desk reporting defaults"),
+ f"footer is not last:\n{text}",
+ )