Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package molecule for openSUSE:Factory checked in at 2025-06-23 15:02:41 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/molecule (Old) and /work/SRC/openSUSE:Factory/.molecule.new.7067 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "molecule" Mon Jun 23 15:02:41 2025 rev:22 rq:1287564 version:25.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/molecule/molecule.changes 2025-05-26 18:41:53.571439787 +0200 +++ /work/SRC/openSUSE:Factory/.molecule.new.7067/molecule.changes 2025-06-23 15:03:08.648446027 +0200 @@ -1,0 +2,25 @@ +Thu Jun 19 06:33:03 UTC 2025 - Johannes Kastl <opensuse_buildserv...@ojkastl.de> + +- update to 25.6.0: + * Enhancements + - Add navigator as molecule playbook executor (#4457) + @shatakshiiii + - Add support for shared inventory between scenarios (#4443) + @Qalthos + * Bugfixes + - Fix #4408 parallel execution: create parent directory (#4458) + @panchal-yash + - Display Docker result in error message (#4450) + @emmanuel-ferdman + - Strip argument documentation from command help text. (#4451) + @Qalthos + * Maintenance + - Bump the dependencies group in /.config with 7 updates + (#4459) @dependabot[bot] + - Bump the dependencies group in /.config with 5 updates + (#4455) @dependabot[bot] + - chore: pre-commit autoupdate (#4454) @pre-commit-ci[bot] + - Bump the dependencies group in /.config with 9 updates + (#4453) @dependabot[bot] + +------------------------------------------------------------------- Old: ---- molecule-25.5.0.tar.gz New: ---- molecule-25.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ molecule.spec ++++++ --- /var/tmp/diff_new_pack.WQoYmm/_old 2025-06-23 15:03:09.540482266 +0200 +++ /var/tmp/diff_new_pack.WQoYmm/_new 2025-06-23 15:03:09.544482429 +0200 @@ -39,7 +39,7 @@ %bcond_without test Name: molecule -Version: 25.5.0 +Version: 25.6.0 Release: 0 Summary: Aids in the development and testing of Ansible roles License: MIT @@ -53,6 +53,7 @@ %if %{with test} BuildRequires: git-core BuildRequires: ansible-lint >= 6.12.1 +BuildRequires: ansible-navigator BuildRequires: %{ansible_python}-ansi2html >= 1.8.0 BuildRequires: %{ansible_python}-coverage >= 7.0.3 BuildRequires: %{ansible_python}-filelock >= 3.9.0 @@ -146,6 +147,7 @@ IGNORED_CHECKS="${IGNORED_CHECKS} or test_command_idempotence[0]" IGNORED_CHECKS="${IGNORED_CHECKS} or test_command_init_scenario" IGNORED_CHECKS="${IGNORED_CHECKS} or test_command_list_with_format_plain" +IGNORED_CHECKS="${IGNORED_CHECKS} or test_with_backend_as_ansible_navigator" IGNORED_CHECKS="${IGNORED_CHECKS} or test_with_missing_platform_name[instance-False-0]" IGNORED_CHECKS="${IGNORED_CHECKS} or test_with_missing_platform_name[gonzo-True-0]" IGNORED_CHECKS="${IGNORED_CHECKS} or test_role_name_check_one" ++++++ molecule-25.5.0.tar.gz -> molecule-25.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/.config/constraints.txt new/molecule-25.6.0/.config/constraints.txt --- old/molecule-25.5.0/.config/constraints.txt 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/.config/constraints.txt 2025-06-18 14:43:16.000000000 +0200 @@ -1,33 +1,38 @@ # This file was autogenerated by uv via the following command: # tox run -e deps ansi2html==1.9.2 # via molecule (pyproject.toml) +ansible-builder==3.1.0 # via ansible-navigator ansible-compat==25.5.0 # via ansible-lint, molecule (pyproject.toml) -ansible-lint==25.5.0 # via molecule (pyproject.toml) +ansible-lint==25.5.0 # via ansible-navigator, molecule (pyproject.toml) +ansible-navigator==25.5.0 # via molecule (pyproject.toml) +ansible-runner==2.4.1 # via ansible-navigator astroid==3.3.10 # via pylint attrs==25.3.0 # via jsonschema, referencing babel==2.17.0 # via mkdocs-material backrefs==5.8 # via mkdocs-material beautifulsoup4==4.13.4 # via linkchecker, mkdocs-htmlproofer-plugin +bindep==2.13.0 # via ansible-builder black==25.1.0 # via ansible-lint bracex==2.5.post1 # via wcmatch cachetools==6.0.0 # via tox cairocffi==1.7.1 # via cairosvg cairosvg==2.7.1 # via mkdocs-ansible -certifi==2025.4.26 # via requests -cffi==1.17.1 # via cairocffi, cryptography +certifi==2025.6.15 # via requests +cffi==1.17.1 # via cairocffi, cryptography, onigurumacffi cfgv==3.4.0 # via pre-commit chardet==5.2.0 # via tox charset-normalizer==3.4.2 # via requests click==8.2.1 # via black, click-help-colors, mkdocs, pydoclint, molecule (pyproject.toml) click-help-colors==0.9.4 # via molecule (pyproject.toml) colorama==0.4.6 # via griffe, mkdocs-material, tox -coverage==7.8.2 # via molecule (pyproject.toml) -cryptography==45.0.3 # via ansible-core +coverage==7.9.1 # via molecule (pyproject.toml) +cryptography==45.0.4 # via ansible-core csscompressor==0.9.5 # via mkdocs-minify-plugin cssselect2==0.8.0 # via cairosvg defusedxml==0.7.1 # via cairosvg dill==0.4.0 # via pylint distlib==0.3.9 # via virtualenv +distro==1.9.0 # via bindep dnspython==2.7.0 # via linkchecker docker==7.1.0 # via molecule (pyproject.toml) docstring-parser-fork==0.0.12 # via pydoclint @@ -44,11 +49,12 @@ importlib-metadata==8.7.0 # via ansible-lint iniconfig==2.1.0 # via pytest isort==6.0.1 # via pylint -jinja2==3.1.6 # via ansible-core, mkdocs, mkdocs-macros-plugin, mkdocs-material, mkdocstrings, molecule (pyproject.toml) +jinja2==3.1.6 # via ansible-core, ansible-navigator, mkdocs, mkdocs-macros-plugin, mkdocs-material, mkdocstrings, molecule (pyproject.toml) jsmin==3.0.1 # via mkdocs-minify-plugin -jsonschema==4.23.0 # via ansible-compat, ansible-lint, molecule (pyproject.toml) +jsonschema==4.24.0 # via ansible-builder, ansible-compat, ansible-lint, ansible-navigator, molecule (pyproject.toml) jsonschema-specifications==2025.4.1 # via jsonschema linkchecker==10.5.0 # via mkdocs-ansible, molecule (pyproject.toml) +lockfile==0.12.2 # via python-daemon markdown==3.8 # via markdown-include, mkdocs, mkdocs-autorefs, mkdocs-htmlproofer-plugin, mkdocs-material, mkdocstrings, pymdown-extensions markdown-exec==1.10.3 # via mkdocs-ansible markdown-include==0.8.1 # via mkdocs-ansible @@ -68,14 +74,17 @@ mkdocs-material-extensions==1.3.1 # via mkdocs-ansible, mkdocs-material mkdocs-minify-plugin==0.8.0 # via mkdocs-ansible mkdocstrings==0.29.1 # via mkdocs-ansible, mkdocstrings-python -mkdocstrings-python==1.16.11 # via mkdocs-ansible -mypy==1.15.0 # via molecule (pyproject.toml) +mkdocstrings-python==1.16.12 # via mkdocs-ansible +mypy==1.16.0 # via molecule (pyproject.toml) mypy-extensions==1.1.0 # via black, mypy nodeenv==1.9.1 # via pre-commit -packaging==25.0 # via ansible-compat, ansible-core, ansible-lint, black, mkdocs, mkdocs-macros-plugin, pyproject-api, pytest, tox, molecule (pyproject.toml) +onigurumacffi==1.4.1 # via ansible-navigator +packaging==25.0 # via ansible-builder, ansible-compat, ansible-core, ansible-lint, ansible-runner, bindep, black, mkdocs, mkdocs-macros-plugin, pyproject-api, pytest, tox, molecule (pyproject.toml) paginate==0.5.7 # via mkdocs-material -pathspec==0.12.1 # via ansible-lint, black, mkdocs, mkdocs-macros-plugin, yamllint -pexpect==4.9.0 # via molecule (pyproject.toml) +parsley==1.3 # via bindep +pathspec==0.12.1 # via ansible-lint, black, mkdocs, mkdocs-macros-plugin, mypy, yamllint +pbr==6.1.1 # via bindep +pexpect==4.9.0 # via ansible-runner, molecule (pyproject.toml) pillow==11.2.1 # via cairosvg, mkdocs-ansible platformdirs==4.3.8 # via black, mkdocs-get-deps, pylint, tox, virtualenv pluggy==1.6.0 # via pytest, tox, molecule (pyproject.toml) @@ -83,25 +92,27 @@ ptyprocess==0.7.0 # via pexpect pycparser==2.22 # via cffi pydoclint==0.6.6 # via molecule (pyproject.toml) -pygments==2.19.1 # via mkdocs-material, rich +pygments==2.19.1 # via mkdocs-material, pytest, rich pylint==3.3.7 # via molecule (pyproject.toml) pymdown-extensions==10.15 # via markdown-exec, mkdocs-ansible, mkdocs-material, mkdocstrings pyproject-api==1.9.1 # via tox -pytest==8.3.5 # via pytest-instafail, pytest-mock, pytest-plus, pytest-testinfra, pytest-xdist, molecule (pyproject.toml) +pytest==8.4.0 # via pytest-instafail, pytest-mock, pytest-plus, pytest-testinfra, pytest-xdist, molecule (pyproject.toml) pytest-instafail==0.5.0 # via molecule (pyproject.toml) -pytest-mock==3.14.0 # via molecule (pyproject.toml) +pytest-mock==3.14.1 # via molecule (pyproject.toml) pytest-plus==0.8.1 # via molecule (pyproject.toml) pytest-testinfra==10.2.2 # via molecule (pyproject.toml) -pytest-xdist==3.6.1 # via molecule (pyproject.toml) +pytest-xdist==3.7.0 # via molecule (pyproject.toml) +python-daemon==3.1.2 # via ansible-runner python-dateutil==2.9.0.post0 # via ghp-import, mkdocs-macros-plugin -pyyaml==6.0.2 # via ansible-compat, ansible-core, ansible-lint, mkdocs, mkdocs-get-deps, mkdocs-macros-plugin, pre-commit, pymdown-extensions, pyyaml-env-tag, yamllint, molecule (pyproject.toml) +pyyaml==6.0.2 # via ansible-builder, ansible-compat, ansible-core, ansible-lint, ansible-navigator, ansible-runner, mkdocs, mkdocs-get-deps, mkdocs-macros-plugin, pre-commit, pymdown-extensions, pyyaml-env-tag, yamllint, molecule (pyproject.toml) pyyaml-env-tag==1.1 # via mkdocs referencing==0.36.2 # via ansible-lint, jsonschema, jsonschema-specifications, types-jsonschema -requests==2.32.3 # via docker, linkchecker, mkdocs-htmlproofer-plugin, mkdocs-material, molecule (pyproject.toml) +requests==2.32.4 # via docker, linkchecker, mkdocs-htmlproofer-plugin, mkdocs-material, molecule (pyproject.toml) rich==14.0.0 # via enrich, molecule (pyproject.toml) rpds-py==0.25.1 # via jsonschema, referencing -ruamel-yaml==0.18.11 # via ansible-lint -ruff==0.11.11 # via molecule (pyproject.toml) +ruamel-yaml==0.18.14 # via ansible-lint +ruff==0.11.13 # via molecule (pyproject.toml) +setuptools==80.9.0 # via pbr six==1.17.0 # via python-dateutil soupsieve==2.7 # via beautifulsoup4 subprocess-tee==0.4.2 # via ansible-compat, ansible-lint @@ -110,18 +121,19 @@ tinycss2==1.4.0 # via cairosvg, cssselect2 toml-sort==0.24.2 # via molecule (pyproject.toml) tomli==2.2.1 # via black, coverage, mypy, pydoclint, pylint, pyproject-api, pytest, tox -tomlkit==0.13.2 # via pylint, toml-sort +tomlkit==0.13.3 # via pylint, toml-sort tox==4.26.0 # via molecule (pyproject.toml) -types-jsonschema==4.23.0.20250516 # via molecule (pyproject.toml) +types-jsonschema==4.24.0.20250528 # via molecule (pyproject.toml) types-pexpect==4.9.0.20250516 # via molecule (pyproject.toml) types-pyyaml==6.0.12.20250516 # via molecule (pyproject.toml) +tzdata==2025.2 # via ansible-navigator urllib3==2.4.0 # via docker, requests virtualenv==20.31.2 # via pre-commit, tox watchdog==6.0.0 # via mkdocs wcmatch==10.0 # via ansible-lint, molecule (pyproject.toml) webencodings==0.5.1 # via cssselect2, tinycss2 yamllint==1.37.1 # via ansible-lint -zipp==3.21.0 # via importlib-metadata +zipp==3.23.0 # via importlib-metadata # The following packages were excluded from the output: # ansible-core diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/.config/requirements-test.txt new/molecule-25.6.0/.config/requirements-test.txt --- old/molecule-25.5.0/.config/requirements-test.txt 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/.config/requirements-test.txt 2025-06-18 14:43:16.000000000 +0200 @@ -1,5 +1,6 @@ ansi2html >= 1.8.0 ansible-lint >= 6.12.1 +ansible-navigator coverage[toml] docker >= 7.1.0 # testing filelock >= 3.9.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/.gitignore new/molecule-25.6.0/.gitignore --- old/molecule-25.5.0/.gitignore 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/.gitignore 2025-06-18 14:43:16.000000000 +0200 @@ -181,3 +181,6 @@ # docs output _readthedocs/ .ansible + +# Ignore ansible-navigator artifact files in test scenarios +tests/fixtures/integration/**/converge-artifact-*.json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/.pre-commit-config.yaml new/molecule-25.6.0/.pre-commit-config.yaml --- old/molecule-25.5.0/.pre-commit-config.yaml 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/.pre-commit-config.yaml 2025-06-18 14:43:16.000000000 +0200 @@ -23,7 +23,7 @@ - id: trailing-whitespace - repo: https://github.com/asottile/add-trailing-comma.git - rev: v3.1.0 + rev: v3.2.0 hooks: - id: add-trailing-comma @@ -57,18 +57,18 @@ - id: tox-ini-fmt - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: v0.11.12 hooks: - id: ruff entry: sh -c 'ruff check --fix --force-exclude && ruff format --force-exclude' - repo: https://github.com/streetsidesoftware/cspell-cli - rev: v8.19.1 + rev: v9.0.1 hooks: - id: cspell name: Spell check with cspell - repo: https://github.com/jsh9/pydoclint - rev: "0.6.6" + rev: "0.6.7" hooks: - id: pydoclint # This allows automatic reduction of the baseline file when needed. @@ -94,7 +94,7 @@ - wcmatch - repo: https://github.com/pre-commit/mirrors-mypy.git - rev: v1.15.0 + rev: v1.16.0 hooks: - id: mypy additional_dependencies: @@ -139,7 +139,7 @@ additional_dependencies: - uv>=0.5.21 - repo: https://github.com/ansible/ansible-lint - rev: v25.4.0 + rev: v25.5.0 hooks: - id: ansible-lint stages: [manual] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/PKG-INFO new/molecule-25.6.0/PKG-INFO --- old/molecule-25.5.0/PKG-INFO 2025-05-26 09:02:16.531381800 +0200 +++ new/molecule-25.6.0/PKG-INFO 2025-06-18 14:43:25.732193200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: molecule -Version: 25.5.0 +Version: 25.6.0 Summary: Molecule aids in the development and testing of Ansible roles Author-email: Ansible by Red Hat <i...@ansible.com> Maintainer-email: Ansible by Red Hat <i...@ansible.com> @@ -50,6 +50,7 @@ Provides-Extra: test Requires-Dist: ansi2html>=1.8.0; extra == "test" Requires-Dist: ansible-lint>=6.12.1; extra == "test" +Requires-Dist: ansible-navigator; extra == "test" Requires-Dist: coverage[toml]; extra == "test" Requires-Dist: docker>=7.1.0; extra == "test" Requires-Dist: filelock>=3.9.0; extra == "test" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/docs/faq.md new/molecule-25.6.0/docs/faq.md --- old/molecule-25.5.0/docs/faq.md 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/docs/faq.md 2025-06-18 14:43:16.000000000 +0200 @@ -111,3 +111,19 @@ - role: <your-dependee-role> when: lookup('env', 'MOLECULE_FILE') ``` + +## How can I execute molecule playbooks inside of an execution-environment? + +By default, Molecule uses ansible-playbook to execute the playbooks. +However, it has the ability to test playbooks inside of an execution-environment +using ansible-navigator as executor backend. This is done by setting +`ansible_navigator` as the backend in `executor` section of `molecule.yml`. +(Note: This feature is experimental and under development). + +```yaml +--- +executor: + backend: ansible-navigator +provisioner: + name: ansible +``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/_version.py new/molecule-25.6.0/src/molecule/_version.py --- old/molecule-25.5.0/src/molecule/_version.py 2025-05-26 09:02:16.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/_version.py 2025-06-18 14:43:25.000000000 +0200 @@ -17,5 +17,5 @@ __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE -__version__ = version = '25.5.0' -__version_tuple__ = version_tuple = (25, 5, 0) +__version__ = version = '25.6.0' +__version_tuple__ = version_tuple = (25, 6, 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/base.py new/molecule-25.6.0/src/molecule/command/base.py --- old/molecule-25.5.0/src/molecule/command/base.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/base.py 2025-06-18 14:43:16.000000000 +0200 @@ -435,6 +435,12 @@ Returns: Function with click options for scenario_name, exclude, all, and report added. """ + # NOTE: because click.option is a decorator, options applied this way will appear in the opposite order. + func = click.option( + "--shared-inventory/--no-shared-inventory", + default=False, + help="EXPERIMENTAL: Enable or disable sharing inventory between scenarios. Default is disabled.", + )(func) func = click.option( "--report/--no-report", default=False, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/check.py new/molecule-25.6.0/src/molecule/command/check.py --- old/molecule-25.5.0/src/molecule/command/check.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/check.py 2025-06-18 14:43:16.000000000 +0200 @@ -60,32 +60,36 @@ default=MOLECULE_PARALLEL, help="Enable or disable parallel mode. Default is disabled.", ) -def check( # pragma: no cover +def check( # noqa: PLR0913 ctx: click.Context, /, scenario_name: list[str] | None, exclude: list[str], __all: bool, # noqa: FBT001 *, - report: bool, parallel: bool, -) -> None: + report: bool, + shared_inventory: bool, +) -> None: # pragma: no cover """Use the provisioner to perform a Dry-Run (destroy, dependency, create, prepare, converge). + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. - report: Whether to show an after-run summary report. parallel: Whether the scenario(s) should be run in parallel. - """ + report: Whether to show an after-run summary report. + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = { "parallel": parallel, "subcommand": subcommand, "report": report, + "shared_inventory": shared_inventory, } if __all: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/cleanup.py new/molecule-25.6.0/src/molecule/command/cleanup.py --- old/molecule-25.5.0/src/molecule/command/cleanup.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/cleanup.py 2025-06-18 14:43:16.000000000 +0200 @@ -66,21 +66,28 @@ *, __all: bool, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to cleanup any changes. Any changes made to external systems during the stages of testing. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 - command_args: CommandArgs = {"subcommand": subcommand, "report": report} + command_args: CommandArgs = { + "subcommand": subcommand, + "report": report, + "shared_inventory": shared_inventory, + } if __all: scenario_name = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/converge.py new/molecule-25.6.0/src/molecule/command/converge.py --- old/molecule-25.5.0/src/molecule/command/converge.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/converge.py 2025-06-18 14:43:16.000000000 +0200 @@ -55,29 +55,36 @@ @click.pass_context @base.click_command_options @click.argument("ansible_args", nargs=-1, type=click.UNPROCESSED) -def converge( +def converge( # noqa: PLR0913 ctx: click.Context, /, scenario_name: list[str] | None, exclude: list[str], __all: bool, # noqa: FBT001 *, - report: bool, ansible_args: tuple[str], + report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to configure instances (dependency, create, prepare converge). + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. - report: Whether to show an after-run summary report. ansible_args: Arguments to forward to Ansible. - """ + report: Whether to show an after-run summary report. + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 - command_args: CommandArgs = {"subcommand": subcommand, "report": report} + command_args: CommandArgs = { + "subcommand": subcommand, + "report": report, + "shared_inventory": shared_inventory, + } if __all: scenario_name = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/create.py new/molecule-25.6.0/src/molecule/command/create.py --- old/molecule-25.5.0/src/molecule/command/create.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/create.py 2025-06-18 14:43:16.000000000 +0200 @@ -70,7 +70,7 @@ type=click.Choice([str(s) for s in drivers()]), help=f"Name of driver to use. ({DEFAULT_DRIVER})", ) -def create( +def create( # noqa: PLR0913 ctx: click.Context, /, scenario_name: list[str] | None, @@ -79,9 +79,11 @@ __all: bool, # noqa: FBT001 *, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to start the instances. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. @@ -89,13 +91,15 @@ driver_name: Name of the Molecule driver to use. __all: Whether molecule should target scenario_name or all scenarios. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = { "subcommand": subcommand, "driver_name": driver_name, "report": report, + "shared_inventory": shared_inventory, } if __all: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/dependency.py new/molecule-25.6.0/src/molecule/command/dependency.py --- old/molecule-25.5.0/src/molecule/command/dependency.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/dependency.py 2025-06-18 14:43:16.000000000 +0200 @@ -61,19 +61,26 @@ *, __all: bool, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Manage the role's dependencies. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 - command_args: CommandArgs = {"subcommand": subcommand, "report": report} + command_args: CommandArgs = { + "subcommand": subcommand, + "report": report, + "shared_inventory": shared_inventory, + } if __all: scenario_name = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/destroy.py new/molecule-25.6.0/src/molecule/command/destroy.py --- old/molecule-25.5.0/src/molecule/command/destroy.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/destroy.py 2025-06-18 14:43:16.000000000 +0200 @@ -83,9 +83,11 @@ *, parallel: bool, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to destroy the instances. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. @@ -94,7 +96,8 @@ __all: Whether molecule should target scenario_name or all scenarios. parallel: Whether the scenario(s) should be run in parallel mode. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = { @@ -102,6 +105,7 @@ "subcommand": subcommand, "driver_name": driver_name, "report": report, + "shared_inventory": shared_inventory, } if __all: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/drivers.py new/molecule-25.6.0/src/molecule/command/drivers.py --- old/molecule-25.5.0/src/molecule/command/drivers.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/drivers.py 2025-06-18 14:43:16.000000000 +0200 @@ -48,10 +48,11 @@ ) -> None: # pragma: no cover """List drivers. + \f Args: ctx: Click context object holding commandline arguments. format: Output format to use. - """ + """ # noqa: D301 drivers = [] # pylint: disable=redefined-outer-name for driver in api.drivers().values(): description = str(driver) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/idempotence.py new/molecule-25.6.0/src/molecule/command/idempotence.py --- old/molecule-25.5.0/src/molecule/command/idempotence.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/idempotence.py 2025-06-18 14:43:16.000000000 +0200 @@ -124,29 +124,37 @@ @click.pass_context @base.click_command_options @click.argument("ansible_args", nargs=-1, type=click.UNPROCESSED) -def idempotence( +def idempotence( # noqa: PLR0913 ctx: click.Context, scenario_name: list[str] | None, exclude: list[str], __all: bool, # noqa: FBT001 - report: bool, # noqa: FBT001 + *, ansible_args: tuple[str, ...], + report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to configure the instances. After parse the output to determine idempotence. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. - report: Whether to show an after-run summary report. ansible_args: Arguments to forward to Ansible. - """ + report: Whether to show an after-run summary report. + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 - command_args: CommandArgs = {"subcommand": subcommand, "report": report} + command_args: CommandArgs = { + "subcommand": subcommand, + "report": report, + "shared_inventory": shared_inventory, + } if __all: scenario_name = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/init/scenario.py new/molecule-25.6.0/src/molecule/command/init/scenario.py --- old/molecule-25.5.0/src/molecule/command/init/scenario.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/init/scenario.py 2025-06-18 14:43:16.000000000 +0200 @@ -195,13 +195,14 @@ If name is not specified the 'default' value will be used. + \f Args: ctx: Click context object holding commandline arguments. dependency_name: Name of dependency to initialize. driver_name: Name of driver to use. provisioner_name: Name of provisioner to use. scenario_name: Name of scenario to initialize. - """ + """ # noqa: D301 command_args: CommandArgs = { "dependency_name": dependency_name, "driver_name": driver_name, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/list.py new/molecule-25.6.0/src/molecule/command/list.py --- old/molecule-25.5.0/src/molecule/command/list.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/list.py 2025-06-18 14:43:16.000000000 +0200 @@ -80,11 +80,12 @@ ) -> None: # pragma: no cover """List status of instances. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. format: Output format type. - """ + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = {"subcommand": subcommand, "format": format} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/login.py new/molecule-25.6.0/src/molecule/command/login.py --- old/molecule-25.5.0/src/molecule/command/login.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/login.py 2025-06-18 14:43:16.000000000 +0200 @@ -136,11 +136,12 @@ ) -> None: # pragma: no cover """Log in to one instance. + \f Args: ctx: Click context object holding commandline arguments. host: Host to access. scenario_name: Name of the scenario to target. - """ + """ # noqa: D301 args = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = {"subcommand": subcommand, "host": host} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/matrix.py new/molecule-25.6.0/src/molecule/command/matrix.py --- old/molecule-25.5.0/src/molecule/command/matrix.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/matrix.py 2025-06-18 14:43:16.000000000 +0200 @@ -87,11 +87,12 @@ ) -> None: # pragma: no cover """List matrix of steps used to test instances. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. subcommand: Subcommand to target. - """ + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") command_args: CommandArgs = {"subcommand": subcommand} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/prepare.py new/molecule-25.6.0/src/molecule/command/prepare.py --- old/molecule-25.5.0/src/molecule/command/prepare.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/prepare.py 2025-06-18 14:43:16.000000000 +0200 @@ -138,9 +138,11 @@ *, force: bool, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to prepare the instances into a particular starting state. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. @@ -149,7 +151,8 @@ __all: Whether molecule should target scenario_name or all scenarios. force: Whether to use force mode. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = { @@ -157,6 +160,7 @@ "driver_name": driver_name, "force": force, "report": report, + "shared_inventory": shared_inventory, } if __all: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/reset.py new/molecule-25.6.0/src/molecule/command/reset.py --- old/molecule-25.5.0/src/molecule/command/reset.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/reset.py 2025-06-18 14:43:16.000000000 +0200 @@ -52,10 +52,11 @@ ) -> None: # pragma: no cover """Reset molecule temporary folders. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. - """ + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = {"subcommand": subcommand} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/side_effect.py new/molecule-25.6.0/src/molecule/command/side_effect.py --- old/molecule-25.5.0/src/molecule/command/side_effect.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/side_effect.py 2025-06-18 14:43:16.000000000 +0200 @@ -69,19 +69,26 @@ *, __all: bool, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to perform side-effects to the instances. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 - command_args: CommandArgs = {"subcommand": subcommand, "report": report} + command_args: CommandArgs = { + "subcommand": subcommand, + "report": report, + "shared_inventory": shared_inventory, + } if __all: scenario_name = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/syntax.py new/molecule-25.6.0/src/molecule/command/syntax.py --- old/molecule-25.5.0/src/molecule/command/syntax.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/syntax.py 2025-06-18 14:43:16.000000000 +0200 @@ -61,19 +61,26 @@ *, __all: bool, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Use the provisioner to syntax check the role. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 - command_args: CommandArgs = {"subcommand": subcommand, "report": report} + command_args: CommandArgs = { + "subcommand": subcommand, + "report": report, + "shared_inventory": shared_inventory, + } if __all: scenario_name = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/test.py new/molecule-25.6.0/src/molecule/command/test.py --- old/molecule-25.5.0/src/molecule/command/test.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/test.py 2025-06-18 14:43:16.000000000 +0200 @@ -95,9 +95,11 @@ ansible_args: tuple[str, ...], platform_name: str, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Test (dependency, cleanup, destroy, syntax, create, prepare, converge, idempotence, side_effect, verify, cleanup, destroy). + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. @@ -109,7 +111,8 @@ ansible_args: Arguments to forward to Ansible. platform_name: Name of the platform to use. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = { @@ -119,6 +122,7 @@ "driver_name": driver_name, "platform_name": platform_name, "report": report, + "shared_inventory": shared_inventory, } if __all: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/command/verify.py new/molecule-25.6.0/src/molecule/command/verify.py --- old/molecule-25.5.0/src/molecule/command/verify.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/command/verify.py 2025-06-18 14:43:16.000000000 +0200 @@ -60,19 +60,26 @@ *, __all: bool, report: bool, + shared_inventory: bool, ) -> None: # pragma: no cover """Run automated tests against instances. + \f Args: ctx: Click context object holding commandline arguments. scenario_name: Name of the scenario to target. exclude: Name of the scenarios to avoid targeting. __all: Whether molecule should target scenario_name or all scenarios. report: Whether to show an after-run summary report. - """ + shared_inventory: Whether the inventory should be shared between scenarios. + """ # noqa: D301 args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 - command_args: CommandArgs = {"subcommand": subcommand, "report": report} + command_args: CommandArgs = { + "subcommand": subcommand, + "report": report, + "shared_inventory": shared_inventory, + } if __all: scenario_name = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/config.py new/molecule-25.6.0/src/molecule/config.py --- old/molecule-25.5.0/src/molecule/config.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/config.py 2025-06-18 14:43:16.000000000 +0200 @@ -158,6 +158,15 @@ return self.command_args.get("parallel", False) @property + def shared_inventory(self) -> bool: + """Should molecule share ephemeral data. + + Returns: + Whether molecule should share ephemeral data. + """ + return self.command_args.get("shared_inventory", False) + + @property def platform_name(self) -> str | None: """Configured platform. @@ -316,6 +325,15 @@ return driver @property + def executor(self) -> str: + """Return playbook executor. + + Returns: + The executor backend. + """ + return self.config.get("executor", {}).get("backend", "ansible-playbook") + + @property def env(self) -> dict[str, str]: """Environment variables. @@ -563,6 +581,9 @@ "ssh_connection_options": [], "safe_files": [], }, + "executor": { + "backend": "ansible-playbook", + }, "platforms": [], "prerun": True, "role_name_check": 0, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/data/molecule.json new/molecule-25.6.0/src/molecule/data/molecule.json --- old/molecule-25.5.0/src/molecule/data/molecule.json 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/data/molecule.json 2025-06-18 14:43:16.000000000 +0200 @@ -130,6 +130,17 @@ "title": "MoleculeDriverOptionsModel", "type": "object" }, + "MoleculeExecutorModel": { + "additionalProperties": false, + "properties": { + "backend": { + "title": "Backend for playbook executor", + "type": "string" + } + }, + "title": "MoleculeExecutorModel", + "type": "object" + }, "MoleculePlatformModel": { "additionalProperties": true, "properties": { @@ -534,6 +545,9 @@ "driver": { "$ref": "#/$defs/MoleculeDriverModel" }, + "executor": { + "$ref": "#/$defs/MoleculeExecutorModel" + }, "lint": { "title": "Lint", "type": "string", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/driver/base.py new/molecule-25.6.0/src/molecule/driver/base.py --- old/molecule-25.5.0/src/molecule/driver/base.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/driver/base.py 2025-06-18 14:43:16.000000000 +0200 @@ -33,6 +33,8 @@ if TYPE_CHECKING: + from typing import Any + from molecule.config import Config from molecule.types import DriverOptions @@ -131,7 +133,7 @@ def ansible_connection_options( self, instance_name: str, - ) -> dict[str, str]: # pragma: no cover + ) -> dict[str, Any]: # pragma: no cover """Ansible specific connection options supplied to inventory and returns a dict. Args: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/driver/delegated.py new/molecule-25.6.0/src/molecule/driver/delegated.py --- old/molecule-25.5.0/src/molecule/driver/delegated.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/driver/delegated.py 2025-06-18 14:43:16.000000000 +0200 @@ -24,7 +24,7 @@ import logging from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from molecule import util from molecule.api import Driver @@ -32,6 +32,8 @@ if TYPE_CHECKING: + from typing import Any + from molecule.config import Config @@ -242,7 +244,7 @@ def ansible_connection_options( self, instance_name: str, - ) -> dict[str, str]: + ) -> dict[str, Any]: """Ansible connection options. Args: @@ -286,15 +288,15 @@ conn_dict["ansible_ssh_common_args"] = " ".join( self.ssh_connection_options, ) - - return conn_dict # noqa: TRY300 - except StopIteration: return {} except OSError: # Instance has yet to be provisioned , therefore the # instance_config is not on disk. return {} + else: + return conn_dict + return self.options.get("ansible_connection_options", {}) def _created(self) -> str: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/provisioner/ansible_playbook.py new/molecule-25.6.0/src/molecule/provisioner/ansible_playbook.py --- old/molecule-25.5.0/src/molecule/provisioner/ansible_playbook.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/provisioner/ansible_playbook.py 2025-06-18 14:43:16.000000000 +0200 @@ -23,6 +23,7 @@ import logging import shlex +import subprocess import warnings from typing import TYPE_CHECKING @@ -72,7 +73,12 @@ self._env = self._config.provisioner.env def bake(self) -> None: - """Bake an ``ansible-playbook`` command so it's ready to execute.""" + """Bake ``ansible-playbook`` or ``navigator run`` command so it's ready to execute. + + Raises: + ValueError: when backend is incorrect. + RuntimeError: when ansible-playbook or ansible-navigator is not available. + """ if not self._playbook: return @@ -101,22 +107,53 @@ else: ansible_args = [] - self._ansible_command = [ - "ansible-playbook", - *util.dict2args(options), - *util.bool2args(verbose_flag), - *ansible_args, - self._playbook, # must always go last - ] + backend = self._config.executor + + if backend: + try: + result = subprocess.run( + [backend, "--version"], + capture_output=True, + text=True, + check=True, + ) + LOG.info("%s version: %s", backend, result.stdout.strip()) + except subprocess.CalledProcessError as exc: + msg = f"{backend} is not available. Please ensure that it is installed." + raise RuntimeError(msg) from exc + + if backend == "ansible-playbook": + self._ansible_command = [ + "ansible-playbook", + *util.dict2args(options), + *util.bool2args(verbose_flag), + *ansible_args, + self._playbook, # must always go last + ] + + elif backend == "ansible-navigator": + self._ansible_command = [ + "ansible-navigator", + "run", + self._playbook, + "--mode", + "stdout", + *util.dict2args(options), + *util.bool2args(verbose_flag), + *ansible_args, + ] + else: + msg = f"Unsupported backend: {backend}" + raise ValueError(msg) def execute(self, action_args: list[str] | None = None) -> str: # noqa: ARG002 - """Execute ``ansible-playbook``. + """Execute ``ansible-playbook`` or ``ansible-navigator run``. Args: action_args: Arguments to forward to the action. Unused. Returns: - Output from ansible-playbook. + Output from ansible-playbook or ansible-navigator. Raises: ScenarioFailureError: when Ansible returns nonzero code. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/scenario.py new/molecule-25.6.0/src/molecule/scenario.py --- old/molecule-25.5.0/src/molecule/scenario.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/scenario.py 2025-06-18 14:43:16.000000000 +0200 @@ -146,6 +146,7 @@ path = self.config.runtime.cache_dir / "tmp" / project_scenario_directory else: path = Path(os.getenv("MOLECULE_EPHEMERAL_DIRECTORY", "")) + path.mkdir(parents=True, exist_ok=True) if self.config.is_parallel and not self._lock: lock_file = path / ".lock" @@ -168,6 +169,24 @@ return path.absolute().as_posix() + @cached_property + def shared_ephemeral_directory(self) -> str: + """Acquire the shared ephemeral directory. + + Returns: + The common ephemeral directory for all scenarios. + """ + path: Path + if "MOLECULE_EPHEMERAL_DIRECTORY" not in os.environ: + project_directory = Path(self.config.project_directory).name + + project_scenario_directory = f"molecule.{checksum(project_directory, 4)}" + path = self.config.runtime.cache_dir / "tmp" / project_scenario_directory + else: + path = Path(os.getenv("MOLECULE_EPHEMERAL_DIRECTORY", "")) + + return path.absolute().as_posix() + @property def inventory_directory(self) -> str: """Inventory directory. @@ -175,8 +194,12 @@ Returns: The directory containing the scenario's inventory. """ - path = Path(self.ephemeral_directory) / "inventory" - return str(path) + if self.config.shared_inventory and not self.config.is_parallel: + ephemeral = Path(self.shared_ephemeral_directory) + else: + ephemeral = Path(self.ephemeral_directory) + + return str(ephemeral / "inventory") @property def check_sequence(self) -> list[str]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/shell.py new/molecule-25.6.0/src/molecule/shell.py --- old/molecule-25.5.0/src/molecule/shell.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/shell.py 2025-06-18 14:43:16.000000000 +0200 @@ -153,13 +153,14 @@ eval "$(_MOLECULE_COMPLETE=SHELL_source molecule)" + \f Args: ctx: Click context object. debug: Debug option value. verbose: Verbose option value. base_config: Base config option value. env_file: Environment variable file option value. - """ + """ # noqa: D301 ctx.obj = {} ctx.obj["args"] = {} ctx.obj["args"]["debug"] = debug diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule/types.py new/molecule-25.6.0/src/molecule/types.py --- old/molecule-25.5.0/src/molecule/types.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/src/molecule/types.py 2025-06-18 14:43:16.000000000 +0200 @@ -88,6 +88,16 @@ safe_files: list[str] +class ExecutorData(TypedDict, total=False): + """Molecule playbook executor configuration. + + Attributes: + backend: The backend to use for executing playbooks. + """ + + backend: str + + class InventoryData(TypedDict): """Inventory data for a molecule run. @@ -216,6 +226,7 @@ Attributes: dependency: Dependency config. driver: Driver config. + executor: Executor config. platforms: List of platforms. prerun: Should prerun tasks be run. role_name_check: ??? @@ -226,6 +237,7 @@ dependency: DependencyData driver: DriverData + executor: ExecutorData platforms: list[PlatformData] prerun: bool role_name_check: int @@ -265,6 +277,7 @@ platform_name: Name of the platform to target. report: Whether to show an after-run summary report. scenario_name: Name of the scenario to target. + shared_inventory: Whether inventory should be shared between scenarios. subcommand: Name of subcommand being run. """ @@ -277,6 +290,7 @@ platform_name: str report: bool scenario_name: str + shared_inventory: bool subcommand: str diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule.egg-info/PKG-INFO new/molecule-25.6.0/src/molecule.egg-info/PKG-INFO --- old/molecule-25.5.0/src/molecule.egg-info/PKG-INFO 2025-05-26 09:02:16.000000000 +0200 +++ new/molecule-25.6.0/src/molecule.egg-info/PKG-INFO 2025-06-18 14:43:25.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: molecule -Version: 25.5.0 +Version: 25.6.0 Summary: Molecule aids in the development and testing of Ansible roles Author-email: Ansible by Red Hat <i...@ansible.com> Maintainer-email: Ansible by Red Hat <i...@ansible.com> @@ -50,6 +50,7 @@ Provides-Extra: test Requires-Dist: ansi2html>=1.8.0; extra == "test" Requires-Dist: ansible-lint>=6.12.1; extra == "test" +Requires-Dist: ansible-navigator; extra == "test" Requires-Dist: coverage[toml]; extra == "test" Requires-Dist: docker>=7.1.0; extra == "test" Requires-Dist: filelock>=3.9.0; extra == "test" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule.egg-info/SOURCES.txt new/molecule-25.6.0/src/molecule.egg-info/SOURCES.txt --- old/molecule-25.5.0/src/molecule.egg-info/SOURCES.txt 2025-05-26 09:02:16.000000000 +0200 +++ new/molecule-25.6.0/src/molecule.egg-info/SOURCES.txt 2025-06-18 14:43:25.000000000 +0200 @@ -178,6 +178,10 @@ tests/fixtures/integration/test_command/molecule/podman/tasks/create-fail.yml tests/fixtures/integration/test_command/molecule/smoke/converge.yml tests/fixtures/integration/test_command/molecule/smoke/molecule.yml +tests/fixtures/integration/test_command/molecule/test-scenario/converge.yml +tests/fixtures/integration/test_command/molecule/test-scenario/molecule.yml +tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/converge.yml +tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/molecule.yml tests/fixtures/integration/test_command/scenarios/cleanup/molecule/default/cleanup.yml tests/fixtures/integration/test_command/scenarios/cleanup/molecule/default/converge.yml tests/fixtures/integration/test_command/scenarios/cleanup/molecule/default/molecule.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/src/molecule.egg-info/requires.txt new/molecule-25.6.0/src/molecule.egg-info/requires.txt --- old/molecule-25.5.0/src/molecule.egg-info/requires.txt 2025-05-26 09:02:16.000000000 +0200 +++ new/molecule-25.6.0/src/molecule.egg-info/requires.txt 2025-06-18 14:43:25.000000000 +0200 @@ -18,6 +18,7 @@ [test] ansi2html>=1.8.0 ansible-lint>=6.12.1 +ansible-navigator coverage[toml] docker>=7.1.0 filelock>=3.9.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/kubevirt/molecule.yml new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/kubevirt/molecule.yml --- old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/kubevirt/molecule.yml 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/kubevirt/molecule.yml 2025-06-18 14:43:16.000000000 +0200 @@ -4,6 +4,8 @@ options: requirements-file: requirements.yml role-file: requirements.yml +executor: + backend: ansible-playbook platforms: - name: rhel9 image: registry.redhat.io/rhel9/rhel-guest-image diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario/converge.yml new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario/converge.yml --- old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario/converge.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario/converge.yml 2025-06-18 14:43:16.000000000 +0200 @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + gather_facts: false + tasks: + - name: Replace this task with one that validates your content + ansible.builtin.debug: + msg: "This is the effective test" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario/molecule.yml new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario/molecule.yml --- old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario/molecule.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario/molecule.yml 2025-06-18 14:43:16.000000000 +0200 @@ -0,0 +1,7 @@ +--- +executor: + backend: ansible-playbook +platforms: + - name: localhost +provisioner: + name: ansible diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/converge.yml new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/converge.yml --- old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/converge.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/converge.yml 2025-06-18 14:43:16.000000000 +0200 @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + gather_facts: false + tasks: + - name: Replace this task with one that validates your content + ansible.builtin.debug: + msg: "This is the effective test" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/molecule.yml new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/molecule.yml --- old/molecule-25.5.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/molecule.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/molecule-25.6.0/tests/fixtures/integration/test_command/molecule/test-scenario-for-nav/molecule.yml 2025-06-18 14:43:16.000000000 +0200 @@ -0,0 +1,7 @@ +--- +executor: + backend: ansible-navigator +platforms: + - name: localhost +provisioner: + name: ansible diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/integration/test_command.py new/molecule-25.6.0/tests/integration/test_command.py --- old/molecule-25.5.0/tests/integration/test_command.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/tests/integration/test_command.py 2025-06-18 14:43:16.000000000 +0200 @@ -102,7 +102,7 @@ else: result = run(["docker", "context", "inspect", "--format", "{{.Endpoints.docker.Host}}"]) if result.returncode != 0: - failure = "Docker was not able to report its currently active socket for current context: {result}" + failure = f"Docker was not able to report its currently active socket for current context: {result.stderr.rstrip()}" else: docker_current_socket = result.stdout.rstrip() warnings.warn( @@ -499,3 +499,36 @@ command = ["molecule", "test", "--scenario-name", "smoke"] result = run(command) assert result.returncode == 0, result + + +def test_with_backend_as_ansible_playbook( + monkeypatch: pytest.MonkeyPatch, + test_fixture_dir: Path, +) -> None: + """Execute test-scenario (smoke test) that should spot potentially breaking changes. + + Args: + monkeypatch: Pytest fixture. + test_fixture_dir: Path to the test fixture directory. + """ + monkeypatch.chdir(test_fixture_dir) + command = ["molecule", "test", "--scenario-name", "test-scenario"] + result = run(command) + assert result.returncode == 0, result + + +@mac_on_gh +def test_with_backend_as_ansible_navigator( + monkeypatch: pytest.MonkeyPatch, + test_fixture_dir: Path, +) -> None: + """Execute test-scenario-for-nav (smoke test) that should spot potentially breaking changes. + + Args: + monkeypatch: Pytest fixture. + test_fixture_dir: Path to the test fixture directory. + """ + monkeypatch.chdir(test_fixture_dir) + command = ["molecule", "test", "--scenario-name", "test-scenario-for-nav"] + result = run(command) + assert result.returncode == 0, result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/unit/provisioner/test_ansible.py new/molecule-25.6.0/tests/unit/provisioner/test_ansible.py --- old/molecule-25.5.0/tests/unit/provisioner/test_ansible.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/tests/unit/provisioner/test_ansible.py 2025-06-18 14:43:16.000000000 +0200 @@ -22,6 +22,7 @@ import collections import os +from pathlib import Path from typing import TYPE_CHECKING, Any import pytest @@ -37,6 +38,8 @@ from pytest_mock import MockerFixture + from molecule.provisioner.ansible import Ansible + @pytest.fixture def _patched_ansible_playbook(mocker: MockerFixture) -> MagicMock: @@ -260,9 +263,38 @@ assert instance.links == {} -def test_inventory_directory_property(instance): # type: ignore[no-untyped-def] # noqa: ANN201, D103 - x = os.path.join(instance._config.scenario.ephemeral_directory, "inventory") # noqa: PTH118 - assert x == instance.inventory_directory +def test_inventory_directory_property(instance: Ansible) -> None: + """Test the inventory_directory property. + + Args: + instance: Ansible provisioner instance. + """ + x = Path(instance._config.scenario.ephemeral_directory, "inventory") + assert str(x) == instance.inventory_directory + + +def test_inventory_directory_property_shared(instance: Ansible) -> None: + """Test the shared_inventory_directory property. + + Args: + instance: Ansible provisioner instance. + """ + instance._config.command_args["shared_inventory"] = True + x = Path(instance._config.scenario.shared_ephemeral_directory, "inventory") + assert str(x) == instance.inventory_directory + + +def test_inventory_directory_property_shared_parallel(instance: Ansible) -> None: + """Test the shared_inventory_directory property with parallel mode on. + + Args: + instance: Ansible provisioner instance. + """ + instance._config.command_args["shared_inventory"] = True + instance._config.command_args["parallel"] = True + # Parallel disables shared ephemeral directory + x = Path(instance._config.scenario.ephemeral_directory, "inventory") + assert str(x) == instance.inventory_directory def test_inventory_file_property(instance): # type: ignore[no-untyped-def] # noqa: ANN201, D103 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/unit/provisioner/test_ansible_playbook.py new/molecule-25.6.0/tests/unit/provisioner/test_ansible_playbook.py --- old/molecule-25.5.0/tests/unit/provisioner/test_ansible_playbook.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/tests/unit/provisioner/test_ansible_playbook.py 2025-06-18 14:43:16.000000000 +0200 @@ -127,6 +127,28 @@ assert _instance._ansible_command == args +def test_bake_with_ansible_navigator(_inventory_directory, _instance): # type: ignore[no-untyped-def] # noqa: ANN201, PT019, D103 + pb = _instance._config.provisioner.playbooks.converge + _instance._playbook = pb + _instance._config.config["executor"]["backend"] = "ansible-navigator" + _instance.bake() + + args = [ + "ansible-navigator", + "run", + pb, + "--mode", + "stdout", + "--become", + "--inventory", + _inventory_directory, + "--skip-tags", + "molecule-notest,notest", + ] + + assert _instance._ansible_command == args + + def test_bake_removes_non_interactive_options_from_non_converge_playbooks( # type: ignore[no-untyped-def] # noqa: ANN201, D103 _inventory_directory, # noqa: PT019 _instance, # noqa: PT019 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/molecule-25.5.0/tests/unit/test_logger.py new/molecule-25.6.0/tests/unit/test_logger.py --- old/molecule-25.5.0/tests/unit/test_logger.py 2025-05-26 09:02:07.000000000 +0200 +++ new/molecule-25.6.0/tests/unit/test_logger.py 2025-06-18 14:43:16.000000000 +0200 @@ -82,7 +82,7 @@ get_section_logger_tests, indirect=True, ) -def test_get_section_loggers(_patched_logger_env): # type: ignore[no-untyped-def] # noqa: ANN201, PT019, D103 +def test_get_section_loggers(_patched_logger_env): # type: ignore[no-untyped-def] # noqa: ANN201, D103 expected_section_loggers = _patched_logger_env get_section_loggers.cache_clear() section_loggers = get_section_loggers()