Remove reference to --jinjaturtle auto/on/off (it's a boolean arg). Fix alma python
All checks were successful
CI / test (push) Successful in 53s
CI / test (almalinux, docker.io/library/almalinux:9, python3.11) (push) Successful in 10m44s
CI / test (debian, docker.io/library/debian:13, python3) (push) Successful in 16m3s
Lint / test (push) Successful in 46s

This commit is contained in:
Miguel Jacq 2026-06-30 12:34:26 +10:00
parent e72044b610
commit 85345083a3
Signed by: mig5
GPG key ID: 03906B4110AAD3B8
10 changed files with 76 additions and 37 deletions

View file

@ -15,7 +15,7 @@ jobs:
image: docker.io/library/debian:13 image: docker.io/library/debian:13
python: python3 python: python3
- distro: almalinux - distro: almalinux
image: docker.io/library/almalinux:10 image: docker.io/library/almalinux:9
python: python3.11 python: python3.11
container: container:
@ -95,6 +95,8 @@ jobs:
chmod +x /usr/local/bin/sops chmod +x /usr/local/bin/sops
- name: Run test script - name: Run test script
env:
PYTHON_BIN: ${{ matrix.python }}
run: | run: |
./tests.sh ./tests.sh

View file

@ -777,7 +777,7 @@ manifest(
bundle_dir, bundle_dir,
out, out,
fqdn=None, fqdn=None,
jinjaturtle="auto", jinjaturtle=None,
sops_fingerprints=None, sops_fingerprints=None,
no_common_roles=False, no_common_roles=False,
target="ansible", target="ansible",
@ -876,7 +876,7 @@ ansible.manifest_from_bundle_dir(
bundle_dir, bundle_dir,
out_dir, out_dir,
fqdn=None, fqdn=None,
jinjaturtle="auto", jinjaturtle=None,
no_common_roles=False, no_common_roles=False,
) )
``` ```
@ -992,14 +992,14 @@ File: `jinjaturtle.py`
JinjaTurtle mode is resolved by: JinjaTurtle mode is resolved by:
```python ```python
resolve_jinjaturtle_mode("auto" | "on" | "off") resolve_jinjaturtle_mode(None | True | False)
``` ```
Semantics: Semantics:
- `auto`: use `jinjaturtle` when it exists on `PATH`; otherwise copy raw files. - `None`: use `jinjaturtle` when it exists on `PATH`; otherwise copy raw files.
- `on`: require `jinjaturtle`; error if missing. - `True`: require `jinjaturtle`; error if missing.
- `off`: never use it. - `False`: never use it.
Supported path types include structured config suffixes: Supported path types include structured config suffixes:
@ -1285,12 +1285,12 @@ Disables the default grouping of package/service snapshots by Debian Section or
### 20.3 `--jinjaturtle` / `--no-jinjaturtle` ### 20.3 `--jinjaturtle` / `--no-jinjaturtle`
The CLI maps these to renderer mode strings: The CLI exposes these as boolean flags, not as options that take values:
```text ```text
no flag -> auto no flag -> use jinjaturtle when it exists on PATH
--jinjaturtle -> on --jinjaturtle -> require jinjaturtle; fail if it is missing
--no-jinjaturtle -> off --no-jinjaturtle -> disable jinjaturtle
``` ```
--- ---

View file

@ -418,7 +418,7 @@ def _prepare_ansible_context(
out_dir: str, out_dir: str,
*, *,
fqdn: Optional[str], fqdn: Optional[str],
jinjaturtle: str, jinjaturtle: Optional[bool],
) -> AnsibleManifestContext: ) -> AnsibleManifestContext:
site_mode = fqdn is not None and fqdn != "" site_mode = fqdn is not None and fqdn != ""
jt_exe, jt_enabled = resolve_jinjaturtle_mode(jinjaturtle) jt_exe, jt_enabled = resolve_jinjaturtle_mode(jinjaturtle)
@ -2400,7 +2400,7 @@ class AnsibleManifestRenderer:
out_dir: str, out_dir: str,
*, *,
fqdn: Optional[str] = None, fqdn: Optional[str] = None,
jinjaturtle: str = "auto", jinjaturtle: Optional[bool] = None,
no_common_roles: bool = False, no_common_roles: bool = False,
) -> None: ) -> None:
self.bundle_dir = bundle_dir self.bundle_dir = bundle_dir
@ -2499,7 +2499,7 @@ def manifest_from_bundle_dir(
out_dir: str, out_dir: str,
*, *,
fqdn: Optional[str] = None, fqdn: Optional[str] = None,
jinjaturtle: str = "auto", jinjaturtle: Optional[bool] = None,
no_common_roles: bool = False, no_common_roles: bool = False,
) -> None: ) -> None:
AnsibleManifestRenderer( AnsibleManifestRenderer(

View file

@ -481,12 +481,12 @@ def _add_common_manifest_args(p: argparse.ArgumentParser) -> None:
) )
def _jt_mode(args: argparse.Namespace) -> str: def _jt_mode(args: argparse.Namespace) -> Optional[bool]:
if getattr(args, "jinjaturtle", False): if getattr(args, "jinjaturtle", False):
return "on" return True
if getattr(args, "no_jinjaturtle", False): if getattr(args, "no_jinjaturtle", False):
return "off" return False
return "auto" return None
def _add_config_args(p: argparse.ArgumentParser) -> None: def _add_config_args(p: argparse.ArgumentParser) -> None:

