From 6faa6151355acd014aae81b87cb409ccf87bc2ee Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Thu, 29 Oct 2026 08:07:11 +0100 Subject: [PATCH] fix(i18n): keep pulled custom roles out of the tools-lane deploy and the core catalogs Two store-i18n-only code paths predate updater's custom roles (web-app-cst-*, d92375922d), which the merge c9e7f950d9 brought in: - utils/i18n/libretranslate.py deploys the translation tools lane with cli.administration.deploy.dedicated but without --skip-pull. The lane bind-mounts the checkout and its generated inventory declares no custom role, so `make i18n-translate` would have run pull_custom_roles and removed every pulled web-app-cst-* from the host's roles/. It now passes --skip-pull, as every other generated deploy does. - utils/i18n/extract.py role_messages globbed roles/*/meta/main.yml, including the gitignored custom roles, so a checkout with a pulled custom role failed test_core_catalogs_hold_exactly_the_current_sources and its remedy would have written the custom role's description into every tracked core catalog, against updater's rule that core never tracks custom roles. It now skips is_custom(role), like the README generators. test_a_pulled_custom_role_stays_out_of_the_core_catalogs pins the second point; without the skip it fails with the custom role in the list. Co-Authored-By: Claude Opus 5.5 --- tests/unit/python/utils/i18n/test_extract.py | 20 +++++++++++++++++++- utils/i18n/extract.py | 5 ++++- utils/i18n/libretranslate.py | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/unit/python/utils/i18n/test_extract.py b/tests/unit/python/utils/i18n/test_extract.py index fe803e25a0..820b4c38a6 100644 --- a/tests/unit/python/utils/i18n/test_extract.py +++ b/tests/unit/python/utils/i18n/test_extract.py @@ -3,7 +3,7 @@ import textwrap import unittest from pathlib import Path -from utils.i18n.extract import core_messages +from utils.i18n.extract import core_messages, role_messages from utils.roles.mapping import ROLE_FILE_META_MAIN @@ -75,6 +75,24 @@ class TestCoreMessages(unittest.TestCase): ], ) + def test_a_pulled_custom_role_stays_out_of_the_core_catalogs(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + _write( + root, + f"roles/web-app-wiki/{ROLE_FILE_META_MAIN}", + "galaxy_info:\n description: A wiki\n", + ) + _write( + root, + f"roles/web-app-cst-demo/{ROLE_FILE_META_MAIN}", + "galaxy_info:\n description: A custom app\n", + ) + + messages = role_messages(root) + + self.assertEqual(messages, [("role:web-app-wiki:description", "A wiki")]) + if __name__ == "__main__": unittest.main() diff --git a/utils/i18n/extract.py b/utils/i18n/extract.py index cfcbd3f582..b1796b2862 100644 --- a/utils/i18n/extract.py +++ b/utils/i18n/extract.py @@ -12,6 +12,7 @@ from pathlib import Path from utils.cache.yaml import load_yaml_any from utils.i18n.catalog import LOCALE_DIR, read_catalog +from utils.roles.custom import is_custom from utils.roles.mapping import ROLE_FILE_META_MAIN CATEGORIES_FILE = Path("meta") / "categories.yml" @@ -90,10 +91,12 @@ def role_messages(root: Path) -> list[tuple[str, str]]: messages = [] roles = root / "roles" for main in sorted(roles.glob(f"*/{ROLE_FILE_META_MAIN}")): + role = main.relative_to(roles).parts[0] + if is_custom(role): + continue galaxy_info = (_load(main) or {}).get("galaxy_info") or {} description = _text(galaxy_info.get("description")) if description: - role = main.relative_to(roles).parts[0] messages.append((role_context(role), description)) return messages diff --git a/utils/i18n/libretranslate.py b/utils/i18n/libretranslate.py index ba979e4418..009d754f2f 100644 --- a/utils/i18n/libretranslate.py +++ b/utils/i18n/libretranslate.py @@ -310,6 +310,7 @@ def deploy(root: Path, roles: tuple[str, ...] = (ROLE, GATEWAY_ROLE)) -> str: str(INVENTORY_DIR / ".password"), "--skip-backup", "--skip-cleanup", + "--skip-pull", ], ] outer = {