fix(packaging): ship locales/ i18n catalogs in wheel, sdist, and Nix (#38383)
* fix(packaging): ship locales/ i18n catalogs in wheel, sdist, and Nix locales/ is a bare data dir (no __init__.py), invisible to packages.find and package-data. Sealed installs (pip wheel, Nix store venv) dropped it, so gateway/CLI commands rendered raw i18n keys like gateway.reset.header_default. - pyproject: [tool.setuptools.data-files] locales = ["locales/*.yaml"] (wheel) - MANIFEST.in: graft locales (sdist) - agent/i18n._locales_dir: env override -> source -> sysconfig data scheme - nix/hermes-agent.nix: copy locales into the store + set HERMES_BUNDLED_LOCALES as defense-in-depth. The wheel's data-files already materialize into the uv2nix venv, so resolution works with no env var; the override pins the store path against a future uv2nix change that could drop data-files. - tests: metadata regression, wheel + sdist build-install smoke tests, and a bundled-locales flake check that verifies BOTH the wrapper override and the env-var-less data-files path. Smoke test wired into CI. Closes #23943, #27632, #35374. Supersedes #23966, #27716, #30261, #33841, #35429, #35494, #35735, #36697. * test: cap locale e2e timeout, tighten catalog count guard The two wheel/sdist e2e tests inherit the global --timeout=30 from addopts; a cold-CI run (isolated build env + venv create + network pip install) can plausibly exceed it. Add @pytest.mark.timeout(300) so they don't ride the unit-test budget and flake intermittently. Also assert the shipped catalog count equals len(SUPPORTED_LANGUAGES) instead of a hardcoded >=16 floor, so the guard self-updates and trips on a single dropped catalog (not just a fully-empty graft).
This commit is contained in:
@@ -200,3 +200,29 @@ def test_locked_starlette_is_not_vulnerable_to_cve_2026_48710():
|
||||
f"floor {'.'.join(map(str, _STARLETTE_CVE_FLOOR))} — regenerate the "
|
||||
f"lockfile after bumping the pin"
|
||||
)
|
||||
|
||||
|
||||
def test_locale_catalogs_ship_in_both_wheel_and_sdist():
|
||||
"""Regression test for #27632 / #35374 / #23943.
|
||||
|
||||
locales/ is a bare data directory (no __init__.py), so it is invisible to
|
||||
packages.find and to package-data (which attaches to a package). It must be
|
||||
declared as setuptools data-files (wheel) AND grafted in MANIFEST.in
|
||||
(sdist). Without both, sealed installs drop the catalogs and gateway/CLI
|
||||
commands surface raw i18n keys like `gateway.reset.header_default`.
|
||||
"""
|
||||
data = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
||||
data_files = data["tool"]["setuptools"].get("data-files", {})
|
||||
assert data_files.get("locales") == ["locales/*.yaml"], (
|
||||
"pyproject [tool.setuptools.data-files] must declare "
|
||||
'locales = ["locales/*.yaml"] so the wheel ships i18n catalogs'
|
||||
)
|
||||
|
||||
manifest = (REPO_ROOT / "MANIFEST.in").read_text(encoding="utf-8")
|
||||
assert "graft locales" in manifest, (
|
||||
"MANIFEST.in must `graft locales` so the sdist ships i18n catalogs"
|
||||
)
|
||||
|
||||
# Every on-disk catalog has the .yaml extension the globs above match.
|
||||
on_disk = list((REPO_ROOT / "locales").glob("*.yaml"))
|
||||
assert on_disk, "expected locales/*.yaml catalogs on disk"
|
||||
|
||||
Reference in New Issue
Block a user