Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ansible-core-2.20 for openSUSE:Factory checked in at 2026-08-11 17:17:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible-core-2.20 (Old) and /work/SRC/openSUSE:Factory/.ansible-core-2.20.new.17972 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible-core-2.20" Tue Aug 11 17:17:09 2026 rev:3 rq:1370645 version:2.20.8 Changes: -------- --- /work/SRC/openSUSE:Factory/ansible-core-2.20/ansible-core-2.20.changes 2026-06-19 17:12:52.580508586 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core-2.20.new.17972/ansible-core-2.20.changes 2026-08-11 17:18:39.101900460 +0200 @@ -1,0 +2,29 @@ +Tue Aug 11 07:21:51 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 2.20.8: + * Minor Changes + - ansible-test - Added a timeout callback that dumps thread + stacks when the test execution deadline defined by + ansible-test env --timeout is approaching. + - ansible-test - Support automatic loading of test collections + in core integration tests. + - parallel fact gathering - the async wrapper now considers the + timeout when determining whether to kill the process running + the module. Previously, a 5 second sleep occurred twice + before checking if the job had remaining time. + * Bugfixes + - ansible-test - Fix target filtering to preserve + user-specified versions that are not in the completion + configuration. + - collection loader - Fix the collection loader logic to + correctly return Python module when calling + pkgutil.iter_modules with a package that is inside a + collection path and contains compiled Python extension + modules. + - parallel fact gathering - fix hang caused by corrupt async + job files. + - rpm_key - ensure a trailing newline is present on PGP armor + data before passing it to librpm for parsing, fixing failures + on systems where pgpParsePkts requires it (#87303). + +------------------------------------------------------------------- Old: ---- ansible_core-2.20.7.tar.gz ansible_core-2.20.7.tar.gz.sha256 New: ---- ansible_core-2.20.8.tar.gz ansible_core-2.20.8.tar.gz.sha256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible-core-2.20.spec ++++++ --- /var/tmp/diff_new_pack.XwOYUq/_old 2026-08-11 17:18:42.138029117 +0200 +++ /var/tmp/diff_new_pack.XwOYUq/_new 2026-08-11 17:18:42.142029287 +0200 @@ -43,7 +43,7 @@ %endif Name: ansible-core-2.20 -Version: 2.20.7 +Version: 2.20.8 Release: 0 Summary: Radically simple IT automation License: GPL-3.0-or-later ++++++ ansible_core-2.20.7.tar.gz -> ansible_core-2.20.8.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/PKG-INFO new/ansible_core-2.20.8/PKG-INFO --- old/ansible_core-2.20.7/PKG-INFO 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/PKG-INFO 2026-08-10 17:50:25.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.20.7 +Version: 2.20.8 Summary: Radically simple IT automation Author: Ansible Project Project-URL: Homepage, https://ansible.com/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/ansible_core.egg-info/PKG-INFO new/ansible_core-2.20.8/ansible_core.egg-info/PKG-INFO --- old/ansible_core-2.20.7/ansible_core.egg-info/PKG-INFO 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/ansible_core.egg-info/PKG-INFO 2026-08-10 17:50:25.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.20.7 +Version: 2.20.8 Summary: Radically simple IT automation Author: Ansible Project Project-URL: Homepage, https://ansible.com/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/ansible_core.egg-info/SOURCES.txt new/ansible_core-2.20.8/ansible_core.egg-info/SOURCES.txt --- old/ansible_core-2.20.7/ansible_core.egg-info/SOURCES.txt 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/ansible_core.egg-info/SOURCES.txt 2026-08-10 17:50:25.000000000 +0200 @@ -1086,6 +1086,17 @@ test/integration/targets/ansible-test-cloud-nios/tasks/main.yml test/integration/targets/ansible-test-cloud-openshift/aliases test/integration/targets/ansible-test-cloud-openshift/tasks/main.yml +test/integration/targets/ansible-test-collection-indirect-by-meta/aliases +test/integration/targets/ansible-test-collection-indirect-by-meta/ansible_collections/ansible_test/ansible_test_collection_indirect_by_meta/plugins/modules/hello.py +test/integration/targets/ansible-test-collection-indirect-by-meta/tasks/main.yml +test/integration/targets/ansible-test-collection-indirect-by-needs-target/aliases +test/integration/targets/ansible-test-collection-indirect-by-needs-target/ansible_collections/ansible_test/ansible_test_collection_indirect_by_needs_target/plugins/modules/hello.py +test/integration/targets/ansible-test-collection-role-dependencies/aliases +test/integration/targets/ansible-test-collection-role-dependencies/meta/main.yml +test/integration/targets/ansible-test-collection-role-dependencies/tasks/main.yml +test/integration/targets/ansible-test-collection-script-dependencies/aliases +test/integration/targets/ansible-test-collection-script-dependencies/runme.sh +test/integration/targets/ansible-test-collection-script-dependencies/runme.yml test/integration/targets/ansible-test-config/aliases test/integration/targets/ansible-test-config/runme.sh test/integration/targets/ansible-test-config-invalid/aliases @@ -1327,6 +1338,14 @@ test/integration/targets/ansible-test-shell/expected-stdout.txt test/integration/targets/ansible-test-shell/runme.sh test/integration/targets/ansible-test-shell/ansible_collections/ns/col/.keep +test/integration/targets/ansible-test-timeout/aliases +test/integration/targets/ansible-test-timeout/runme.sh +test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/aliases +test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/runme.sh +test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/aliases +test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/tasks/main.yml +test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/aliases +test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/runme.sh test/integration/targets/ansible-test-units/aliases test/integration/targets/ansible-test-units/runme.sh test/integration/targets/ansible-test-units-assertions/aliases @@ -1695,6 +1714,12 @@ test/integration/targets/collection/aliases test/integration/targets/collection/setup.sh test/integration/targets/collection/update-ignore.py +test/integration/targets/collection_loader/aliases +test/integration/targets/collection_loader/main.yml +test/integration/targets/collection_loader/runme.sh +test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py +test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py +test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/sub/__init__.py test/integration/targets/collections/a.statichost.yml test/integration/targets/collections/aliases test/integration/targets/collections/cache.statichost.yml @@ -4659,6 +4684,7 @@ test/lib/ansible_test/_internal/provider/source/unsupported.py test/lib/ansible_test/_internal/provider/source/unversioned.py test/lib/ansible_test/_util/__init__.py +test/lib/ansible_test/_util/controller/ansible_collections/ansible_test/_internal/plugins/callback/timeout.py test/lib/ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.json test/lib/ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py test/lib/ansible_test/_util/controller/sanity/code-smell/changelog.json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/changelogs/CHANGELOG-v2.20.rst new/ansible_core-2.20.8/changelogs/CHANGELOG-v2.20.rst --- old/ansible_core-2.20.7/changelogs/CHANGELOG-v2.20.rst 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/changelogs/CHANGELOG-v2.20.rst 2026-08-10 17:50:25.000000000 +0200 @@ -4,6 +4,30 @@ .. contents:: Topics +v2.20.8 +======= + +Release Summary +--------------- + +| Release Date: 2026-08-10 +| `Porting Guide <https://docs.ansible.com/ansible-core/2.20/porting_guides/porting_guide_core_2.20.html>`__ + +Minor Changes +------------- + +- ansible-test - Added a timeout callback that dumps thread stacks when the test execution deadline defined by ``ansible-test env --timeout`` is approaching. +- ansible-test - Support automatic loading of test collections in core integration tests. +- parallel fact gathering - the async wrapper now considers the timeout when determining whether to kill the process running the module. Previously, a 5 second sleep occurred twice before checking if the job had remaining time. + +Bugfixes +-------- + +- ansible-test - Fix target filtering to preserve user-specified versions that are not in the completion configuration. +- collection loader - Fix the collection loader logic to correctly return Python module when calling ``pkgutil.iter_modules`` with a package that is inside a collection path and contains compiled Python extension modules. +- parallel fact gathering - fix hang caused by corrupt async job files. +- rpm_key - ensure a trailing newline is present on PGP armor data before passing it to librpm for parsing, fixing failures on systems where ``pgpParsePkts`` requires it (https://github.com/ansible/ansible/issues/87303). + v2.20.7 ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/changelogs/changelog.yaml new/ansible_core-2.20.8/changelogs/changelog.yaml --- old/ansible_core-2.20.7/changelogs/changelog.yaml 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/changelogs/changelog.yaml 2026-08-10 17:50:25.000000000 +0200 @@ -690,3 +690,49 @@ - fix-cloning-malformed-role-requirements.yml - sort_obfuscation.yml release_date: '2026-06-11' + 2.20.8: + changes: + release_summary: '| Release Date: 2026-08-10 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.20/porting_guides/porting_guide_core_2.20.html>`__ + + ' + codename: Good Times Bad Times + fragments: + - 2.20.8_summary.yaml + release_date: '2026-08-10' + 2.20.8rc1: + changes: + bugfixes: + - ansible-test - Fix target filtering to preserve user-specified versions that + are not in the completion configuration. + - collection loader - Fix the collection loader logic to correctly return Python + module when calling ``pkgutil.iter_modules`` with a package that is inside + a collection path and contains compiled Python extension modules. + - parallel fact gathering - fix hang caused by corrupt async job files. + - rpm_key - ensure a trailing newline is present on PGP armor data before passing + it to librpm for parsing, fixing failures on systems where ``pgpParsePkts`` + requires it (https://github.com/ansible/ansible/issues/87303). + minor_changes: + - ansible-test - Added a timeout callback that dumps thread stacks when the + test execution deadline defined by ``ansible-test env --timeout`` is approaching. + - ansible-test - Support automatic loading of test collections in core integration + tests. + - parallel fact gathering - the async wrapper now considers the timeout when + determining whether to kill the process running the module. Previously, a + 5 second sleep occurred twice before checking if the job had remaining time. + release_summary: '| Release Date: 2026-08-03 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.20/porting_guides/porting_guide_core_2.20.html>`__ + + ' + codename: Good Times Bad Times + fragments: + - 2.20.8rc1_summary.yaml + - 87304-rpm_key-armor-trailing-newline.yml + - ansible-test-auto-collection-loading.yml + - ansible-test-target-filtering.yml + - ansible-test-timeout-callback.yml + - collection-loader-extensions.yml + - fix-short-timeout-async-wrapper.yml + release_date: '2026-08-03' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/lib/ansible/cli/galaxy.py new/ansible_core-2.20.8/lib/ansible/cli/galaxy.py --- old/ansible_core-2.20.7/lib/ansible/cli/galaxy.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/lib/ansible/cli/galaxy.py 2026-08-10 17:50:25.000000000 +0200 @@ -203,7 +203,7 @@ if args[1:3] == ['role', 'login']: display.error( "The login command was removed in late 2020. An API key is now required to publish roles or collections " - "to Galaxy. The key can be found at https://galaxy.ansible.com/me/preferences, and passed to the " + "to Galaxy. The key can be found at https://galaxy.ansible.com/ui/token, and passed to the " "ansible-galaxy CLI via a file at {0} or (insecurely) via the `--token` " "command-line argument.".format(to_text(C.GALAXY_TOKEN_PATH))) sys.exit(1) @@ -238,7 +238,7 @@ common.add_argument('--api-version', type=int, choices=[2, 3], help=argparse.SUPPRESS) # Hidden argument that should only be used in our tests common.add_argument('--token', '--api-key', dest='api_key', help='The Ansible Galaxy API key which can be found at ' - 'https://galaxy.ansible.com/me/preferences.') + 'https://galaxy.ansible.com/ui/token.') common.add_argument('-c', '--ignore-certs', action='store_true', dest='ignore_certs', help='Ignore SSL certificate validation errors.', default=None) # --timeout uses the default None to handle two different scenarios. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/lib/ansible/module_utils/ansible_release.py new/ansible_core-2.20.8/lib/ansible/module_utils/ansible_release.py --- old/ansible_core-2.20.7/lib/ansible/module_utils/ansible_release.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/lib/ansible/module_utils/ansible_release.py 2026-08-10 17:50:25.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.20.7' +__version__ = '2.20.8' __author__ = 'Ansible, Inc.' __codename__ = "Good Times Bad Times" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/lib/ansible/modules/async_wrapper.py new/ansible_core-2.20.8/lib/ansible/modules/async_wrapper.py --- old/ansible_core-2.20.7/lib/ansible/modules/async_wrapper.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/lib/ansible/modules/async_wrapper.py 2026-08-10 17:50:25.000000000 +0200 @@ -310,24 +310,31 @@ os.setpgid(sub_pid, sub_pid) notice("Start watching %s (%s)" % (sub_pid, remaining)) + + if remaining != 0 and step >= remaining: + step = remaining / 2 + time.sleep(step) + while os.waitpid(sub_pid, os.WNOHANG) == (0, 0): - notice("%s still running (%s)" % (sub_pid, remaining)) - time.sleep(step) remaining = remaining - step + notice("%s still running (%s)" % (sub_pid, remaining)) if remaining <= 0: - # ensure we leave response in poll location - res = {'msg': 'Timeout exceeded', 'failed': True, 'child_pid': sub_pid} - jwrite(res) - # actually kill it notice("Timeout reached, now killing %s" % (sub_pid)) os.killpg(sub_pid, signal.SIGKILL) notice("Sent kill to group %s " % sub_pid) time.sleep(1) + + # ensure we leave response in poll location + res = {'msg': 'Timeout exceeded', 'failed': True, 'child_pid': sub_pid} + jwrite(res) + if not preserve_tmp: shutil.rmtree(os.path.dirname(wrapped_module), True) end(res) + + time.sleep(step) notice("Done in kid B.") if not preserve_tmp: shutil.rmtree(os.path.dirname(wrapped_module), True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/lib/ansible/modules/rpm_key.py new/ansible_core-2.20.8/lib/ansible/modules/rpm_key.py --- old/ansible_core-2.20.7/lib/ansible/modules/rpm_key.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/lib/ansible/modules/rpm_key.py 2026-08-10 17:50:25.000000000 +0200 @@ -161,6 +161,9 @@ pkt = ctypes.POINTER(ctypes.c_uint8)() pktlen = ctypes.c_size_t() + if not armor.endswith("\n"): + armor += "\n" + armor_bytes = armor.encode() result = self._lib.pgpParsePkts(armor_bytes, ctypes.byref(pkt), ctypes.byref(pktlen)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/lib/ansible/release.py new/ansible_core-2.20.8/lib/ansible/release.py --- old/ansible_core-2.20.7/lib/ansible/release.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/lib/ansible/release.py 2026-08-10 17:50:25.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.20.7' +__version__ = '2.20.8' __author__ = 'Ansible, Inc.' __codename__ = "Good Times Bad Times" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/lib/ansible/utils/collection_loader/_collection_finder.py new/ansible_core-2.20.8/lib/ansible/utils/collection_loader/_collection_finder.py --- old/ansible_core-2.20.7/lib/ansible/utils/collection_loader/_collection_finder.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/lib/ansible/utils/collection_loader/_collection_finder.py 2026-08-10 17:50:25.000000000 +0200 @@ -6,6 +6,7 @@ from __future__ import annotations +import inspect import itertools import os import os.path @@ -1237,23 +1238,33 @@ prefix = _to_text(prefix) # yield (module_loader, name, ispkg) for each module/pkg under path # TODO: implement ignore/silent catch for unreadable? - for b_path in map(_to_bytes, paths): - if not os.path.isdir(b_path): + # Mostly based off the logic in the builtin pkgutil.iter_modules importer. + # Only difference is we don't check if dirs contains __init__. + # https://github.com/python/cpython/blob/fda056e64bdfcac3dd3d13eebda0a24994d83cb8/Lib/pkgutil.py#L130-L168 + for path in paths: + if not os.path.isdir(path): continue - for b_basename in sorted(os.listdir(b_path)): - b_candidate_module_path = os.path.join(b_path, b_basename) - if os.path.isdir(b_candidate_module_path): + + yielded = {'__init__'} + for basename in sorted(os.listdir(path)): + modname = inspect.getmodulename(basename) + if modname in yielded: + continue + + mod_path = os.path.join(path, basename) + ispkg = False + if not modname and os.path.isdir(mod_path) and '.' not in basename: # exclude things that obviously aren't Python package dirs # FIXME: this dir is adjustable in py3.8+, check for it - if b'.' in b_basename or b_basename == b'__pycache__': + if basename == '__pycache__': continue - # TODO: proper string handling? - yield prefix + _to_text(b_basename), True - else: - # FIXME: match builtin ordering for package/dir/file, support compiled? - if b_basename.endswith(b'.py') and b_basename != b'__init__.py': - yield prefix + _to_text(os.path.splitext(b_basename)[0]), False + modname = basename + ispkg = True + + if modname and '.' not in modname: + yielded.add(modname) + yield prefix + modname, ispkg def _get_collection_metadata(collection_name): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/pyproject.toml new/ansible_core-2.20.8/pyproject.toml --- old/ansible_core-2.20.7/pyproject.toml 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/pyproject.toml 2026-08-10 17:50:25.000000000 +0200 @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools >= 66.1.0, <= 82.0.1", "wheel == 0.45.1"] # lower bound to support controller Python versions, upper bound for latest version tested at release +requires = ["setuptools >= 66.1.0, <= 84.0.0", "wheel == 0.45.1"] # lower bound to support controller Python versions, upper bound for latest version tested at release build-backend = "setuptools.build_meta" [project] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-meta/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-meta/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-meta/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-meta/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1 @@ +hidden diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-meta/ansible_collections/ansible_test/ansible_test_collection_indirect_by_meta/plugins/modules/hello.py new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-meta/ansible_collections/ansible_test/ansible_test_collection_indirect_by_meta/plugins/modules/hello.py --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-meta/ansible_collections/ansible_test/ansible_test_collection_indirect_by_meta/plugins/modules/hello.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-meta/ansible_collections/ansible_test/ansible_test_collection_indirect_by_meta/plugins/modules/hello.py 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,13 @@ +from __future__ import annotations + + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule(argument_spec=dict()) + module.exit_json(source='meta') + + +if __name__ == '__main__': + main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-meta/tasks/main.yml new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-meta/tasks/main.yml --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-meta/tasks/main.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-meta/tasks/main.yml 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,3 @@ +- name: Set a fact indicating this collection was loaded as a dependency + set_fact: + by_meta_loaded: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-needs-target/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-needs-target/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-needs-target/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-needs-target/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1 @@ +hidden diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-needs-target/ansible_collections/ansible_test/ansible_test_collection_indirect_by_needs_target/plugins/modules/hello.py new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-needs-target/ansible_collections/ansible_test/ansible_test_collection_indirect_by_needs_target/plugins/modules/hello.py --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-indirect-by-needs-target/ansible_collections/ansible_test/ansible_test_collection_indirect_by_needs_target/plugins/modules/hello.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-indirect-by-needs-target/ansible_collections/ansible_test/ansible_test_collection_indirect_by_needs_target/plugins/modules/hello.py 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,13 @@ +from __future__ import annotations + + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule(argument_spec=dict()) + module.exit_json(source='needs/target') + + +if __name__ == '__main__': + main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-role-dependencies/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-role-dependencies/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-role-dependencies/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-role-dependencies/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,5 @@ +shippable/posix/group3 # runs in the distro test containers +shippable/generic/group1 # runs in the default test container +needs/target/ansible-test-collection-indirect-by-needs-target +context/controller +gather_facts/no diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-role-dependencies/meta/main.yml new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-role-dependencies/meta/main.yml --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-role-dependencies/meta/main.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-role-dependencies/meta/main.yml 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,2 @@ +dependencies: + - ansible-test-collection-indirect-by-meta diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-role-dependencies/tasks/main.yml new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-role-dependencies/tasks/main.yml --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-role-dependencies/tasks/main.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-role-dependencies/tasks/main.yml 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,18 @@ +- name: Verify collection dependencies executed as appropriate + assert: + that: + - by_meta_loaded + +- name: Use dependency defined by alias + ansible_test.ansible_test_collection_indirect_by_needs_target.hello: + register: by_needs_target + +- name: Use dependency defined by meta + ansible_test.ansible_test_collection_indirect_by_meta.hello: + register: by_meta + +- name: Verify the results + assert: + that: + - by_needs_target.source == 'needs/target' + - by_meta.source == 'meta' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-script-dependencies/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-script-dependencies/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-script-dependencies/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-script-dependencies/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,4 @@ +shippable/posix/group3 # runs in the distro test containers +shippable/generic/group1 # runs in the default test container +needs/target/ansible-test-collection-indirect-by-needs-target +context/controller diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-script-dependencies/runme.sh new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-script-dependencies/runme.sh --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-script-dependencies/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-script-dependencies/runme.sh 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook runme.yml "${@}" -i ../../inventory diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-script-dependencies/runme.yml new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-script-dependencies/runme.yml --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-collection-script-dependencies/runme.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-collection-script-dependencies/runme.yml 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,11 @@ +- hosts: testhost + gather_facts: no + tasks: + - name: Use dependency defined by alias + ansible_test.ansible_test_collection_indirect_by_needs_target.hello: + register: by_needs_target + + - name: Verify the results + assert: + that: + - by_needs_target.source == 'needs/target' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,4 @@ +shippable/posix/group3 # runs in the distro test containers +shippable/generic/group1 # runs in the default test container +context/controller +needs/target/collection diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1 @@ +context/controller diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/runme.sh new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/runme.sh --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/noop/runme.sh 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +set -eux diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,2 @@ +context/controller +gather_facts/no diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/tasks/main.yml new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/tasks/main.yml --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/tasks/main.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/pause/tasks/main.yml 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,3 @@ +- name: Wait long enough for the timeout callback to fire + wait_for: + timeout: 30 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/aliases new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/aliases --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1 @@ +context/controller diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/runme.sh new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/runme.sh --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/ansible_collections/ns/col/tests/integration/targets/sleep/runme.sh 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +sleep 30 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/runme.sh new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/runme.sh --- old/ansible_core-2.20.7/test/integration/targets/ansible-test-timeout/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/ansible-test-timeout/runme.sh 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Test ansible-test timeout functionality and the timeout callback. + +source ../collection/setup.sh + +set -eux -o pipefail + +# Prime the venv so timeouts don't risk leaving a broken venv. +ansible-test integration noop --venv --color --truncate 0 + +echo "=== Test 1: ansible-test timeout already expired ===" +ansible-test env --timeout 0.0001 2>&1 | tee test1-env.txt + +if ansible-test integration sleep --venv --color --truncate 0 2>&1 | tee test1.txt; then + echo "FAIL: expected ansible-test to fail due to expired timeout, but it passed." + exit 1 +fi + +grep -q "test timeout expired" test1.txt +grep -q "timeout may be too short" test1-env.txt + +echo "=== Test 2: ansible-test timeout fires during a test ===" +# Use the sleep target (a shell script) so ansible-playbook doesn't run +# and the timeout callback doesn't intercept. +ansible-test env --timeout 0.1 + +if ansible-test integration sleep --venv --color --truncate 0 2>&1 | tee test2.txt; then + echo "FAIL: expected ansible-test to fail due to timeout, but it passed." + exit 1 +fi + +grep -q "time limit" test2.txt + +echo "=== Test 3: timeout callback with deadline already passed ===" +# The timeout margin equals the timeout duration, so the callback +# deadline is guaranteed to be in the past by the time it loads. +# ansible-test's own timeout check passes since the deadline itself hasn't +# expired yet at startup. +ansible-test env --timeout 0.167 2>&1 | tee test3-env.txt + +if ansible-test integration pause --venv --color --truncate 0 2>&1 | tee test3.txt; then + echo "FAIL: expected ansible-test to fail due to timeout traceback, but it passed." + exit 1 +fi + +grep -q "Tests aborted by the timeout callback" test3.txt +grep -q "deadline exceeded by" test3.txt +grep -q "timeout may be too short" test3-env.txt && exit 1 + +echo "=== Test 4: timeout callback fires while process is hung ===" +# Use a longer timeout so the callback deadline is in the future when it +# loads. The pause target keeps ansible-playbook alive long enough for the +# timeout callback to fire. +ansible-test env --timeout 0.25 + +if ansible-test integration pause --venv --color --truncate 0 2>&1 | tee test4.txt; then + echo "FAIL: expected ansible-test to fail due to timeout traceback, but it passed." + exit 1 +fi + +grep -q "Tests aborted by the timeout callback" test4.txt +grep -q "remaining" test4.txt + +echo PASS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/async/tasks/main.yml new/ansible_core-2.20.8/test/integration/targets/async/tasks/main.yml --- old/ansible_core-2.20.7/test/integration/targets/async/tasks/main.yml 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/integration/targets/async/tasks/main.yml 2026-08-10 17:50:25.000000000 +0200 @@ -284,7 +284,7 @@ - name: Test that async has stdin command: > {{ ansible_python_interpreter|default('/usr/bin/python') }} -c 'import os; os.fdopen(os.dup(0), "r")' - async: 1 + async: 5 poll: 1 - name: run async poll callback test playbook @@ -311,6 +311,7 @@ - name: test 'hidden' async, gather_facts does internal on parallel true. Ensure it won't freeze in check_mode due to async_status not supporting it. gather_facts: parallel: true + gather_timeout: 10 timeout: 60 check_mode: true when: "ansible_facts['os_family'] != 'Darwin'" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/collection/setup.sh new/ansible_core-2.20.8/test/integration/targets/collection/setup.sh --- old/ansible_core-2.20.7/test/integration/targets/collection/setup.sh 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/integration/targets/collection/setup.sh 2026-08-10 17:50:25.000000000 +0200 @@ -15,7 +15,7 @@ # # 4) Windows tests need access to the ansible.windows vendored collection. # This script copies any of the existing collections in ANSIBLE_COLLECTIONS_PATH to the temporary directory. - +# set -eu -o pipefail export TEST_DIR @@ -30,7 +30,13 @@ cd "${WORK_DIR}/ansible_collections/ns/${COLLECTION_NAME:-col}" if [ "${ANSIBLE_COLLECTIONS_PATH:+set}" = "set" ]; then - cp -aL "${ANSIBLE_COLLECTIONS_PATH}"/ansible_collections/* "${WORK_DIR}/ansible_collections" + IFS=':' read -ra collections_paths <<< "${ANSIBLE_COLLECTIONS_PATH}" + + for collections_path in "${collections_paths[@]}"; do + if [ -d "${collections_path}/ansible_collections" ]; then + cp -aL "${collections_path}"/ansible_collections/* "${WORK_DIR}/ansible_collections" + fi + done fi "${TEST_DIR}/../collection/update-ignore.py" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/collection_loader/aliases new/ansible_core-2.20.8/test/integration/targets/collection_loader/aliases --- old/ansible_core-2.20.7/test/integration/targets/collection_loader/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/collection_loader/aliases 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,3 @@ +shippable/posix/group3 +context/controller +needs/target/collection diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py new/ansible_core-2.20.8/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py --- old/ansible_core-2.20.7/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,13 @@ +from __future__ import annotations + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + def run(self, tmp=None, task_vars=None): + import my_module + + result = super(ActionModule, self).run(tmp, task_vars) + result['modules'] = my_module.run() + + return result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py new/ansible_core-2.20.8/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py --- old/ansible_core-2.20.7/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,9 @@ +from __future__ import annotations + +import pkgutil + +from my_module import sub + + +def run() -> list[str]: + return [m[1] for m in pkgutil.iter_modules(sub.__path__)] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/collection_loader/main.yml new/ansible_core-2.20.8/test/integration/targets/collection_loader/main.yml --- old/ansible_core-2.20.7/test/integration/targets/collection_loader/main.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/collection_loader/main.yml 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,11 @@ +- name: run collection loader tests + hosts: localhost + tasks: + - name: run module which imports Python library inside collection dir + ns.col.test_collection_loader: + register: result + + - name: assert module loader results + assert: + that: + - result.modules == ['foo'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/collection_loader/runme.sh new/ansible_core-2.20.8/test/integration/targets/collection_loader/runme.sh --- old/ansible_core-2.20.7/test/integration/targets/collection_loader/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/integration/targets/collection_loader/runme.sh 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eux + +PLAYBOOK_DIR="${PWD}" + +source ../collection/setup.sh + +EXT=$( python -c 'import importlib.machinery as i; print(next(iter([s for s in i.all_suffixes() if s != ".py"])))' ) +touch "temp_pythonpath/my_module/sub/foo${EXT}" + +ANSIBLE_COLLECTIONS_PATH="${PWD}../../../" \ + ANSIBLE_LOCALHOST_WARNING=false \ + PYTHONPATH="./temp_pythonpath:${PYTHONPATH}" \ + ansible-playbook "${PLAYBOOK_DIR}/main.yml" "$@" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/gathering_facts/library/slow new/ansible_core-2.20.8/test/integration/targets/gathering_facts/library/slow --- old/ansible_core-2.20.7/test/integration/targets/gathering_facts/library/slow 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/integration/targets/gathering_facts/library/slow 2026-08-10 17:50:25.000000000 +0200 @@ -1,6 +1,6 @@ #!/bin/sh -sleep 10 +sleep 3 echo '{ "changed": false, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/gathering_facts/runme.sh new/ansible_core-2.20.8/test/integration/targets/gathering_facts/runme.sh --- old/ansible_core-2.20.7/test/integration/targets/gathering_facts/runme.sh 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/integration/targets/gathering_facts/runme.sh 2026-08-10 17:50:25.000000000 +0200 @@ -12,7 +12,7 @@ ansible-playbook test_prevent_injection.yml -i inventory -v "$@" # ensure fact merging is working properly -ansible-playbook verify_merge_facts.yml -v "$@" -e 'ansible_facts_parallel: False' +ansible-playbook verify_merge_facts.yml -v "$@" -e 'ansible_facts_parallel=False' # ensure we dont clobber facts in loop ansible-playbook prevent_clobbering.yml -v "$@" @@ -33,10 +33,10 @@ ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ "$@" # test that gather_facts will timeout parallel modules that dont support gather_timeout when using gather_Timeout -ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ -a 'gather_timeout=1 parallel=true' "$@" 2>&1 |grep 'Timeout exceeded' +ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ANSIBLE_TASK_TIMEOUT=5 ansible -m gather_facts localhost --playbook-dir ./ -a 'gather_timeout=1 parallel=true' "$@" 2>&1 |grep 'Timeout exceeded' # test that gather_facts parallel w/o timing out -ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ -a 'gather_timeout=30 parallel=true' "$@" 2>&1 |grep -v 'Timeout exceeded' +ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ -a 'gather_timeout=10 parallel=true' "$@" 2>&1 |grep 'Timeout exceeded' && exit 1 # test parallelism diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/integration/targets/rpm_key/tasks/rpm_key.yaml new/ansible_core-2.20.8/test/integration/targets/rpm_key/tasks/rpm_key.yaml --- old/ansible_core-2.20.7/test/integration/targets/rpm_key/tasks/rpm_key.yaml 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/integration/targets/rpm_key/tasks/rpm_key.yaml 2026-08-10 17:50:25.000000000 +0200 @@ -238,6 +238,42 @@ - result is success - result is not changed +# Test armor without trailing newline (https://github.com/ansible/ansible/issues/87303). +# Note that this particular issue is very dependent on the version of librpm installed. +# Tolerancy of a missing newline varies across older versions, but we add this specific +# test to validate our handling of the missing trailing newline. +- name: Remove all keys from key ring + shell: rpm -q gpg-pubkey | xargs rpm -e + +- name: Read test key content + slurp: + src: "{{ test_key_path }}" + register: test_key_content + +- name: Write key file without trailing newline + copy: + content: "{{ (test_key_content['content'] | b64decode).rstrip('\n') }}" + dest: "{{ remote_tmp_dir }}/key_no_newline.asc" + mode: "0644" + +- name: Verify key file has no trailing newline + shell: test "$(tail -c 1 {{ remote_tmp_dir }}/key_no_newline.asc)" != "" + +- name: Import key without trailing newline + rpm_key: + state: present + key: "{{ remote_tmp_dir }}/key_no_newline.asc" + +- name: Import key without trailing newline (idempotent) + rpm_key: + state: present + key: "{{ remote_tmp_dir }}/key_no_newline.asc" + register: no_newline_idempotence + +- name: Verify idempotence + assert: + that: no_newline_idempotence is not changed + # Test PGPv6 keys - name: Test PGPv6 Keys block: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/lib/ansible_test/_internal/commands/env/__init__.py new/ansible_core-2.20.8/test/lib/ansible_test/_internal/commands/env/__init__.py --- old/ansible_core-2.20.7/test/lib/ansible_test/_internal/commands/env/__init__.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/lib/ansible_test/_internal/commands/env/__init__.py 2026-08-10 17:50:25.000000000 +0200 @@ -36,6 +36,7 @@ ) from ...constants import ( + TIMEOUT_MARGIN_SECONDS, TIMEOUT_PATH, ) @@ -134,6 +135,9 @@ if timeout: display.info(f'Setting a {timeout.duration} minute test timeout which will end at: {timeout.deadline}', verbosity=1) + + if timeout.duration * 60 <= TIMEOUT_MARGIN_SECONDS: + display.warning(f'The {timeout.duration} minute timeout may be too short for the faulthandler callback to provide useful diagnostics.') else: display.info('Clearing existing test timeout.', verbosity=1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/lib/ansible_test/_internal/commands/integration/__init__.py new/ansible_core-2.20.8/test/lib/ansible_test/_internal/commands/integration/__init__.py --- old/ansible_core-2.20.7/test/lib/ansible_test/_internal/commands/integration/__init__.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/lib/ansible_test/_internal/commands/integration/__init__.py 2026-08-10 17:50:25.000000000 +0200 @@ -4,9 +4,11 @@ import collections.abc as c import contextlib +import dataclasses import datetime import json import os +import pathlib import re import shutil import tempfile @@ -56,7 +58,12 @@ read_text_file, ) +from ...constants import ( + TIMEOUT_MARGIN_SECONDS, +) + from ...util import ( + ANSIBLE_TEST_CONTROLLER_ROOT, ApplicationError, display, get_ansible_version, @@ -131,6 +138,10 @@ CoverageManager, ) +from ...timeout import ( + get_timeout, +) + def generate_dependency_map(integration_targets: list[IntegrationTarget]) -> dict[str, set[IntegrationTarget]]: """Analyze the given list of integration test targets and return a dictionary expressing target names and the targets on which they depend.""" @@ -176,6 +187,51 @@ return files_needed +def get_collection_roots_needed(target_dependencies: list[IntegrationTarget]) -> list[str]: + """ + Return a list of collection roots needed by the given list of target dependencies. + This feature is only supported for ansible-core, not collections. + To be recognized, a collection must reside in the following directory: + test/integration/targets/{target_name}/ansible_collections/ansible_test/{target_name} + If the target name has dashes, they will be replaced with underscores for the collection name. + It is an error if the collection root contains additional namespaces or collections. + This is enforced to ensure there are no naming conflicts between collection roots. + """ + if not data_context().content.is_ansible: + return [] + + collection_roots: list[str] = [] + namespace_name = 'ansible_test' + + for target_dependency in target_dependencies: + collection_root = os.path.join(data_context().content.integration_targets_path, target_dependency.name) + collection_name = target_dependency.name.replace('-', '_') + namespaces_path = os.path.join(collection_root, 'ansible_collections') + namespace_path = os.path.join(namespaces_path, namespace_name) + collection_path = os.path.join(namespace_path, collection_name) + + if not os.path.isdir(collection_path): + continue + + namespaces = set(os.listdir(namespaces_path)) + namespaces.remove(namespace_name) + + if namespaces: + raise ApplicationError(f"Additional test collection namespaces not supported: {', '.join(sorted(namespaces))}") + + collections = set(os.listdir(namespace_path)) + collections.remove(collection_name) + + if collections: + raise ApplicationError(f"Additional test collections not supported: {', '.join(sorted(collections))}") + + collection_roots.append(collection_root) + + collection_roots = sorted(set(collection_roots)) + + return collection_roots + + def check_inventory(args: IntegrationConfig, inventory_path: str) -> None: """Check the given inventory for issues.""" if not isinstance(args.controller, OriginConfig): @@ -259,7 +315,11 @@ ansible_config = ansible_config_src vars_file = os.path.join(data_context().content.root, data_context().content.integration_vars_path) - yield IntegrationEnvironment(data_context().content.root, integration_dir, targets_dir, inventory_path, ansible_config, vars_file) + test_env = IntegrationEnvironment(data_context().content.root, integration_dir, targets_dir, inventory_path, ansible_config, vars_file) + + with test_env.run(): + yield test_env + return # When testing a collection, the temporary directory must reside within the collection. @@ -290,6 +350,7 @@ target_dependencies = sorted([target] + list(cache.dependency_map.get(target.name, set()))) files_needed = get_files_needed(target_dependencies) + collection_roots = get_collection_roots_needed(target_dependencies) integration_dir = os.path.join(temp_dir, data_context().content.integration_path) targets_dir = os.path.join(temp_dir, data_context().content.integration_targets_path) @@ -337,7 +398,10 @@ make_dirs(os.path.dirname(file_dst)) shutil.copy2(file_src, file_dst) - yield IntegrationEnvironment(temp_dir, integration_dir, targets_dir, inventory_path, ansible_config, vars_file) + test_env = IntegrationEnvironment(temp_dir, integration_dir, targets_dir, inventory_path, ansible_config, vars_file, collection_roots) + + with test_env.run(): + yield test_env finally: if not args.explain: remove_tree(temp_dir) @@ -629,6 +693,7 @@ if config_path: cmd += ['-e', '@%s' % config_path] + test_env.update_environment(env) env.update(coverage_manager.get_environment(target.name, target.aliases)) cover_python(args, host_state.controller_profile.python, cmd, target.name, env, cwd=cwd, capture=False) @@ -748,6 +813,7 @@ env['ANSIBLE_ROLES_PATH'] = test_env.targets_dir + test_env.update_environment(env) env.update(coverage_manager.get_environment(target.name, target.aliases)) cover_python(args, host_state.controller_profile.python, cmd, target.name, env, cwd=cwd, capture=False) @@ -798,6 +864,11 @@ callback_plugins = ['junit'] + (env_config.callback_plugins or [] if env_config else []) + timeout = get_timeout() + + if timeout: + callback_plugins.append('ansible_test._internal.timeout') + integration = dict( JUNIT_OUTPUT_DIR=ResultType.JUNIT.path, JUNIT_TASK_RELATIVE_PATH=test_env.test_dir, @@ -810,6 +881,16 @@ INVENTORY_PATH=os.path.abspath(inventory_path), ) + if timeout: + collections_path = env.get('ANSIBLE_COLLECTIONS_PATH', '') + collections_path = f'{ANSIBLE_TEST_CONTROLLER_ROOT}:{collections_path}' if collections_path else ANSIBLE_TEST_CONTROLLER_ROOT + + integration.update( + ANSIBLE_COLLECTIONS_PATH=collections_path, + ANSIBLE_TEST_TIMEOUT_DEADLINE=str(timeout.deadline.timestamp() - TIMEOUT_MARGIN_SECONDS), + ANSIBLE_TEST_TIMEOUT_DIR=str(test_env.timeout_dir), + ) + if args.debug_strategy: env.update(ANSIBLE_STRATEGY='debug') @@ -828,16 +909,63 @@ return env [email protected](frozen=True) class IntegrationEnvironment: """Details about the integration environment.""" - def __init__(self, test_dir: str, integration_dir: str, targets_dir: str, inventory_path: str, ansible_config: str, vars_file: str) -> None: - self.test_dir = test_dir - self.integration_dir = integration_dir - self.targets_dir = targets_dir - self.inventory_path = inventory_path - self.ansible_config = ansible_config - self.vars_file = vars_file + test_dir: str + integration_dir: str + targets_dir: str + inventory_path: str + ansible_config: str + vars_file: str + collection_roots: list[str] = dataclasses.field(default_factory=list) + + @property + def timeout_dir(self) -> pathlib.Path: + """Return the path for timeout dump files.""" + return pathlib.Path(self.test_dir) / 'timeout' + + @contextlib.contextmanager + def run(self) -> c.Iterator[None]: + """Context manager that checks for timeout dumps after the test runs.""" + try: + yield + finally: + self.check_timeout_dumps() + + def check_timeout_dumps(self) -> None: + """Check for timeout dump files and raise an error if any contain tracebacks.""" + if not (timeout := get_timeout()): + return + + if not self.timeout_dir.is_dir(): + return + + found = False + + for dump_file in sorted(self.timeout_dir.iterdir()): + lines = dump_file.read_text().splitlines() + + if len(lines) <= 2: # first line is the command, second is the timeout status, traceback, if any, follows + continue + + found = True + display.error(f'Timeout traceback detected ({dump_file.name}):\n' + '\n'.join(lines)) + + if found: + raise ApplicationError( + f'Tests aborted by the timeout callback after approaching the {timeout.duration} minute time limit.' + ' See traceback(s) above for process state.' + ) + + def update_environment(self, env: dict[str, str]) -> None: + """Update the given environment dictionary with the variables necessary for this integration environment.""" + collections_path = value.split(':') if (value := env.get('ANSIBLE_COLLECTIONS_PATH')) else [] + collections_path.extend([os.path.join(self.test_dir, root) for root in self.collection_roots]) + + if collections_path: + env['ANSIBLE_COLLECTIONS_PATH'] = ':'.join(collections_path) class IntegrationCache(CommonCache): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/lib/ansible_test/_internal/constants.py new/ansible_core-2.20.8/test/lib/ansible_test/_internal/constants.py --- old/ansible_core-2.20.7/test/lib/ansible_test/_internal/constants.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/lib/ansible_test/_internal/constants.py 2026-08-10 17:50:25.000000000 +0200 @@ -15,6 +15,7 @@ # File used to track the ansible-test test execution timeout. TIMEOUT_PATH = '.ansible-test-timeout.json' +TIMEOUT_MARGIN_SECONDS = 10 CONTROLLER_MIN_PYTHON_VERSION = CONTROLLER_PYTHON_VERSIONS[0] SUPPORTED_PYTHON_VERSIONS = REMOTE_ONLY_PYTHON_VERSIONS + CONTROLLER_PYTHON_VERSIONS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/lib/ansible_test/_internal/host_configs.py new/ansible_core-2.20.8/test/lib/ansible_test/_internal/host_configs.py --- old/ansible_core-2.20.7/test/lib/ansible_test/_internal/host_configs.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/lib/ansible_test/_internal/host_configs.py 2026-08-10 17:50:25.000000000 +0200 @@ -384,7 +384,9 @@ super().apply_defaults(context, defaults) self.become = self.become or defaults.become - self.name = defaults.name + + if not defaults.is_default: + self.name = defaults.name @property def have_root(self) -> bool: @@ -414,7 +416,9 @@ super().apply_defaults(context, defaults) self.connection = self.connection or defaults.connection - self.name = defaults.name + + if not defaults.is_default: + self.name = defaults.name @dataclasses.dataclass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/lib/ansible_test/_internal/timeout.py new/ansible_core-2.20.8/test/lib/ansible_test/_internal/timeout.py --- old/ansible_core-2.20.7/test/lib/ansible_test/_internal/timeout.py 2026-06-18 21:33:04.000000000 +0200 +++ new/ansible_core-2.20.8/test/lib/ansible_test/_internal/timeout.py 2026-08-10 17:50:25.000000000 +0200 @@ -20,6 +20,7 @@ ) from .util import ( + cache, display, TimeoutExpiredError, ) @@ -81,6 +82,7 @@ ) +@cache def get_timeout() -> TimeoutDetail | None: """Return details about the currently set timeout, if any, otherwise return None.""" try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.7/test/lib/ansible_test/_util/controller/ansible_collections/ansible_test/_internal/plugins/callback/timeout.py new/ansible_core-2.20.8/test/lib/ansible_test/_util/controller/ansible_collections/ansible_test/_internal/plugins/callback/timeout.py --- old/ansible_core-2.20.7/test/lib/ansible_test/_util/controller/ansible_collections/ansible_test/_internal/plugins/callback/timeout.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.8/test/lib/ansible_test/_util/controller/ansible_collections/ansible_test/_internal/plugins/callback/timeout.py 2026-08-10 17:50:25.000000000 +0200 @@ -0,0 +1,73 @@ +"""Ansible callback plugin to enforce an execution deadline using faulthandler.""" +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import annotations + +import datetime +import faulthandler +import os +import pathlib +import shlex +import sys + +from ansible.plugins.callback import CallbackBase + +DOCUMENTATION = """ + name: timeout + type: aggregate + short_description: Dump thread stacks when an execution deadline is reached. + description: + - Enforces an execution deadline by dumping all thread stacks to a file + and terminating the process when the deadline expires. + - Intended for use by ansible-test only. Do not enable manually. + options: + deadline: + description: Execution deadline as a UTC epoch timestamp. + env: + - name: ANSIBLE_TEST_TIMEOUT_DEADLINE + dump_dir: + description: Directory for timeout dump files. + env: + - name: ANSIBLE_TEST_TIMEOUT_DIR +""" + + +class CallbackModule(CallbackBase): + """Dump thread stacks and exit when an execution deadline is reached.""" + + CALLBACK_VERSION = 2.0 + CALLBACK_TYPE = 'aggregate' + CALLBACK_NAME = 'timeout' + CALLBACK_NEEDS_ENABLED = True + + def __init__(self): + super().__init__() + + deadline_str = os.environ.get('ANSIBLE_TEST_TIMEOUT_DEADLINE') + dump_dir = os.environ.get('ANSIBLE_TEST_TIMEOUT_DIR') + + if not deadline_str or not dump_dir: + return + + deadline = datetime.datetime.fromtimestamp(float(deadline_str), tz=datetime.timezone.utc) + now = datetime.datetime.now(datetime.timezone.utc) + remaining = deadline - now + + dump_dir_path = pathlib.Path(dump_dir) + dump_dir_path.mkdir(parents=True, exist_ok=True) + + dump_path = dump_dir_path / f'timeout-{now.isoformat()}-{os.getpid()}.txt' + + self._dump_file = dump_path.open('w') + self._dump_file.write(f'Command: {shlex.join(sys.argv).replace("\n", " ")}\n') + self._dump_file.flush() + + if remaining > datetime.timedelta(): + self._dump_file.write(f'Timeout: {remaining} remaining\n') + self._dump_file.flush() + faulthandler.dump_traceback_later(remaining.total_seconds(), file=self._dump_file, exit=True) + else: + self._dump_file.write(f'Timeout: deadline exceeded by {-remaining}\n') + self._dump_file.flush() + faulthandler.dump_traceback(file=self._dump_file) + sys.exit(1) ++++++ ansible_core-2.20.7.tar.gz.sha256 -> ansible_core-2.20.8.tar.gz.sha256 ++++++ --- /work/SRC/openSUSE:Factory/ansible-core-2.20/ansible_core-2.20.7.tar.gz.sha256 2026-06-19 17:12:52.684512136 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core-2.20.new.17972/ansible_core-2.20.8.tar.gz.sha256 2026-08-11 17:18:41.349995724 +0200 @@ -1 +1 @@ -84d4097a28e816e8bf8066307088f833837db9efdc9dc6716b43253de3b14106 ansible_core-2.20.7.tar.gz +66e0e91ee43d98b3968a612c42c8e6e3d1ea43be501c4f8f6fc8c75b35892cdf ansible_core-2.20.8.tar.gz
