fix(registry): quote keys, dedupe tags, and document sourcing order; CI commits regenerated registry

- Quote both keys and values in the generated __fish_config_op_registry.fish
  keys array so a future identity/tag containing a glob-special character
  (*, ?, [) can't be silently dropped by fish's set, desyncing the
  positionally-paired keys/values arrays.
- Deduplicate the tag list per site in build_registry() so an identity
  collision across sources (e.g. auto-pull tagged autoexec/sync in both
  functions/auto-pull.fish and conf.d/auto-pull.fish) no longer produces a
  duplicated tag in the committed registry.
- Add a comment to the generated file's header noting it must sort first
  among conf.d/*.fish guard-callers, since that ordering is currently
  implicit in the filename rather than stated anywhere.
- CI's auto-commit step now also stages conf.d/__fish_config_op_registry.fish
  alongside the docs it already regenerates, so a # COMPONENT header change
  pushed without a manual __fish_config_op_registry_rebuild self-heals
  instead of silently drifting from what CI just regenerated.
This commit is contained in:
2026-08-18 18:12:49 -04:00
parent a8468f3402
commit 1cb6f37b5b
3 changed files with 81 additions and 68 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ jobs:
run: | run: |
git config user.name "Gitea Actions" git config user.name "Gitea Actions"
git config user.email "actions@gitea" git config user.email "actions@gitea"
git add docs/fish-config.md docs/fish-config.1 git add docs/fish-config.md docs/fish-config.1 conf.d/__fish_config_op_registry.fish
git diff --cached --quiet && echo "No changes to commit" && exit 0 git diff --cached --quiet && echo "No changes to commit" && exit 0
git commit -m "chore(docs): regenerate manual and man page" git commit -m "chore(docs): regenerate manual and man page"
git push git push
+71 -65
View File
@@ -5,72 +5,78 @@
# Regenerate with __fish_config_op_registry_rebuild after editing a # Regenerate with __fish_config_op_registry_rebuild after editing a
# # COMPONENT header, or automatically via docs/build-manual.py. # # COMPONENT header, or automatically via docs/build-manual.py.
# Source: docs/generate_component_registry.py # Source: docs/generate_component_registry.py
#
# This file must be sourced before any other conf.d/*.fish file that
# calls the opinionated guard. That currently holds only because fish's
# glob-based conf.d loading happens to sort this filename first
# alphabetically among the guard-calling files -- do not rename it
# without preserving that ordering.
set -g __fish_config_op_registry_keys \ set -g __fish_config_op_registry_keys \
__auto_source_fallback_venv: \ "__auto_source_fallback_venv:" \
__fish_config_sync_logging: \ "__fish_config_sync_logging:" \
__fish_user_dots_link: \ "__fish_user_dots_link:" \
_zellij_dump_log: \ "_zellij_dump_log:" \
abbr:abbr-integrations \ "abbr:abbr-integrations" \
abbr:abbr-overrides \ "abbr:abbr-overrides" \
agy: \ "agy:" \
auto-pull: \ "auto-pull:" \
autopair: \ "autopair:" \
bash: \ "bash:" \
bash_expands: \ "bash_expands:" \
cat: \ "cat:" \
claude: \ "claude:" \
config:cachyos-strip-aliases \ "config:cachyos-strip-aliases" \
config:cachyos-strip-overrides \ "config:cachyos-strip-overrides" \
config:cachyos-tricks \ "config:cachyos-tricks" \
config:cdpath \ "config:cdpath" \
config:exit-wiring \ "config:exit-wiring" \
config:greeting-stamp \ "config:greeting-stamp" \
config:pager-editor-gpg \ "config:pager-editor-gpg" \
config:path-setup \ "config:path-setup" \
config:vi-mode \ "config:vi-mode" \
done: \ "done:" \
du: \ "du:" \
edit: \ "edit:" \
first_run:first-run-bootstrap \ "first_run:first-run-bootstrap" \
first_run:first-run-greeting \ "first_run:first-run-greeting" \
fish_right_prompt: \ "fish_right_prompt:" \
help: \ "help:" \
hist: \ "hist:" \
key_bindings: \ "key_bindings:" \
kitty-logging: \ "kitty-logging:" \
kitty-watcher-reminder: \ "kitty-watcher-reminder:" \
less: \ "less:" \
logs: \ "logs:" \
ls: \ "ls:" \
mkdir: \ "mkdir:" \
mv: \ "mv:" \
paru-wrapper:paru-autoexec \ "paru-wrapper:paru-autoexec" \
paru-wrapper:paru-logging \ "paru-wrapper:paru-logging" \
ping: \ "ping:" \
puffer: \ "puffer:" \
rg: \ "rg:" \
rm: \ "rm:" \
smart_exit:exit-plain \ "smart_exit:exit-plain" \
smart_exit:logging-guard \ "smart_exit:logging-guard" \
split: \ "split:" \
spwin: \ "spwin:" \
ssh: \ "ssh:" \
starship: \ "starship:" \
tab: \ "tab:" \
theme: \ "theme:" \
tmux-logging: \ "tmux-logging:" \
top: \ "top:" \
tricks:aliases-tricks \ "tricks:aliases-tricks" \
tricks:tricks-bang \ "tricks:tricks-bang" \
tricks:tricks-manpager \ "tricks:tricks-manpager" \
upgrade: \ "upgrade:" \
wakatime:wakatime-autoexec \ "wakatime:wakatime-autoexec" \
wakatime:wakatime-hook \ "wakatime:wakatime-hook" \
yay-wrapper:yay-autoexec \ "yay-wrapper:yay-autoexec" \
yay-wrapper:yay-logging \ "yay-wrapper:yay-logging" \
yt-dlp: \ "yt-dlp:" \
zoxide: "zoxide:"
set -g __fish_config_op_registry_values \ set -g __fish_config_op_registry_values \
"autoexec/venv" \ "autoexec/venv" \
@@ -80,7 +86,7 @@ set -g __fish_config_op_registry_values \
"integrations/terminal-abbrs" \ "integrations/terminal-abbrs" \
"overrides/key-bindings" \ "overrides/key-bindings" \
"aliases/dev-tools" \ "aliases/dev-tools" \
"autoexec/sync autoexec/sync" \ "autoexec/sync" \
"overrides/key-bindings" \ "overrides/key-bindings" \
"aliases/shell-tools" \ "aliases/shell-tools" \
"overrides/key-bindings" \ "overrides/key-bindings" \
+9 -2
View File
@@ -67,7 +67,7 @@ def build_registry(components: dict[str, list[str]]) -> tuple[dict[str, list[str
) )
tags = [t for t in tags if t not in ("always/on", "always/off")] tags = [t for t in tags if t not in ("always/on", "always/off")]
if tags: if tags:
registry[f"{identity}:{site}"] = tags registry[f"{identity}:{site}"] = list(dict.fromkeys(tags))
return registry, warnings return registry, warnings
@@ -81,6 +81,12 @@ def render(registry: dict[str, list[str]]) -> str:
"# Regenerate with __fish_config_op_registry_rebuild after editing a", "# Regenerate with __fish_config_op_registry_rebuild after editing a",
"# # COMPONENT header, or automatically via docs/build-manual.py.", "# # COMPONENT header, or automatically via docs/build-manual.py.",
"# Source: docs/generate_component_registry.py", "# Source: docs/generate_component_registry.py",
"#",
"# This file must be sourced before any other conf.d/*.fish file that",
"# calls the opinionated guard. That currently holds only because fish's",
"# glob-based conf.d loading happens to sort this filename first",
"# alphabetically among the guard-calling files -- do not rename it",
"# without preserving that ordering.",
"", "",
] ]
if not keys: if not keys:
@@ -88,8 +94,9 @@ def render(registry: dict[str, list[str]]) -> str:
lines.append("set -g __fish_config_op_registry_values") lines.append("set -g __fish_config_op_registry_values")
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
quoted_keys = [f'"{k}"' for k in keys]
lines.append("set -g __fish_config_op_registry_keys \\") lines.append("set -g __fish_config_op_registry_keys \\")
lines += [f" {k} \\" for k in keys[:-1]] + [f" {keys[-1]}"] lines += [f" {k} \\" for k in quoted_keys[:-1]] + [f" {quoted_keys[-1]}"]
lines.append("") lines.append("")
values = ['"' + " ".join(registry[k]) + '"' for k in keys] values = ['"' + " ".join(registry[k]) + '"' for k in keys]