diff --git a/cli/meta/env/__main__.py b/cli/meta/env/__main__.py index 9ab0ca4b71..d4f7e3764f 100644 --- a/cli/meta/env/__main__.py +++ b/cli/meta/env/__main__.py @@ -2,6 +2,7 @@ from __future__ import annotations +import os import sys from utils.env.builder import build_env @@ -10,6 +11,8 @@ from utils.env.writer import write_dotenv from . import PROJECT_ROOT as REPO_ROOT +HOST_OWNED_KEY = "INFINITO_DOTENV_HOST_OWNED" # nocheck: dotenv-host-owned-marker Reason: set only by compose.yml inside the runner container; not a configuration key, a .env entry could freeze the host .env + def main() -> int: static_path = REPO_ROOT / "default.env" @@ -19,6 +22,14 @@ def main() -> int: print(f"ERROR: missing {static_path}", file=sys.stderr) return 2 + if out_path.is_file() and os.environ.get(HOST_OWNED_KEY) == "true": + print( + f"Kept {out_path.relative_to(REPO_ROOT)}: the host checkout owns it " + f"({HOST_OWNED_KEY})", + file=sys.stderr, + ) + return 0 + static, static_comments = parse_static_env_with_comments(static_path) eb = build_env(static, repo_root=REPO_ROOT, comments=static_comments) write_dotenv(eb, out_path) diff --git a/compose.yml b/compose.yml index 3bb9f3e536..8d12571046 100644 --- a/compose.yml +++ b/compose.yml @@ -78,6 +78,7 @@ services: INFINITO_DIR_VAR_LIB: ${INFINITO_DIR_VAR_LIB} INFINITO_DNS_IP: ${INFINITO_DNS_IP} INFINITO_DOMAIN: ${INFINITO_DOMAIN} + INFINITO_DOTENV_HOST_OWNED: "true" INFINITO_GHCR_MIRROR_PREFIX: ${INFINITO_GHCR_MIRROR_PREFIX} INFINITO_IMAGE_TAG: ${INFINITO_IMAGE_TAG} INFINITO_IP4: ${INFINITO_IP4} diff --git a/tests/unit/python/cli/meta/env/__init__.py b/tests/unit/python/cli/meta/env/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit/python/cli/meta/env/test_main.py b/tests/unit/python/cli/meta/env/test_main.py new file mode 100644 index 0000000000..d2cc44c42f --- /dev/null +++ b/tests/unit/python/cli/meta/env/test_main.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +import cli.meta.env.__main__ as env_main + + +class TestDotenvHostOwned(unittest.TestCase): + def _run(self, *, dotenv_exists: bool, host_owned: str | None) -> tuple[int, bool]: + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + (root / "default.env").write_text("INFINITO_DISTRO=debian\n") + if dotenv_exists: + (root / ".env").write_text("INFINITO_RUNNING_ON_GITHUB=true\n") + environ = ( + {} if host_owned is None else {env_main.HOST_OWNED_KEY: host_owned} + ) + with ( + patch.object(env_main, "REPO_ROOT", root), + patch.dict("os.environ", environ, clear=True), + patch.object( + env_main, "parse_static_env_with_comments", return_value=({}, {}) + ), + patch.object(env_main, "build_env") as build_env, + patch.object(env_main, "write_dotenv") as write_dotenv, + ): + build_env.return_value.values = {} + rc = env_main.main() + return rc, write_dotenv.called + + def test_a_host_owned_dotenv_is_kept(self) -> None: + self.assertEqual(self._run(dotenv_exists=True, host_owned="true"), (0, False)) + + def test_a_missing_dotenv_is_generated_even_when_host_owned(self) -> None: + self.assertEqual(self._run(dotenv_exists=False, host_owned="true"), (0, True)) + + def test_without_the_marker_the_dotenv_is_regenerated(self) -> None: + self.assertEqual(self._run(dotenv_exists=True, host_owned=None), (0, True)) + + +if __name__ == "__main__": # pragma: no cover + unittest.main() diff --git a/tests/unit/python/scripts/tests/deploy/test_distros.py b/tests/unit/python/scripts/tests/deploy/test_distros.py index 0abb3c7479..4b6cb76aee 100644 --- a/tests/unit/python/scripts/tests/deploy/test_distros.py +++ b/tests/unit/python/scripts/tests/deploy/test_distros.py @@ -29,6 +29,7 @@ STRIPPED = ( "INFINITO_DISTRO", "INFINITO_CI_DISTRO_BUDGET_SECONDS", "INFINITO_SWARM_STEP_TIMEOUT_MINUTES", + "INFINITO_ENV_LOADED", # nocheck: env-loader-internal-guard )