diff --git a/src/ansible_compat/runtime.py b/src/ansible_compat/runtime.py index 4556b7ec..fbeaa985 100644 --- a/src/ansible_compat/runtime.py +++ b/src/ansible_compat/runtime.py @@ -156,6 +156,7 @@ class Runtime: # to do it multiple tilmes will cause runtime warnings from within ansible-core initialized: bool = False plugins: Plugins + _has_playbook_cache: dict[tuple[str, Path | None], bool] = {} def __init__( self, @@ -432,6 +433,28 @@ def version_in_range( return False return True + def has_playbook(self, playbook: str, *, basedir: Path | None = None) -> bool: + """Return true if ansible can load a given playbook. + + This is also used for checking if playbooks from within collections + are present and if they pass syntax check. + """ + if (playbook, basedir) in self._has_playbook_cache: + return self._has_playbook_cache[playbook, basedir] + + proc = self.run(["ansible-playbook", "--syntax-check", playbook], cwd=basedir) + result = proc.returncode == 0 + if not result: + if not basedir: + basedir = Path() + msg = f"has_playbook returned false for '{basedir / playbook}' due to syntax check returning {proc.returncode}" + logging.debug(msg) + + # cache the result + self._has_playbook_cache[playbook, basedir] = result + + return result + def install_collection( self, collection: str | Path, diff --git a/test/test_runtime.py b/test/test_runtime.py index 6a110d41..0823f603 100644 --- a/test/test_runtime.py +++ b/test/test_runtime.py @@ -893,3 +893,16 @@ def test_get_galaxy_role_name_invalid() -> None: "role_name": False, # <-- invalid data, should be string } assert _get_galaxy_role_name(galaxy_infos) == "" + + +def test_runtime_has_playbook() -> None: + """Tests has_playbook method.""" + runtime = Runtime(require_module=True) + + assert not runtime.has_playbook("this-does-not-exist.yml") + # call twice to ensure cache is used: + assert not runtime.has_playbook("this-does-not-exist.yml") + + assert not runtime.has_playbook("this-does-not-exist.yml", basedir=Path()) + # this is part of community.molecule collection + assert runtime.has_playbook("community.molecule.validate.yml") diff --git a/tox.ini b/tox.ini index 6a1df3c4..6f6c82bd 100644 --- a/tox.ini +++ b/tox.ini @@ -87,7 +87,7 @@ setenv = PIP_DISABLE_PIP_VERSION_CHECK = 1 PIP_CONSTRAINT = {toxinidir}/.config/constraints.txt PRE_COMMIT_COLOR = always - PYTEST_REQPASS = 93 + PYTEST_REQPASS = 94 FORCE_COLOR = 1 allowlist_externals = ansible