diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e45b02411..fa82f97d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,6 +29,6 @@ jobs: with: fetch-depth: 0 # needed by setuptools-scm - name: Build dists - run: python -m tox -e pkg + run: python3 -m tox -e pkg - name: Publish to pypi.org uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index fc4e0465c..2bb6b5077 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -47,7 +47,7 @@ jobs: matrix: ${{ fromJson(needs.pre.outputs.matrix) }} env: - PYTEST_REQPASS: 450 + PYTEST_REQPASS: 452 environment: test steps: - uses: actions/checkout@v4 diff --git a/docs/ci.md b/docs/ci.md index e3294d828..8ccebdc61 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -197,10 +197,10 @@ steps: inputs: versionSpec: "3.10" - - script: python -m pip install "molecule[lint]" "python-vagrant" "molecule-vagrant" "ansible" + - script: python3 -m pip install "molecule[lint]" "python-vagrant" "molecule-vagrant" "ansible" displayName: Install dependencies - - script: python -m pip install "python-tss-sdk" + - script: python3 -m pip install "python-tss-sdk" displayName: Role-specific dependencies - script: | diff --git a/docs/installation.md b/docs/installation.md index c8d5b222b..2772fe285 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -93,7 +93,7 @@ $ python3 -m pip install --user "molecule-plugins[podman]" Installing molecule package also installed its main script `molecule`, usually in `PATH`. Users should know that molecule can also be called as -a python module, using `python -m molecule ...`. This alternative method +a python module, using `python3 -m molecule ...`. This alternative method has some benefits: - allows to explicitly control which Python interpreter is used by diff --git a/src/molecule/provisioner/ansible.py b/src/molecule/provisioner/ansible.py index 92f1abb54..e29211557 100644 --- a/src/molecule/provisioner/ansible.py +++ b/src/molecule/provisioner/ansible.py @@ -515,32 +515,7 @@ def default_env(self): self._config.ansible_collections_path: ":".join(collections_path_list), "ANSIBLE_LIBRARY": ":".join(self._get_modules_directories()), "ANSIBLE_FILTER_PLUGINS": ":".join( - [ - self._get_filter_plugin_directory(), - util.abs_path( - os.path.join( - self._config.scenario.ephemeral_directory, - "plugins", - "filter", - ), - ), - util.abs_path( - os.path.join( - self._config.project_directory, - "plugins", - "filter", - ), - ), - util.abs_path( - os.path.join( - os.path.expanduser("~"), - ".ansible", - "plugins", - "filter", - ), - ), - "/usr/share/ansible/plugins/filter", - ], + self._get_filter_plugins_directories(), ), }, ) @@ -1012,5 +987,44 @@ def _get_modules_directories(self) -> list[str]: def _get_filter_plugin_directory(self): return util.abs_path(os.path.join(self._get_plugin_directory(), "filter")) + def _get_filter_plugins_directories(self) -> list[str]: + """Return list of ansilbe filter plugins includes directories.""" + paths: list[str | None] = [] + if os.environ.get("ANSIBLE_FILTER_PLUGINS"): + paths = list( + map(util.abs_path, os.environ["ANSIBLE_FILTER_PLUGINS"].split(":")), + ) + + paths.extend( + [ + self._get_filter_plugin_directory(), + util.abs_path( + os.path.join( + self._config.scenario.ephemeral_directory, + "plugins", + "filter", + ), + ), + util.abs_path( + os.path.join( + self._config.project_directory, + "plugins", + "filter", + ), + ), + util.abs_path( + os.path.join( + os.path.expanduser("~"), + ".ansible", + "plugins", + "filter", + ), + ), + "/usr/share/ansible/plugins/filter", + ], + ) + + return [path for path in paths if path is not None] + def _absolute_path_for(self, env, key): return ":".join([self.abs_path(p) for p in env[key].split(":")]) diff --git a/test/a_unit/provisioner/test_ansible.py b/test/a_unit/provisioner/test_ansible.py index 58f3c6163..9e4a36578 100644 --- a/test/a_unit/provisioner/test_ansible.py +++ b/test/a_unit/provisioner/test_ansible.py @@ -749,6 +749,47 @@ def test_get_filter_plugin_directory(_instance): assert x == parts[-5:] +def test_get_filter_plugins_directories_default(_instance, monkeypatch): + monkeypatch.delenv("ANSIBLE_FILTER_PLUGINS", raising=False) + + paths = _instance._get_filter_plugins_directories() + + assert len(paths) == 5 + assert re.search(r"molecule/provisioner/ansible/plugins/filter$", paths[0]) + assert re.search(r"\.cache/molecule/[^/]+/default/plugins/filter$", paths[1]) + assert re.search(r"/filter$", paths[2]) + assert re.search(r"\.ansible/plugins/filter$", paths[3]) + assert re.search(r"/usr/share/ansible/plugins/filter$", paths[4]) + + +def tes_get_filter_plugins_directories_single_ansible_filter_plugins( + _instance, + monkeypatch, +): + monkeypatch.setenv("ANSIBLE_FILTER_PLUGINS", "/abs/path/plugins/filter") + + paths = _instance._get_filter_plugins_directories() + + assert len(paths) == 6 + assert paths[0] == "/abs/path/plugins/filter" + + +def test_get_filter_plugins_directories_multi_ansible_filter_plugins( + _instance, + monkeypatch, +): + monkeypatch.setenv( + "ANSIBLE_FILTER_PLUGINS", + "relpath/plugins/filter:/abs/path/plugins/filter", + ) + + paths = _instance._get_filter_plugins_directories() + + assert len(paths) == 7 + assert paths[0].endswith("relpath/plugins/filter") + assert paths[1] == "/abs/path/plugins/filter" + + def test_absolute_path_for(_instance): env = {"foo": "foo:bar"} x = ":".join( diff --git a/test/scenarios/verifier/.pre-commit-config.yaml b/test/scenarios/verifier/.pre-commit-config.yaml index ce9e96bd9..1c6652b9d 100644 --- a/test/scenarios/verifier/.pre-commit-config.yaml +++ b/test/scenarios/verifier/.pre-commit-config.yaml @@ -4,6 +4,6 @@ repos: hooks: - id: flake8 name: flake8 - entry: python -m flake8 --max-line-length=120 + entry: python3 -m flake8 --max-line-length=120 language: system types: [python] diff --git a/tox.ini b/tox.ini index 7e5a7497e..718ab593e 100644 --- a/tox.ini +++ b/tox.ini @@ -55,7 +55,7 @@ commands = # failsafe as pip may install incompatible dependencies pip check # failsafe for preventing changes that may break pytest collection - sh -c "PYTEST_ADDOPTS= python -m pytest -p no:cov --collect-only >>/dev/null" + sh -c "PYTEST_ADDOPTS= python3 -m pytest -p no:cov --collect-only >>/dev/null" sh -c "rm -f .tox/.coverage.*" # html report is used by Zuul CI to display reports coverage run -m pytest {env:_EXTRAS} {env:PYTEST_ADDOPTS:} {posargs} @@ -71,7 +71,7 @@ description = Runs all linting tasks basepython = python3.10 commands = # to run a single linter you can do "pre-commit run flake8" - python -m pre_commit run {posargs:--all --show-diff-on-failure} + python3 -m pre_commit run {posargs:--all --show-diff-on-failure} deps = pre-commit>=2.21.0 check-jsonschema>=0.20.0 @@ -105,11 +105,11 @@ deps = setenv = commands = rm -rfv {toxinidir}/dist/ - python -m build \ + python3 -m build \ --outdir {toxinidir}/dist/ \ {toxinidir} # metadata validation - sh -c "python -m twine check --strict {toxinidir}/dist/*" + sh -c "python3 -m twine check --strict {toxinidir}/dist/*" [testenv:snap] description = Builds a snap package