diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 683b417903..d3d295c134 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -27,23 +27,23 @@ jobs: - tox_env: docs python-version: 3.9 - tox_env: py38 - PREFIX: PYTEST_REQPASS=454 + PREFIX: PYTEST_REQPASS=455 python-version: 3.8 cover: true - tox_env: py39 - PREFIX: PYTEST_REQPASS=454 + PREFIX: PYTEST_REQPASS=455 python-version: 3.9 cover: true - tox_env: py310 - PREFIX: PYTEST_REQPASS=454 + PREFIX: PYTEST_REQPASS=455 python-version: "3.10" cover: true - tox_env: py38-devel - PREFIX: PYTEST_REQPASS=454 + PREFIX: PYTEST_REQPASS=455 python-version: 3.8 cover: true - tox_env: py310-devel - PREFIX: PYTEST_REQPASS=454 + PREFIX: PYTEST_REQPASS=455 python-version: "3.10" # see https://github.com/ansible-community/molecule/issues/3291 experimental: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f5c2eced52..affe27f921 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -74,7 +74,7 @@ repos: entry: mypy src/ pass_filenames: false additional_dependencies: - - ansible-compat>=2.0.0 + - ansible-compat>=2.1.0 - click>=8.0.1 - enrich>=1.2.7 - importlib-metadata>=4.6.1 @@ -91,7 +91,7 @@ repos: hooks: - id: pylint additional_dependencies: - - ansible-compat>=2.0.0 + - ansible-compat>=2.1.0 - cerberus - click - click-help-colors diff --git a/setup.cfg b/setup.cfg index 22fe4935da..7e530385e1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -57,7 +57,7 @@ zip_safe = False # These are required in actual runtime: install_requires = - ansible-compat >= 1.0.0 + ansible-compat >= 2.1.0 cerberus >= 1.3.1, !=1.3.3, !=1.3.4 click >= 8.0, < 9 click-help-colors >= 0.9 diff --git a/src/molecule/provisioner/ansible.py b/src/molecule/provisioner/ansible.py index e9849b03ff..bcbdc7902f 100644 --- a/src/molecule/provisioner/ansible.py +++ b/src/molecule/provisioner/ansible.py @@ -205,7 +205,7 @@ class Ansible(base.Base): :: ANSIBLE_ROLES_PATH: - $ephemeral_directory/roles/:$project_directory/../:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles + $runtime_cache_dir/roles:$ephemeral_directory/roles/:$project_directory/../:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles ANSIBLE_LIBRARY: $ephemeral_directory/modules/:$project_directory/library/:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules ANSIBLE_FILTER_PLUGINS: @@ -419,9 +419,14 @@ def default_env(self): # top of the collection_path_list. This prevents dependency commands # from installing dependencies to user list of collections. collections_path_list = [ + util.abs_path( + os.path.join( + self._config.scenario.config.runtime.cache_dir, "collections" + ) + ), util.abs_path( os.path.join(self._config.scenario.ephemeral_directory, "collections") - ) + ), ] if collection_indicator in self._config.project_directory: collection_path, right = self._config.project_directory.rsplit( @@ -437,28 +442,30 @@ def default_env(self): "/etc/ansible/collections", ] ) + + roles_path_list = [ + util.abs_path( + os.path.join(self._config.scenario.config.runtime.cache_dir, "roles") + ), + util.abs_path( + os.path.join(self._config.scenario.ephemeral_directory, "roles") + ), + util.abs_path(os.path.join(self._config.project_directory, os.path.pardir)), + util.abs_path(os.path.join(os.path.expanduser("~"), ".ansible", "roles")), + "/usr/share/ansible/roles", + "/etc/ansible/roles", + ] + + if os.environ.get("ANSIBLE_ROLES_PATH", ""): + roles_path_list.extend( + list(map(util.abs_path, os.environ["ANSIBLE_ROLES_PATH"].split(":"))) + ) + env = util.merge_dicts( os.environ, { "ANSIBLE_CONFIG": self._config.provisioner.config_file, - "ANSIBLE_ROLES_PATH": ":".join( - [ - util.abs_path( - os.path.join( - self._config.scenario.ephemeral_directory, "roles" - ) - ), - util.abs_path( - os.path.join(self._config.project_directory, os.path.pardir) - ), - util.abs_path( - os.path.join(os.path.expanduser("~"), ".ansible", "roles") - ), - "/usr/share/ansible/roles", - "/etc/ansible/roles", - *os.environ.get("ANSIBLE_ROLES_PATH", "").split(":"), - ] - ), + "ANSIBLE_ROLES_PATH": ":".join(roles_path_list), self._config.ansible_collections_path: ":".join(collections_path_list), "ANSIBLE_LIBRARY": ":".join(self._get_modules_directories()), "ANSIBLE_FILTER_PLUGINS": ":".join( diff --git a/src/molecule/provisioner/ansible_playbook.py b/src/molecule/provisioner/ansible_playbook.py index ab0000c4f6..79b92770d0 100644 --- a/src/molecule/provisioner/ansible_playbook.py +++ b/src/molecule/provisioner/ansible_playbook.py @@ -51,9 +51,7 @@ def __init__(self, playbook, config, verify=False): self._config.verifier.env, self._config.config["verifier"]["env"] ) else: - self._env = util.merge_dicts( - self._config.provisioner.env, self._config.runtime.environ - ) + self._env = self._config.provisioner.env def bake(self): """ diff --git a/src/molecule/test/unit/provisioner/test_ansible.py b/src/molecule/test/unit/provisioner/test_ansible.py index 0b0fc58db0..340848b1a5 100644 --- a/src/molecule/test/unit/provisioner/test_ansible.py +++ b/src/molecule/test/unit/provisioner/test_ansible.py @@ -204,11 +204,10 @@ def test_env_property(_instance): "config_instance", ["_provisioner_section_data"], indirect=True ) def test_env_appends_env_property(_instance): + os.environ["ANSIBLE_ROLES_PATH"] = "" - # molecule could decide to add extra paths, so we only want to check - # that those that we need are kept inside the list - roles_path_list = _instance.env["ANSIBLE_ROLES_PATH"].split(":") - for x in [ + expected = [ + util.abs_path(os.path.join(_instance._config.runtime.cache_dir, "roles")), util.abs_path( os.path.join(_instance._config.scenario.ephemeral_directory, "roles") ), @@ -219,8 +218,14 @@ def test_env_appends_env_property(_instance): "/usr/share/ansible/roles", "/etc/ansible/roles", util.abs_path(os.path.join(_instance._config.scenario.directory, "foo", "bar")), - ]: - assert x in roles_path_list + ] + + # molecule could decide to add extra paths, so we only want to check + # that those that we need are kept inside the list with exact order + + roles_path_list = _instance.env["ANSIBLE_ROLES_PATH"].split(":") + + assert roles_path_list == expected x = _instance._get_modules_directories() x.append( @@ -247,6 +252,36 @@ def test_env_appends_env_property(_instance): assert x == _instance.env["ANSIBLE_FILTER_PLUGINS"].split(":") +@pytest.mark.parametrize( + "config_instance", ["_provisioner_section_data"], indirect=True +) +def test_env_appends_env_property_with_os_env(_instance): + os.environ["ANSIBLE_ROLES_PATH"] = "/foo/bar:/foo/baz" + + expected = [ + util.abs_path(os.path.join(_instance._config.runtime.cache_dir, "roles")), + util.abs_path( + os.path.join(_instance._config.scenario.ephemeral_directory, "roles") + ), + util.abs_path( + os.path.join(_instance._config.project_directory, os.path.pardir) + ), + util.abs_path(os.path.join(os.path.expanduser("~"), ".ansible", "roles")), + "/usr/share/ansible/roles", + "/etc/ansible/roles", + "/foo/bar", + "/foo/baz", + util.abs_path(os.path.join(_instance._config.scenario.directory, "foo", "bar")), + ] + + # molecule could decide to add extra paths, so we only want to check + # that those that we need are kept inside the list + + roles_path_list = _instance.env["ANSIBLE_ROLES_PATH"].split(":") + + assert roles_path_list == expected + + @pytest.mark.parametrize( "config_instance", ["_provisioner_section_data"], indirect=True )