fix(docs): merge, not overwrite, COMPONENT lines on identity collision

collect_components() used dict.update(), which let conf.d silently
overwrite functions/ (or vice versa) when the same bare identity
appears in both, e.g. functions/auto-pull.fish and
conf.d/auto-pull.fish. The runtime guard can only ever look up the
bare status current-function/basename string, so both call sites
genuinely share one identity and their raw COMPONENT lines must be
concatenated, not replaced.
This commit is contained in:
2026-08-17 20:42:04 -04:00
parent c0628dbd4c
commit 9d6923f225
2 changed files with 53 additions and 4 deletions
+17 -4
View File
@@ -23,10 +23,23 @@ OUTPUT = REPO / "conf.d" / "__fish_config_op_registry.fish"
def collect_components() -> dict[str, list[str]]:
"""Gather every `# COMPONENT` header across the whole repo."""
out = mt.parse_components(REPO / "functions")
out.update(mt.parse_components(REPO / "conf.d"))
out.update(mt.parse_component_file(REPO / "config.fish"))
"""Gather every `# COMPONENT` header across the whole repo.
Concatenates raw component lines when the same identity appears in
more than one source (e.g. functions/auto-pull.fish and
conf.d/auto-pull.fish both self-identify as "auto-pull" at runtime,
since the guard can only ever look up the bare status
current-function/basename string) rather than letting one silently
overwrite the other.
"""
out: dict[str, list[str]] = {}
for source in (
mt.parse_components(REPO / "functions"),
mt.parse_components(REPO / "conf.d"),
mt.parse_component_file(REPO / "config.fish"),
):
for identity, lines in source.items():
out.setdefault(identity, []).extend(lines)
return out
+36
View File
@@ -938,6 +938,42 @@ def test_build_registry_keeps_sites_independent():
assert not warnings
def test_collect_components_merges_identity_collisions_across_sources():
"""functions/auto-pull.fish and conf.d/auto-pull.fish both self-identify
as "auto-pull" at runtime -- the guard only ever has the bare
status current-function/basename string to look up with -- so
collect_components must concatenate their raw COMPONENT lines
rather than letting conf.d's entry silently overwrite functions'."""
import generate_component_registry as gcr
with tempfile.TemporaryDirectory() as d:
root = Path(d)
(root / "functions").mkdir()
(root / "conf.d").mkdir()
(root / "functions" / "auto-pull.fish").write_text(
"# COMPONENT\n"
"# autoexec/sync\n"
"function auto-pull\n"
"end\n"
)
(root / "conf.d" / "auto-pull.fish").write_text(
"# COMPONENT\n"
"# autoexec/sync\n"
)
(root / "config.fish").write_text("")
orig_repo = gcr.REPO
try:
gcr.REPO = root
got = gcr.collect_components()
finally:
gcr.REPO = orig_repo
assert got["auto-pull"] == ["autoexec/sync", "autoexec/sync"], (
f"both sources' tags should survive the merge, not overwrite: {got}"
)
def test_render_registry_is_valid_fish_and_round_trips():
"""Sourcing render()'s output must leave the two arrays in the exact
shape __fish_config_op_registry_lookup expects -- checked via the real