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

Reply via email to