View file

@ -43,24 +43,26 @@ SUPPORTED_SUFFIXES = {
} | SYSTEMD_SUFFIXES } | SYSTEMD_SUFFIXES
def resolve_jinjaturtle_mode(jinjaturtle: str) -> Tuple[Optional[str], bool]: def resolve_jinjaturtle_mode(
"""Resolve Enroll's common JinjaTurtle mode flag. jinjaturtle: Optional[bool] = None,
) -> Tuple[Optional[str], bool]:
"""Resolve Enroll's common JinjaTurtle flag.
Renderers accept the same values: ``None`` means the default behaviour: use JinjaTurtle when its executable
- ``auto``: use JinjaTurtle when present on PATH is present on PATH. ``True`` corresponds to ``--jinjaturtle`` and requires
- ``on``: require it and fail if it is absent the executable. ``False`` corresponds to ``--no-jinjaturtle`` and disables
- ``off``: never use it the integration.
""" """
jt_exe = find_jinjaturtle_cmd() jt_exe = find_jinjaturtle_cmd()
if jinjaturtle not in {"auto", "on", "off"}: if jinjaturtle is True:
raise ValueError("jinjaturtle must be one of: auto, on, off")
if jinjaturtle == "on":
if not jt_exe: if not jt_exe:
raise RuntimeError("jinjaturtle requested but not found on PATH") raise RuntimeError("jinjaturtle requested but not found on PATH")
return jt_exe, True return jt_exe, True
if jinjaturtle == "auto": if jinjaturtle is False:
return jt_exe, False
if jinjaturtle is None:
return jt_exe, jt_exe is not None return jt_exe, jt_exe is not None
return jt_exe, False raise TypeError("jinjaturtle must be None, True, or False")
def _merge_mappings_overwrite( def _merge_mappings_overwrite(

View file

@ -189,7 +189,7 @@ def manifest(
out: str, out: str,
*, *,
fqdn: Optional[str] = None, fqdn: Optional[str] = None,
jinjaturtle: str = "auto", # auto|on|off jinjaturtle: Optional[bool] = None,
sops_fingerprints: Optional[List[str]] = None, sops_fingerprints: Optional[List[str]] = None,
no_common_roles: bool = False, no_common_roles: bool = False,
) -> Optional[str]: ) -> Optional[str]:

View file

@ -137,6 +137,38 @@ is_rpm_family() {
esac esac
} }
preferred_python_bin() {
if [[ -n "${ENROLL_TEST_PYTHON_BIN:-}" ]]; then
printf '%s' "${ENROLL_TEST_PYTHON_BIN}"
return
fi
if [[ -n "${PYTHON_BIN:-}" ]]; then
printf '%s' "${PYTHON_BIN}"
return
fi
if command -v python3.11 >/dev/null 2>&1; then
printf '%s' "python3.11"
return
fi
printf '%s' "python3"
}
require_python_for_jinjaturtle() {
local py_bin
py_bin="$(preferred_python_bin)"
command -v "${py_bin}" >/dev/null 2>&1 \
|| fail "Python interpreter '${py_bin}' was not found; set PYTHON_BIN or ENROLL_TEST_PYTHON_BIN."
"${py_bin}" - <<'PY'
import sys
if sys.version_info < (3, 10):
raise SystemExit(
f"Python {sys.version.split()[0]} is too old for JinjaTurtle; "
"install/use Python >= 3.10 (for AlmaLinux 9, set PYTHON_BIN=python3.11)."
)
PY
printf '%s' "${py_bin}"
}
pkg_update_once() { pkg_update_once() {
if is_debian; then if is_debian; then
if [[ -z "${APT_UPDATED:-}" ]]; then if [[ -z "${APT_UPDATED:-}" ]]; then
@ -292,11 +324,14 @@ ensure_jinjaturtle_from_git() {
if is_debian; then if is_debian; then
pkg_install ca-certificates git python3 python3-venv pkg_install ca-certificates git python3 python3-venv
elif is_rpm_family; then elif is_rpm_family; then
pkg_install ca-certificates git python3 pkg_install ca-certificates git python3.11 python3.11-pip
else else
fail "Unsupported OS for JinjaTurtle git build: $(os_id)." fail "Unsupported OS for JinjaTurtle git build: $(os_id)."
fi fi
local jt_python
jt_python="$(require_python_for_jinjaturtle)"
local src="${ENROLL_TEST_JINJATURTLE_SOURCE:-}" local src="${ENROLL_TEST_JINJATURTLE_SOURCE:-}"
local cloned=0 local cloned=0
if [[ -z "${src}" ]]; then if [[ -z "${src}" ]]; then
@ -316,7 +351,7 @@ ensure_jinjaturtle_from_git() {
# the console script on PATH. Enroll only needs shutil.which("jinjaturtle") # the console script on PATH. Enroll only needs shutil.which("jinjaturtle")
# to succeed, which a symlink into /usr/local/bin satisfies. # to succeed, which a symlink into /usr/local/bin satisfies.
rm -rf "${JINJATURTLE_VENV_DIR}" rm -rf "${JINJATURTLE_VENV_DIR}"
run python3 -m venv "${JINJATURTLE_VENV_DIR}" run "${jt_python}" -m venv "${JINJATURTLE_VENV_DIR}"
run "${JINJATURTLE_VENV_DIR}/bin/pip" install --upgrade pip run "${JINJATURTLE_VENV_DIR}/bin/pip" install --upgrade pip
run "${JINJATURTLE_VENV_DIR}/bin/pip" install "${src}" run "${JINJATURTLE_VENV_DIR}/bin/pip" install "${src}"

View file

@ -67,7 +67,7 @@ def test_cli_manifest_subcommand_calls_manifest(monkeypatch, tmp_path):
assert called["harvest"] == str(tmp_path / "bundle") assert called["harvest"] == str(tmp_path / "bundle")
assert called["out"] == str(tmp_path / "ansible") assert called["out"] == str(tmp_path / "ansible")
assert called["fqdn"] is None assert called["fqdn"] is None
assert called["jinjaturtle"] == "auto" assert called["jinjaturtle"] is None
assert called["no_common_roles"] is False assert called["no_common_roles"] is False
@ -202,7 +202,7 @@ def test_cli_enroll_subcommand_runs_harvest_then_manifest(monkeypatch, tmp_path)
cli.main() cli.main()
assert calls == [ assert calls == [
("harvest", str(tmp_path / "bundle"), False, [], []), ("harvest", str(tmp_path / "bundle"), False, [], []),
("manifest", str(tmp_path / "bundle"), str(tmp_path / "ansible"), None, "auto"), ("manifest", str(tmp_path / "bundle"), str(tmp_path / "ansible"), None, None),
] ]
@ -491,7 +491,7 @@ def test_cli_manifest_common_args(monkeypatch, tmp_path):
cli.main() cli.main()
assert called["fqdn"] == "example.test" assert called["fqdn"] == "example.test"
assert called["jinjaturtle"] == "off" assert called["jinjaturtle"] is False
def test_cli_explain_passes_args_and_writes_stdout(monkeypatch, capsys, tmp_path): def test_cli_explain_passes_args_and_writes_stdout(monkeypatch, capsys, tmp_path):

View file

@ -127,7 +127,7 @@ def test_manifest_uses_jinjaturtle_templates_and_does_not_copy_raw(
monkeypatch.setattr(jinjaturtle_mod, "run_jinjaturtle", fake_run_jinjaturtle) monkeypatch.setattr(jinjaturtle_mod, "run_jinjaturtle", fake_run_jinjaturtle)
manifest_mod.manifest(str(bundle), str(out), jinjaturtle="on") manifest_mod.manifest(str(bundle), str(out), jinjaturtle=True)
role_dir = out / "roles" / "utils" role_dir = out / "roles" / "utils"

View file

@ -1541,7 +1541,7 @@ def test_manifest_applies_jinjaturtle_to_jinjifyable_managed_file(
monkeypatch.setattr(jinjaturtle_mod, "run_jinjaturtle", lambda *a, **k: _Res()) monkeypatch.setattr(jinjaturtle_mod, "run_jinjaturtle", lambda *a, **k: _Res())
out_dir = tmp_path / "out" out_dir = tmp_path / "out"
manifest.manifest(str(bundle), str(out_dir), jinjaturtle="on") manifest.manifest(str(bundle), str(out_dir), jinjaturtle=True)
tmpl = out_dir / "roles" / "apt_config" / "templates" / "etc" / "apt" / "foo.ini.j2" tmpl = out_dir / "roles" / "apt_config" / "templates" / "etc" / "apt" / "foo.ini.j2"
assert tmpl.exists() assert tmpl.exists()