Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ansible-core for openSUSE:Factory 
checked in at 2023-09-12 21:03:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ansible-core (Old)
 and      /work/SRC/openSUSE:Factory/.ansible-core.new.1766 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ansible-core"

Tue Sep 12 21:03:59 2023 rev:18 rq:1110578 version:2.15.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/ansible-core/ansible-core.changes        
2023-09-08 21:17:08.789913301 +0200
+++ /work/SRC/openSUSE:Factory/.ansible-core.new.1766/ansible-core.changes      
2023-09-12 21:05:18.761374708 +0200
@@ -1,0 +2,33 @@
+Tue Sep 12 08:13:53 UTC 2023 - Johannes Kastl <ka...@b1-systems.de>
+
+- update to 2.15.4:
+  * Deprecated Features
+    - vault and unfault filters - the undocumented vaultid
+      parameter is deprecated and will be removed in ansible-core
+      2.20. Use vault_id instead.
+  * Bugfixes
+    - PowerShell - Remove some code which is no longer valid for
+      dotnet 5+
+    - Prompting - add a short sleep between polling for user input
+      to reduce CPU consumption (#81516).
+    - ansible-galaxy - Enabled the data tarfile filter during role
+      installation for Python versions that support it. A probing
+      mechanism is used to avoid Python versions with a broken
+      implementation.
+    - ansible-test - Always use ansible-test managed entry points
+      for ansible-core CLI tools when not running from source. This
+      fixes issues where CLI entry points created during install
+      are not compatible with ansible-test.
+    - first found lookup has been updated to use the normalized
+      argument parsing (pythonic) matching the documented examples.
+    - handlers - the listen keyword can affect only one handler
+      with the same name, the last one defined as it is a case with
+      the notify keyword (#81013)
+    - include_role - expose variables from parent roles to role's
+      handlers (#80459)
+    - tarfile - handle data filter deprecation warning message for
+      extract and extractall (#80832).
+    - vault and unvault filters now properly take vault_id
+      parameter.
+
+-------------------------------------------------------------------

Old:
----
  ansible-core-2.15.3.tar.gz

New:
----
  ansible-core-2.15.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ansible-core.spec ++++++
--- /var/tmp/diff_new_pack.xOjPpl/_old  2023-09-12 21:05:19.905415515 +0200
+++ /var/tmp/diff_new_pack.xOjPpl/_new  2023-09-12 21:05:19.905415515 +0200
@@ -38,7 +38,7 @@
 %endif
 
 Name:           ansible-core
-Version:        2.15.3
+Version:        2.15.4
 Release:        0
 Summary:        Radically simple IT automation
 License:        GPL-3.0-or-later

++++++ ansible-core-2.15.3.tar.gz -> ansible-core-2.15.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible-core-2.15.3/PKG-INFO 
new/ansible-core-2.15.4/PKG-INFO
--- old/ansible-core-2.15.3/PKG-INFO    2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/PKG-INFO    2023-09-12 00:11:17.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: ansible-core
-Version: 2.15.3
+Version: 2.15.4
 Summary: Radically simple IT automation
 Home-page: https://ansible.com/
 Author: Ansible, Inc.
@@ -31,6 +31,12 @@
 Requires-Python: >=3.9
 Description-Content-Type: text/markdown
 License-File: COPYING
+Requires-Dist: jinja2>=3.0.0
+Requires-Dist: PyYAML>=5.1
+Requires-Dist: cryptography
+Requires-Dist: packaging
+Requires-Dist: importlib_resources<5.1,>=5.0; python_version < "3.10"
+Requires-Dist: resolvelib<1.1.0,>=0.5.3
 
 [![PyPI 
version](https://img.shields.io/pypi/v/ansible-core.svg)](https://pypi.org/project/ansible-core)
 [![Docs 
badge](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.ansible.com/ansible/latest/)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible-core-2.15.3/changelogs/CHANGELOG-v2.15.rst 
new/ansible-core-2.15.4/changelogs/CHANGELOG-v2.15.rst
--- old/ansible-core-2.15.3/changelogs/CHANGELOG-v2.15.rst      2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/changelogs/CHANGELOG-v2.15.rst      2023-09-12 
00:11:17.000000000 +0200
@@ -5,6 +5,34 @@
 .. contents:: Topics
 
 
+v2.15.4
+=======
+
+Release Summary
+---------------
+
+| Release Date: 2023-09-11
+| `Porting Guide 
<https://docs.ansible.com/ansible-core/2.15/porting_guides/porting_guide_core_2.15.html>`__
+
+
+Deprecated Features
+-------------------
+
+- vault and unfault filters - the undocumented ``vaultid`` parameter is 
deprecated and will be removed in ansible-core 2.20. Use ``vault_id`` instead.
+
+Bugfixes
+--------
+
+- PowerShell - Remove some code which is no longer valid for dotnet 5+
+- Prompting - add a short sleep between polling for user input to reduce CPU 
consumption (https://github.com/ansible/ansible/issues/81516).
+- ansible-galaxy - Enabled the ``data`` tarfile filter during role 
installation for Python versions that support it. A probing mechanism is used 
to avoid Python versions with a broken implementation.
+- ansible-test - Always use ansible-test managed entry points for ansible-core 
CLI tools when not running from source. This fixes issues where CLI entry 
points created during install are not compatible with ansible-test.
+- first found lookup has been updated to use the normalized argument parsing 
(pythonic) matching the documented examples.
+- handlers - the ``listen`` keyword can affect only one handler with the same 
name, the last one defined as it is a case with the ``notify`` keyword 
(https://github.com/ansible/ansible/issues/81013)
+- include_role - expose variables from parent roles to role's handlers 
(https://github.com/ansible/ansible/issues/80459)
+- tarfile - handle data filter deprecation warning message for extract and 
extractall (https://github.com/ansible/ansible/issues/80832).
+- vault and unvault filters now properly take ``vault_id`` parameter.
+
 v2.15.3
 =======
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible-core-2.15.3/changelogs/changelog.yaml 
new/ansible-core-2.15.4/changelogs/changelog.yaml
--- old/ansible-core-2.15.3/changelogs/changelog.yaml   2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/changelogs/changelog.yaml   2023-09-12 
00:11:17.000000000 +0200
@@ -1069,3 +1069,54 @@
     - urls-client-cert-py12.yml
     - urls-unit-test-latest-cryptography.yml
     release_date: '2023-08-07'
+  2.15.4:
+    changes:
+      release_summary: '| Release Date: 2023-09-11
+
+        | `Porting Guide 
<https://docs.ansible.com/ansible-core/2.15/porting_guides/porting_guide_core_2.15.html>`__
+
+        '
+    codename: Ten Years Gone
+    fragments:
+    - 2.15.4_summary.yaml
+    release_date: '2023-09-11'
+  2.15.4rc1:
+    changes:
+      bugfixes:
+      - PowerShell - Remove some code which is no longer valid for dotnet 5+
+      - Prompting - add a short sleep between polling for user input to reduce 
CPU
+        consumption (https://github.com/ansible/ansible/issues/81516).
+      - ansible-galaxy - Enabled the ``data`` tarfile filter during role 
installation
+        for Python versions that support it. A probing mechanism is used to 
avoid
+        Python versions with a broken implementation.
+      - ansible-test - Always use ansible-test managed entry points for 
ansible-core
+        CLI tools when not running from source. This fixes issues where CLI 
entry
+        points created during install are not compatible with ansible-test.
+      - first found lookup has been updated to use the normalized argument 
parsing
+        (pythonic) matching the documented examples.
+      - handlers - the ``listen`` keyword can affect only one handler with the 
same
+        name, the last one defined as it is a case with the ``notify`` keyword 
(https://github.com/ansible/ansible/issues/81013)
+      - include_role - expose variables from parent roles to role's handlers 
(https://github.com/ansible/ansible/issues/80459)
+      - tarfile - handle data filter deprecation warning message for extract 
and extractall
+        (https://github.com/ansible/ansible/issues/80832).
+      - vault and unvault filters now properly take ``vault_id`` parameter.
+      deprecated_features:
+      - vault and unfault filters - the undocumented ``vaultid`` parameter is 
deprecated
+        and will be removed in ansible-core 2.20. Use ``vault_id`` instead.
+      release_summary: '| Release Date: 2023-09-05
+
+        | `Porting Guide 
<https://docs.ansible.com/ansible-core/2.15/porting_guides/porting_guide_core_2.15.html>`__
+
+        '
+    codename: Ten Years Gone
+    fragments:
+    - 2.15.4rc1_summary.yaml
+    - 80459-handlers-nested-includes-vars.yml
+    - 81013-handlers-listen-last-defined-only.yml
+    - ansible-test-entry-points.yml
+    - dotnet-preparation.yml
+    - first_found_fixes.yml
+    - fix-display-prompt-cpu-consumption.yml
+    - tarfile_extract_warn.yml
+    - vault_unvault_id_fix.yml
+    release_date: '2023-09-05'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible-core-2.15.3/lib/ansible/galaxy/role.py 
new/ansible-core-2.15.4/lib/ansible/galaxy/role.py
--- old/ansible-core-2.15.3/lib/ansible/galaxy/role.py  2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/galaxy/role.py  2023-09-12 
00:11:17.000000000 +0200
@@ -24,6 +24,7 @@
 
 import errno
 import datetime
+import functools
 import os
 import tarfile
 import tempfile
@@ -45,6 +46,32 @@
 display = Display()
 
 
+@functools.cache
+def _check_working_data_filter() -> bool:
+    """
+    Check if tarfile.data_filter implementation is working
+    for the current Python version or not
+    """
+
+    # Implemented the following code to circumvent broken implementation of 
data_filter
+    # in tarfile. See for more information - 
https://github.com/python/cpython/issues/107845
+    # deprecated: description='probing broken data filter implementation' 
python_version='3.11'
+    ret = False
+    if hasattr(tarfile, 'data_filter'):
+        # We explicitly check if tarfile.data_filter is broken or not
+        ti = tarfile.TarInfo('docs/README.md')
+        ti.type = tarfile.SYMTYPE
+        ti.linkname = '../README.md'
+
+        try:
+            tarfile.data_filter(ti, '/foo')
+        except tarfile.LinkOutsideDestinationError:
+            pass
+        else:
+            ret = True
+    return ret
+
+
 class GalaxyRole(object):
 
     SUPPORTED_SCMS = set(['git', 'hg'])
@@ -379,7 +406,12 @@
                                     if n_part != '..' and not 
n_part.startswith('~') and '$' not in n_part:
                                         n_final_parts.append(n_part)
                                 member.name = os.path.join(*n_final_parts)
-                                role_tar_file.extract(member, 
to_native(self.path))
+
+                                if _check_working_data_filter():
+                                    # deprecated: description='extract 
fallback without filter' python_version='3.11'
+                                    role_tar_file.extract(member, 
to_native(self.path), filter='data')  # type: ignore[call-arg]
+                                else:
+                                    role_tar_file.extract(member, 
to_native(self.path))
 
                         # write out the install info file for later use
                         self._write_galaxy_install_info()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/module_utils/ansible_release.py 
new/ansible-core-2.15.4/lib/ansible/module_utils/ansible_release.py
--- old/ansible-core-2.15.3/lib/ansible/module_utils/ansible_release.py 
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/module_utils/ansible_release.py 
2023-09-12 00:11:17.000000000 +0200
@@ -19,6 +19,6 @@
 from __future__ import (absolute_import, division, print_function)
 __metaclass__ = type
 
-__version__ = '2.15.3'
+__version__ = '2.15.4'
 __author__ = 'Ansible, Inc.'
 __codename__ = "Ten Years Gone"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/module_utils/basic.py 
new/ansible-core-2.15.4/lib/ansible/module_utils/basic.py
--- old/ansible-core-2.15.3/lib/ansible/module_utils/basic.py   2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/module_utils/basic.py   2023-09-12 
00:11:17.000000000 +0200
@@ -1852,6 +1852,14 @@
         '''
         Execute a command, returns rc, stdout, and stderr.
 
+        The mechanism of this method for reading stdout and stderr differs from
+        that of CPython subprocess.Popen.communicate, in that this method will
+        stop reading once the spawned command has exited and stdout and stderr
+        have been consumed, as opposed to waiting until stdout/stderr are
+        closed. This can be an important distinction, when taken into account
+        that a forked or backgrounded process may hold stdout or stderr open
+        for longer than the spawned command.
+
         :arg args: is the command to run
             * If args is a list, the command will be run with shell=False.
             * If args is a string and use_unsafe_shell=False it will split 
args to a list and run with shell=False
@@ -2031,17 +2039,17 @@
             if before_communicate_callback:
                 before_communicate_callback(cmd)
 
-            # the communication logic here is essentially taken from that
-            # of the _communicate() function in ssh.py
-
             stdout = b''
             stderr = b''
-            try:
-                selector = selectors.DefaultSelector()
-            except (IOError, OSError):
-                # Failed to detect default selector for the given platform
-                # Select PollSelector which is supported by major platforms
+
+            # Mirror the CPython subprocess logic and preference for the 
selector to use.
+            # poll/select have the advantage of not requiring any extra file
+            # descriptor, contrarily to epoll/kqueue (also, they require a 
single
+            # syscall).
+            if hasattr(selectors, 'PollSelector'):
                 selector = selectors.PollSelector()
+            else:
+                selector = selectors.SelectSelector()
 
             if data:
                 if not binary_data:
@@ -2049,53 +2057,58 @@
                 if isinstance(data, text_type):
                     data = to_bytes(data)
 
-            if not prompt_re:
-                stdout, stderr = cmd.communicate(input=data)
-            else:
-                # We only need this to look for a prompt, to abort instead of 
hanging
-                selector.register(cmd.stdout, selectors.EVENT_READ)
-                selector.register(cmd.stderr, selectors.EVENT_READ)
-                if os.name == 'posix':
-                    fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_SETFL, 
fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
-                    fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_SETFL, 
fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
-
-                if data:
-                    cmd.stdin.write(data)
-                    cmd.stdin.close()
-
-                while True:
-                    events = selector.select(1)
-                    for key, event in events:
-                        b_chunk = key.fileobj.read()
-                        if b_chunk == b(''):
-                            selector.unregister(key.fileobj)
-                        if key.fileobj == cmd.stdout:
-                            stdout += b_chunk
-                        elif key.fileobj == cmd.stderr:
-                            stderr += b_chunk
-                    # if we're checking for prompts, do it now
-                    if prompt_re:
-                        if prompt_re.search(stdout) and not data:
-                            if encoding:
-                                stdout = to_native(stdout, encoding=encoding, 
errors=errors)
-                            return (257, stdout, "A prompt was encountered 
while running a command, but no input data was specified")
-                    # only break out if no pipes are left to read or
-                    # the pipes are completely read and
-                    # the process is terminated
-                    if (not events or not selector.get_map()) and cmd.poll() 
is not None:
-                        break
-                    # No pipes are left to read but process is not yet 
terminated
-                    # Only then it is safe to wait for the process to be 
finished
-                    # NOTE: Actually cmd.poll() is always None here if no 
selectors are left
-                    elif not selector.get_map() and cmd.poll() is None:
-                        cmd.wait()
-                        # The process is terminated. Since no pipes to read 
from are
-                        # left, there is no need to call select() again.
-                        break
-
-                cmd.stdout.close()
-                cmd.stderr.close()
-                selector.close()
+            selector.register(cmd.stdout, selectors.EVENT_READ)
+            selector.register(cmd.stderr, selectors.EVENT_READ)
+
+            if os.name == 'posix':
+                fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_SETFL, 
fcntl.fcntl(cmd.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
+                fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_SETFL, 
fcntl.fcntl(cmd.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
+
+            if data:
+                cmd.stdin.write(data)
+                cmd.stdin.close()
+
+            while True:
+                # A timeout of 1 is both a little short and a little long.
+                # With None we could deadlock, with a lower value we would
+                # waste cycles. As it is, this is a mild inconvenience if
+                # we need to exit, and likely doesn't waste too many cycles
+                events = selector.select(1)
+                stdout_changed = False
+                for key, event in events:
+                    b_chunk = key.fileobj.read(32768)
+                    if not b_chunk:
+                        selector.unregister(key.fileobj)
+                    elif key.fileobj == cmd.stdout:
+                        stdout += b_chunk
+                        stdout_changed = True
+                    elif key.fileobj == cmd.stderr:
+                        stderr += b_chunk
+
+                # if we're checking for prompts, do it now, but only if stdout
+                # actually changed since the last loop
+                if prompt_re and stdout_changed and prompt_re.search(stdout) 
and not data:
+                    if encoding:
+                        stdout = to_native(stdout, encoding=encoding, 
errors=errors)
+                    return (257, stdout, "A prompt was encountered while 
running a command, but no input data was specified")
+
+                # break out if no pipes are left to read or the pipes are 
completely read
+                # and the process is terminated
+                if (not events or not selector.get_map()) and cmd.poll() is 
not None:
+                    break
+
+                # No pipes are left to read but process is not yet terminated
+                # Only then it is safe to wait for the process to be finished
+                # NOTE: Actually cmd.poll() is always None here if no 
selectors are left
+                elif not selector.get_map() and cmd.poll() is None:
+                    cmd.wait()
+                    # The process is terminated. Since no pipes to read from 
are
+                    # left, there is no need to call select() again.
+                    break
+
+            cmd.stdout.close()
+            cmd.stderr.close()
+            selector.close()
 
             rc = cmd.returncode
         except (OSError, IOError) as e:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.AccessToken.cs 
new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.AccessToken.cs
--- 
old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.AccessToken.cs  
    2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.AccessToken.cs  
    2023-09-12 00:11:17.000000000 +0200
@@ -2,7 +2,6 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Runtime.ConstrainedExecution;
 using System.Runtime.InteropServices;
 using System.Security.Principal;
 using System.Text;
@@ -123,7 +122,6 @@
             base.SetHandle(handle);
         }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             Marshal.FreeHGlobal(handle);
@@ -247,7 +245,6 @@
         public SafeNativeHandle() : base(true) { }
         public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = 
handle; }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             return NativeMethods.CloseHandle(handle);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.Become.cs 
new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.Become.cs
--- old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.Become.cs   
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.Become.cs   
2023-09-12 00:11:17.000000000 +0200
@@ -4,7 +4,6 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-using System.Runtime.ConstrainedExecution;
 using System.Runtime.InteropServices;
 using System.Security.AccessControl;
 using System.Security.Principal;
@@ -175,7 +174,6 @@
     {
         public SafeLsaHandle() : base(true) { }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             UInt32 res = NativeMethods.LsaDeregisterLogonProcess(handle);
@@ -187,7 +185,6 @@
     {
         public SafeLsaMemoryBuffer() : base(true) { }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             UInt32 res = NativeMethods.LsaFreeReturnBuffer(handle);
@@ -200,7 +197,6 @@
         public NoopSafeHandle() : base(IntPtr.Zero, false) { }
         public override bool IsInvalid { get { return false; } }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle() { return true; }
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.Privilege.cs 
new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.Privilege.cs
--- 
old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.Privilege.cs    
    2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.Privilege.cs    
    2023-09-12 00:11:17.000000000 +0200
@@ -3,7 +3,6 @@
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
-using System.Runtime.ConstrainedExecution;
 using System.Runtime.InteropServices;
 using System.Security.Principal;
 using System.Text;
@@ -92,7 +91,6 @@
         {
             base.SetHandle(handle);
         }
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             Marshal.FreeHGlobal(handle);
@@ -104,7 +102,7 @@
     {
         public SafeNativeHandle() : base(true) { }
         public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = 
handle; }
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+
         protected override bool ReleaseHandle()
         {
             return NativeMethods.CloseHandle(handle);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.Process.cs 
new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.Process.cs
--- old/ansible-core-2.15.3/lib/ansible/module_utils/csharp/Ansible.Process.cs  
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/module_utils/csharp/Ansible.Process.cs  
2023-09-12 00:11:17.000000000 +0200
@@ -3,7 +3,6 @@
 using System.Collections;
 using System.IO;
 using System.Linq;
-using System.Runtime.ConstrainedExecution;
 using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading;
@@ -176,7 +175,6 @@
             base.SetHandle(handle);
         }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             Marshal.FreeHGlobal(handle);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/playbook/role_include.py 
new/ansible-core-2.15.4/lib/ansible/playbook/role_include.py
--- old/ansible-core-2.15.3/lib/ansible/playbook/role_include.py        
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/playbook/role_include.py        
2023-09-12 00:11:17.000000000 +0200
@@ -113,7 +113,7 @@
             b.collections = actual_role.collections
 
         # updated available handlers in play
-        handlers = actual_role.get_handler_blocks(play=myplay)
+        handlers = actual_role.get_handler_blocks(play=myplay, 
dep_chain=dep_chain)
         for h in handlers:
             h._parent = p_block
         myplay.handlers = myplay.handlers + handlers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/plugins/filter/encryption.py 
new/ansible-core-2.15.4/lib/ansible/plugins/filter/encryption.py
--- old/ansible-core-2.15.3/lib/ansible/plugins/filter/encryption.py    
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/plugins/filter/encryption.py    
2023-09-12 00:11:17.000000000 +0200
@@ -17,7 +17,7 @@
 display = Display()
 
 
-def do_vault(data, secret, salt=None, vaultid='filter_default', 
wrap_object=False):
+def do_vault(data, secret, salt=None, vault_id='filter_default', 
wrap_object=False, vaultid=None):
 
     if not isinstance(secret, (string_types, binary_type, Undefined)):
         raise AnsibleFilterTypeError("Secret passed is required to be a 
string, instead we got: %s" % type(secret))
@@ -25,11 +25,18 @@
     if not isinstance(data, (string_types, binary_type, Undefined)):
         raise AnsibleFilterTypeError("Can only vault strings, instead we got: 
%s" % type(data))
 
+    if vaultid is not None:
+        display.deprecated("Use of undocumented 'vaultid', use 'vault_id' 
instead", version='2.20')
+        if vault_id == 'filter_default':
+            vault_id = vaultid
+        else:
+            display.warning("Ignoring vaultid as vault_id is already set.")
+
     vault = ''
     vs = VaultSecret(to_bytes(secret))
     vl = VaultLib()
     try:
-        vault = vl.encrypt(to_bytes(data), vs, vaultid, salt)
+        vault = vl.encrypt(to_bytes(data), vs, vault_id, salt)
     except UndefinedError:
         raise
     except Exception as e:
@@ -43,7 +50,7 @@
     return vault
 
 
-def do_unvault(vault, secret, vaultid='filter_default'):
+def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
 
     if not isinstance(secret, (string_types, binary_type, Undefined)):
         raise AnsibleFilterTypeError("Secret passed is required to be as 
string, instead we got: %s" % type(secret))
@@ -51,9 +58,16 @@
     if not isinstance(vault, (string_types, binary_type, 
AnsibleVaultEncryptedUnicode, Undefined)):
         raise AnsibleFilterTypeError("Vault should be in the form of a string, 
instead we got: %s" % type(vault))
 
+    if vaultid is not None:
+        display.deprecated("Use of undocumented 'vaultid', use 'vault_id' 
instead", version='2.20')
+        if vault_id == 'filter_default':
+            vault_id = vaultid
+        else:
+            display.warning("Ignoring vaultid as vault_id is already set.")
+
     data = ''
     vs = VaultSecret(to_bytes(secret))
-    vl = VaultLib([(vaultid, vs)])
+    vl = VaultLib([(vault_id, vs)])
     if isinstance(vault, AnsibleVaultEncryptedUnicode):
         vault.vault = vl
         data = vault.data
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/plugins/lookup/first_found.py 
new/ansible-core-2.15.4/lib/ansible/plugins/lookup/first_found.py
--- old/ansible-core-2.15.3/lib/ansible/plugins/lookup/first_found.py   
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/plugins/lookup/first_found.py   
2023-09-12 00:11:17.000000000 +0200
@@ -165,6 +165,9 @@
         total_search = []
         skip = False
 
+        if not terms and kwargs:
+            terms = ['']
+
         # can use a dict instead of list item to pass inline config
         for term in terms:
             if isinstance(term, Mapping):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible/plugins/strategy/__init__.py 
new/ansible-core-2.15.4/lib/ansible/plugins/strategy/__init__.py
--- old/ansible-core-2.15.3/lib/ansible/plugins/strategy/__init__.py    
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/plugins/strategy/__init__.py    
2023-09-12 00:11:17.000000000 +0200
@@ -509,8 +509,11 @@
 
     def search_handlers_by_notification(self, notification: str, iterator: 
PlayIterator) -> t.Generator[Handler, None, None]:
         templar = Templar(None)
+        handlers = [h for b in reversed(iterator._play.handlers) for h in 
b.block]
         # iterate in reversed order since last handler loaded with the same 
name wins
-        for handler in (h for b in reversed(iterator._play.handlers) for h in 
b.block if h.name):
+        for handler in handlers:
+            if not handler.name:
+                continue
             if not handler.cached_name:
                 if templar.is_template(handler.name):
                     templar.available_variables = 
self._variable_manager.get_vars(
@@ -548,7 +551,8 @@
                 break
 
         templar.available_variables = {}
-        for handler in (h for b in iterator._play.handlers for h in b.block):
+        seen = []
+        for handler in handlers:
             if listeners := handler.listen:
                 if notification in handler.get_validated_value(
                     'listen',
@@ -556,6 +560,9 @@
                     listeners,
                     templar,
                 ):
+                    if handler.name and handler.name in seen:
+                        continue
+                    seen.append(handler.name)
                     yield handler
 
     @debug_closure
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible-core-2.15.3/lib/ansible/release.py 
new/ansible-core-2.15.4/lib/ansible/release.py
--- old/ansible-core-2.15.3/lib/ansible/release.py      2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/release.py      2023-09-12 
00:11:17.000000000 +0200
@@ -19,6 +19,6 @@
 from __future__ import (absolute_import, division, print_function)
 __metaclass__ = type
 
-__version__ = '2.15.3'
+__version__ = '2.15.4'
 __author__ = 'Ansible, Inc.'
 __codename__ = "Ten Years Gone"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible-core-2.15.3/lib/ansible/utils/display.py 
new/ansible-core-2.15.4/lib/ansible/utils/display.py
--- old/ansible-core-2.15.3/lib/ansible/utils/display.py        2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible/utils/display.py        2023-09-12 
00:11:17.000000000 +0200
@@ -698,6 +698,8 @@
                 os.set_blocking(self._stdin_fd, False)
                 while key_pressed is None and (seconds is None or (time.time() 
- start < seconds)):
                     key_pressed = self._stdin.read(1)
+                    # throttle to prevent excess CPU consumption
+                    time.sleep(C.DEFAULT_INTERNAL_POLL_INTERVAL)
             finally:
                 os.set_blocking(self._stdin_fd, True)
                 if key_pressed is None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible_core.egg-info/PKG-INFO 
new/ansible-core-2.15.4/lib/ansible_core.egg-info/PKG-INFO
--- old/ansible-core-2.15.3/lib/ansible_core.egg-info/PKG-INFO  2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible_core.egg-info/PKG-INFO  2023-09-12 
00:11:17.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: ansible-core
-Version: 2.15.3
+Version: 2.15.4
 Summary: Radically simple IT automation
 Home-page: https://ansible.com/
 Author: Ansible, Inc.
@@ -31,6 +31,12 @@
 Requires-Python: >=3.9
 Description-Content-Type: text/markdown
 License-File: COPYING
+Requires-Dist: jinja2>=3.0.0
+Requires-Dist: PyYAML>=5.1
+Requires-Dist: cryptography
+Requires-Dist: packaging
+Requires-Dist: importlib_resources<5.1,>=5.0; python_version < "3.10"
+Requires-Dist: resolvelib<1.1.0,>=0.5.3
 
 [![PyPI 
version](https://img.shields.io/pypi/v/ansible-core.svg)](https://pypi.org/project/ansible-core)
 [![Docs 
badge](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.ansible.com/ansible/latest/)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/lib/ansible_core.egg-info/SOURCES.txt 
new/ansible-core-2.15.4/lib/ansible_core.egg-info/SOURCES.txt
--- old/ansible-core-2.15.3/lib/ansible_core.egg-info/SOURCES.txt       
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/lib/ansible_core.egg-info/SOURCES.txt       
2023-09-12 00:11:17.000000000 +0200
@@ -984,6 +984,10 @@
 test/integration/targets/ansible-test-git/collection-tests/git-common.bash
 test/integration/targets/ansible-test-git/collection-tests/install-git.yml
 test/integration/targets/ansible-test-git/collection-tests/uninstall-git.yml
+test/integration/targets/ansible-test-installed/aliases
+test/integration/targets/ansible-test-installed/runme.sh
+test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/aliases
+test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/runme.sh
 test/integration/targets/ansible-test-integration/aliases
 test/integration/targets/ansible-test-integration/runme.sh
 test/integration/targets/ansible-test-integration-constraints/aliases
@@ -1590,6 +1594,7 @@
 test/integration/targets/command_shell/files/remove_afile.sh
 test/integration/targets/command_shell/files/test.sh
 test/integration/targets/command_shell/meta/main.yml
+test/integration/targets/command_shell/scripts/yoink.sh
 test/integration/targets/command_shell/tasks/main.yml
 test/integration/targets/common_network/aliases
 test/integration/targets/common_network/tasks/main.yml
@@ -2002,6 +2007,7 @@
 test/integration/targets/handlers/test_handlers_meta.yml
 test/integration/targets/handlers/test_handlers_template_run_once.yml
 test/integration/targets/handlers/test_include_role_handler_once.yml
+test/integration/targets/handlers/test_listen_role_dedup.yml
 test/integration/targets/handlers/test_listening_handlers.yml
 test/integration/targets/handlers/test_notify_included-handlers.yml
 test/integration/targets/handlers/test_notify_included.yml
@@ -2010,6 +2016,10 @@
 test/integration/targets/handlers/test_skip_flush.yml
 test/integration/targets/handlers/test_templating_in_handlers.yml
 
test/integration/targets/handlers/roles/import_template_handler_names/tasks/main.yml
+test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml
+test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml
+test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml
+test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml
 
test/integration/targets/handlers/roles/template_handler_names/handlers/main.yml
 
test/integration/targets/handlers/roles/template_handler_names/tasks/evaluation_time.yml
 
test/integration/targets/handlers/roles/template_handler_names/tasks/lazy_evaluation.yml
@@ -2028,6 +2038,11 @@
 
test/integration/targets/handlers/roles/test_handlers_meta/handlers/alternate.yml
 test/integration/targets/handlers/roles/test_handlers_meta/handlers/main.yml
 test/integration/targets/handlers/roles/test_handlers_meta/tasks/main.yml
+test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml
+test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml
+test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml
+test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml
+test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml
 
test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/A.yml
 
test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/main.yml
 
test/integration/targets/handlers/roles/test_role_handlers_include_tasks/tasks/B.yml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible-core-2.15.3/packaging/release.py 
new/ansible-core-2.15.4/packaging/release.py
--- old/ansible-core-2.15.3/packaging/release.py        2023-08-14 
20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/packaging/release.py        2023-09-12 
00:11:17.000000000 +0200
@@ -42,7 +42,7 @@
 # region CLI Framework
 
 
-C = t.TypeVar("C", bound=t.Callable[[...], None])
+C = t.TypeVar("C", bound=t.Callable[..., None])
 
 
 def path_to_str(value: t.Any) -> str:
@@ -50,12 +50,27 @@
     return f"{value}/" if isinstance(value, pathlib.Path) and value.is_dir() 
else str(value)
 
 
+@t.overload
+def run(*args: t.Any, env: dict[str, t.Any] | None, cwd: pathlib.Path | str, 
capture_output: t.Literal[True]) -> CompletedProcess:
+    ...
+
+
+@t.overload
+def run(*args: t.Any, env: dict[str, t.Any] | None, cwd: pathlib.Path | str, 
capture_output: t.Literal[False]) -> None:
+    ...
+
+
+@t.overload
+def run(*args: t.Any, env: dict[str, t.Any] | None, cwd: pathlib.Path | str) 
-> None:
+    ...
+
+
 def run(
     *args: t.Any,
     env: dict[str, t.Any] | None,
     cwd: pathlib.Path | str,
     capture_output: bool = False,
-) -> CompletedProcess:
+) -> CompletedProcess | None:
     """Run the specified command."""
     args = [arg.relative_to(cwd) if isinstance(arg, pathlib.Path) else arg for 
arg in args]
 
@@ -76,16 +91,18 @@
             stderr=ex.stderr,
         ) from None
 
+    if not capture_output:
+        return None
+
     # improve type hinting
     return CompletedProcess(
-        args=str_args,
         stdout=p.stdout,
         stderr=p.stderr,
     )
 
 
 @contextlib.contextmanager
-def suppress_when(error_as_warning: bool) -> None:
+def suppress_when(error_as_warning: bool) -> t.Generator[None, None, None]:
     """Conditionally convert an ApplicationError in the provided context to a 
warning."""
     if error_as_warning:
         try:
@@ -122,9 +139,8 @@
 class CompletedProcess:
     """Results from a completed process."""
 
-    args: tuple[str, ...]
-    stdout: str | None
-    stderr: str | None
+    stdout: str
+    stderr: str
 
 
 class Display:
@@ -167,7 +183,7 @@
     """
 
     def __init__(self, **kwargs: dict[str, t.Any] | None) -> None:
-        self.commands: list[C] = []
+        self.commands: list[t.Callable[..., None]] = []
         self.arguments = kwargs
         self.parsed_arguments: argparse.Namespace | None = None
 
@@ -176,7 +192,7 @@
         self.commands.append(func)
         return func
 
-    def run(self, *args: C, **kwargs) -> None:
+    def run(self, *args: t.Callable[..., None], **kwargs) -> None:
         """Run the specified command(s), using any provided internal args."""
         for arg in args:
             self._run(arg, **kwargs)
@@ -203,6 +219,9 @@
                 arguments = arguments.copy()
                 exclusive = arguments.pop("exclusive", None)
 
+                # noinspection PyProtectedMember, PyUnresolvedReferences
+                command_parser: argparse._ActionsContainer
+
                 if exclusive:
                     if exclusive not in exclusive_groups:
                         exclusive_groups[exclusive] = 
func_parser.add_mutually_exclusive_group()
@@ -234,7 +253,7 @@
             display.fatal(ex)
             sys.exit(1)
 
-    def _run(self, func: C, **kwargs) -> None:
+    def _run(self, func: t.Callable[..., None], **kwargs) -> None:
         """Run the specified command, using any provided internal args."""
         signature = inspect.signature(func)
         func_args = {name: getattr(self.parsed_arguments, name) for name in 
signature.parameters if hasattr(self.parsed_arguments, name)}
@@ -253,7 +272,7 @@
         display.show(f"<== {label}", color=Display.BLUE)
 
     @staticmethod
-    def _format_command_name(func: C) -> str:
+    def _format_command_name(func: t.Callable[..., None]) -> str:
         """Return the friendly name of the given command."""
         return func.__name__.replace("_", "-")
 
@@ -441,7 +460,22 @@
         raise NotImplementedError(self)
 
 
-def git(*args: t.Any, capture_output: bool = False) -> CompletedProcess:
+@t.overload
+def git(*args: t.Any, capture_output: t.Literal[True]) -> CompletedProcess:
+    ...
+
+
+@t.overload
+def git(*args: t.Any, capture_output: t.Literal[False]) -> None:
+    ...
+
+
+@t.overload
+def git(*args: t.Any) -> None:
+    ...
+
+
+def git(*args: t.Any, capture_output: t.Literal[True] | t.Literal[False] = 
False) -> CompletedProcess | None:
     """Run the specified git command."""
     return run("git", *args, env=None, cwd=CHECKOUT_DIR, 
capture_output=capture_output)
 
@@ -534,10 +568,6 @@
 ##### ISSUE TYPE
 
 Feature Pull Request
-
-##### COMPONENT NAME
-
-ansible
 """
 
     return body.lstrip()
@@ -629,7 +659,7 @@
 
 
 @functools.cache
-def ensure_venv() -> dict[str, str]:
+def ensure_venv() -> dict[str, t.Any]:
     """Ensure the release venv is ready and return the env vars needed to use 
it."""
 
     # TODO: consider freezing the ansible and release requirements along with 
their dependencies
@@ -943,7 +973,7 @@
     variables = dict(
         version=version,
         releases=get_release_artifact_details(repository, version, validate),
-        changelog=f"https://github.com/{upstream.user}/{upstream.repo}/blob/v{ 
version }/changelogs/CHANGELOG-v{ version.major }.{ version.minor }.rst",
+        
changelog=f"https://github.com/{upstream.user}/{upstream.repo}/blob/v{version}/changelogs/CHANGELOG-v{version.major}.{version.minor}.rst";,
     )
 
     release_notes = template.render(**variables).strip()
@@ -1274,7 +1304,7 @@
         commit_time = int(git("show", "-s", "--format=%ct", 
capture_output=True).stdout)
 
         env.update(
-            SOURCE_DATE_EPOCH=str(commit_time),
+            SOURCE_DATE_EPOCH=commit_time,
         )
 
         git("worktree", "add", "-d", temp_dir)
@@ -1312,7 +1342,11 @@
             except FileNotFoundError:
                 raise ApplicationError(f"Missing sdist: 
{sdist_file.relative_to(CHECKOUT_DIR)}") from None
 
-            sdist.extractall(temp_dir)
+            # deprecated: description='extractall fallback without filter' 
python_version='3.11'
+            if hasattr(tarfile, 'data_filter'):
+                sdist.extractall(temp_dir, filter='data')  # type: 
ignore[call-arg]
+            else:
+                sdist.extractall(temp_dir)
 
         pyc_glob = "*.pyc*"
         pyc_files = sorted(path.relative_to(temp_dir) for path in 
temp_dir.rglob(pyc_glob))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py
 
new/ansible-core-2.15.4/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py
--- 
old/ansible-core-2.15.3/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py
     2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py
     2023-09-12 00:11:17.000000000 +0200
@@ -152,7 +152,12 @@
 
             # Extract the tarfile to sign the MANIFEST.json
             with tarfile.open(collection_path, mode='r') as collection_tar:
-                collection_tar.extractall(path=os.path.join(collection_dir, 
'%s-%s-%s' % (namespace, name, version)))
+                # deprecated: description='extractall fallback without filter' 
python_version='3.11'
+                # Replace 'tar_filter' with 'data_filter' and 'filter=tar' 
with 'filter=data' once Python 3.12 is minimum requirement.
+                if hasattr(tarfile, 'tar_filter'):
+                    
collection_tar.extractall(path=os.path.join(collection_dir, '%s-%s-%s' % 
(namespace, name, version)), filter='tar')
+                else:
+                    
collection_tar.extractall(path=os.path.join(collection_dir, '%s-%s-%s' % 
(namespace, name, version)))
 
             manifest_path = os.path.join(collection_dir, '%s-%s-%s' % 
(namespace, name, version), 'MANIFEST.json')
             signature_path = os.path.join(module.params['signature_dir'], 
'%s-%s-%s-MANIFEST.json.asc' % (namespace, name, version))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/ansible-test-installed/aliases 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/aliases
--- 
old/ansible-core-2.15.3/test/integration/targets/ansible-test-installed/aliases 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/aliases 
    2023-09-12 00:11:17.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.15.3/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/aliases
 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/aliases
--- 
old/ansible-core-2.15.3/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/aliases
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/aliases
      2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1 @@
+context/controller
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/runme.sh
 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/runme.sh
--- 
old/ansible-core-2.15.3/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/runme.sh
     1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/ansible_collections/ns/col/tests/integration/targets/installed/runme.sh
     2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# This test ensures that the bin entry points created by ansible-test work
+# when ansible-test is running from an install instead of from source.
+
+set -eux
+
+# The third PATH entry is the injected bin directory created by ansible-test.
+bin_dir="$(python -c 'import os; print(os.environ["PATH"].split(":")[2])')"
+
+while IFS= read -r name
+do
+    bin="${bin_dir}/${name}"
+
+    entry_point="${name//ansible-/}"
+    entry_point="${entry_point//ansible/adhoc}"
+
+    echo "=== ${name} (${entry_point})=${bin} ==="
+
+    if [ "${name}" == "ansible-test" ]; then
+        echo "skipped - ansible-test does not support self-testing from an 
install"
+    else
+        "${bin}" --version | tee /dev/stderr | grep -Eo "(^${name}\ \[core\ 
.*|executable location = ${bin}$)"
+    fi
+done < entry-points.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/ansible-test-installed/runme.sh
 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/runme.sh
--- 
old/ansible-core-2.15.3/test/integration/targets/ansible-test-installed/runme.sh
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/ansible-test-installed/runme.sh
    2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+base_dir="$(dirname "$(dirname "$(dirname "$(dirname "${OUTPUT_DIR}")")")")"
+bin_dir="${base_dir}/bin"
+
+source ../collection/setup.sh
+source virtualenv.sh
+
+unset PYTHONPATH
+
+# find the bin entry points to test
+ls "${bin_dir}" > tests/integration/targets/installed/entry-points.txt
+
+# deps are already installed, using --no-deps to avoid re-installing them
+pip install "${base_dir}" --disable-pip-version-check --no-deps
+
+# verify entry point generation without delegation
+ansible-test integration --color --truncate 0 "${@}"
+
+# verify entry point generation with same-host delegation
+ansible-test integration --venv --color --truncate 0 "${@}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/command_shell/scripts/yoink.sh 
new/ansible-core-2.15.4/test/integration/targets/command_shell/scripts/yoink.sh
--- 
old/ansible-core-2.15.3/test/integration/targets/command_shell/scripts/yoink.sh 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/command_shell/scripts/yoink.sh 
    2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+sleep 10
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/command_shell/tasks/main.yml 
new/ansible-core-2.15.4/test/integration/targets/command_shell/tasks/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/command_shell/tasks/main.yml   
    2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/integration/targets/command_shell/tasks/main.yml   
    2023-09-12 00:11:17.000000000 +0200
@@ -546,3 +546,8 @@
       - command_strip.stderr == 'hello \n '
       - command_no_strip.stdout== 'hello \n \r\n'
       - command_no_strip.stderr == 'hello \n \r\n'
+
+- name: Run command that backgrounds, to ensure no hang
+  shell: '{{ role_path }}/scripts/yoink.sh &'
+  delegate_to: localhost
+  timeout: 5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/filter_encryption/base.yml 
new/ansible-core-2.15.4/test/integration/targets/filter_encryption/base.yml
--- old/ansible-core-2.15.3/test/integration/targets/filter_encryption/base.yml 
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/integration/targets/filter_encryption/base.yml 
2023-09-12 00:11:17.000000000 +0200
@@ -2,6 +2,7 @@
   gather_facts: true
   vars:
     data: secret
+    data2: 'foo: bar\n'
     dvault: '{{ "secret"|vault("test")}}'
     password: test
     s_32: '{{(2**31-1)}}'
@@ -21,6 +22,15 @@
     is_64: '{{ "64" in ansible_facts["architecture"] }}'
     salt: '{{ is_64|bool|ternary(s_64, s_32)|random(seed=inventory_hostname)}}'
     vaultedstring: '{{ is_64|bool|ternary(vaultedstring_64, vaultedstring_32) 
}}'
+    # command line vaulted data2
+    vaulted_id: !vault |
+                $ANSIBLE_VAULT;1.2;AES256;test1
+                
36383733336533656264393332663131613335333332346439356164383935656234663631356430
+                
3533353537343834333538356366376233326364613362640a623832636339363966336238393039
+                
35316562626335306534356162623030613566306235623863373036626531346364626166656134
+                
3063376436656635330a363636376131663362633731313964353061663661376638326461393736
+                3863
+    vaulted_to_id: "{{data2|vault('test1@secret', vault_id='test1')}}"
 
   tasks:
     - name: check vaulting
@@ -35,3 +45,5 @@
         that:
           - vaultedstring|unvault(password) == data
           - vault|unvault(password) == data
+          - vaulted_id|unvault('test1@secret', vault_id='test1')
+          - vaulted_to_id|unvault('test1@secret', vault_id='test1')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml
 2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1 @@
+v: foo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml
    2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,2 @@
+- include_role:
+    name: r2-dep_chain-vars
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml
 2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,4 @@
+- name: h
+  assert:
+    that:
+      - v is defined
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml
    2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,2 @@
+- command: echo
+  notify: h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml
     1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml
     2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,4 @@
+- name: role_handler
+  debug:
+    msg: "a handler from a role"
+  listen: role_handler
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml
  2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,2 @@
+dependencies:
+  - test_listen_role_dedup_global
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml
 2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,3 @@
+- name: a task from role1
+  command: echo
+  notify: role_handler
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml
  2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,2 @@
+dependencies:
+  - test_listen_role_dedup_global
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml
 2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,3 @@
+- name: a task from role2
+  command: echo
+  notify: role_handler
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/runme.sh 
new/ansible-core-2.15.4/test/integration/targets/handlers/runme.sh
--- old/ansible-core-2.15.3/test/integration/targets/handlers/runme.sh  
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/integration/targets/handlers/runme.sh  
2023-09-12 00:11:17.000000000 +0200
@@ -187,3 +187,8 @@
 
 ansible-playbook test_include_role_handler_once.yml -i inventory.handlers "$@" 
2>&1 | tee out.txt
 [ "$(grep out.txt -ce 'handler ran')" = "1" ]
+
+ansible-playbook test_listen_role_dedup.yml "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'a handler from a role')" = "1" ]
+
+ansible localhost -m include_role -a "name=r1-dep_chain-vars" "$@"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/handlers/test_listen_role_dedup.yml
 
new/ansible-core-2.15.4/test/integration/targets/handlers/test_listen_role_dedup.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/handlers/test_listen_role_dedup.yml
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible-core-2.15.4/test/integration/targets/handlers/test_listen_role_dedup.yml
        2023-09-12 00:11:17.000000000 +0200
@@ -0,0 +1,5 @@
+- hosts: localhost
+  gather_facts: false
+  roles:
+    - test_listen_role_dedup_role1
+    - test_listen_role_dedup_role2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/lookup_first_found/tasks/main.yml
 
new/ansible-core-2.15.4/test/integration/targets/lookup_first_found/tasks/main.yml
--- 
old/ansible-core-2.15.3/test/integration/targets/lookup_first_found/tasks/main.yml
  2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/integration/targets/lookup_first_found/tasks/main.yml
  2023-09-12 00:11:17.000000000 +0200
@@ -94,3 +94,11 @@
 - assert:
     that:
       - foo is defined
+
+# TODO: no 'terms' test
+- name: test first_found lookup with no terms
+  set_fact:
+    no_terms: "{{ query('first_found', files=['missing1', 'hosts', 
'missing2'], paths=['/etc'], errors='ignore') }}"
+
+- assert:
+    that: "no_terms|first == '/etc/hosts'"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/module_utils_Ansible.Become/library/ansible_become_tests.ps1
 
new/ansible-core-2.15.4/test/integration/targets/module_utils_Ansible.Become/library/ansible_become_tests.ps1
--- 
old/ansible-core-2.15.3/test/integration/targets/module_utils_Ansible.Become/library/ansible_become_tests.ps1
       2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/integration/targets/module_utils_Ansible.Become/library/ansible_become_tests.ps1
       2023-09-12 00:11:17.000000000 +0200
@@ -48,7 +48,6 @@
     Add-Type -TypeDefinition @'
 using Microsoft.Win32.SafeHandles;
 using System;
-using System.Runtime.ConstrainedExecution;
 using System.Runtime.InteropServices;
 using System.Security.Principal;
 using System.Text;
@@ -212,7 +211,6 @@
     {
         public SafeLsaMemoryBuffer() : base(true) { }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             UInt32 res = NativeMethods.LsaFreeReturnBuffer(handle);
@@ -232,7 +230,6 @@
             base.SetHandle(handle);
         }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             Marshal.FreeHGlobal(handle);
@@ -245,7 +242,6 @@
         public SafeNativeHandle() : base(true) { }
         public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = 
handle; }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             return NativeMethods.CloseHandle(handle);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/unvault/main.yml 
new/ansible-core-2.15.4/test/integration/targets/unvault/main.yml
--- old/ansible-core-2.15.3/test/integration/targets/unvault/main.yml   
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/integration/targets/unvault/main.yml   
2023-09-12 00:11:17.000000000 +0200
@@ -1,4 +1,5 @@
 - hosts: localhost
+  gather_facts: false
   tasks:
     - set_fact:
         unvaulted: "{{ lookup('unvault', 'vault') }}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/integration/targets/unvault/runme.sh 
new/ansible-core-2.15.4/test/integration/targets/unvault/runme.sh
--- old/ansible-core-2.15.3/test/integration/targets/unvault/runme.sh   
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/integration/targets/unvault/runme.sh   
2023-09-12 00:11:17.000000000 +0200
@@ -2,5 +2,5 @@
 
 set -eux
 
-
+# simple run
 ansible-playbook --vault-password-file password main.yml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/ansible_util.py 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/ansible_util.py
--- old/ansible-core-2.15.3/test/lib/ansible_test/_internal/ansible_util.py     
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/lib/ansible_test/_internal/ansible_util.py     
2023-09-12 00:11:17.000000000 +0200
@@ -3,9 +3,11 @@
 
 import json
 import os
+import shutil
 import typing as t
 
 from .constants import (
+    ANSIBLE_BIN_SYMLINK_MAP,
     SOFT_RLIMIT_NOFILE,
 )
 
@@ -17,12 +19,15 @@
     common_environment,
     ApplicationError,
     ANSIBLE_LIB_ROOT,
+    ANSIBLE_TEST_ROOT,
     ANSIBLE_TEST_DATA_ROOT,
-    ANSIBLE_BIN_PATH,
+    ANSIBLE_ROOT,
     ANSIBLE_SOURCE_ROOT,
     ANSIBLE_TEST_TOOLS_ROOT,
+    MODE_FILE_EXECUTE,
     get_ansible_version,
     raw_command,
+    verified_chmod,
 )
 
 from .util_common import (
@@ -78,8 +83,10 @@
     env = common_environment()
     path = env['PATH']
 
-    if not path.startswith(ANSIBLE_BIN_PATH + os.path.pathsep):
-        path = ANSIBLE_BIN_PATH + os.path.pathsep + path
+    ansible_bin_path = get_ansible_bin_path(args)
+
+    if not path.startswith(ansible_bin_path + os.path.pathsep):
+        path = ansible_bin_path + os.path.pathsep + path
 
     if not ansible_config:
         # use the default empty configuration unless one has been provided
@@ -197,6 +204,52 @@
 
 
 @mutex
+def get_ansible_bin_path(args: CommonConfig) -> str:
+    """
+    Return a directory usable for PATH, containing only the ansible entry 
points.
+    If a temporary directory is required, it will be cached for the lifetime 
of the process and cleaned up at exit.
+    """
+    try:
+        return get_ansible_bin_path.bin_path  # type: ignore[attr-defined]
+    except AttributeError:
+        pass
+
+    if ANSIBLE_SOURCE_ROOT:
+        # when running from source there is no need for a temporary directory 
since we already have known entry point scripts
+        bin_path = os.path.join(ANSIBLE_ROOT, 'bin')
+    else:
+        # when not running from source the installed entry points cannot be 
relied upon
+        # doing so would require using the interpreter specified by those 
entry points, which conflicts with using our interpreter and injector
+        # instead a temporary directory is created which contains only ansible 
entry points
+        # symbolic links cannot be used since the files are likely not 
executable
+        bin_path = create_temp_dir(prefix='ansible-test-', suffix='-bin')
+        bin_links = {os.path.join(bin_path, name): get_cli_path(path) for 
name, path in ANSIBLE_BIN_SYMLINK_MAP.items()}
+
+        if not args.explain:
+            for dst, src in bin_links.items():
+                shutil.copy(src, dst)
+                verified_chmod(dst, MODE_FILE_EXECUTE)
+
+    get_ansible_bin_path.bin_path = bin_path  # type: ignore[attr-defined]
+
+    return bin_path
+
+
+def get_cli_path(path: str) -> str:
+    """Return the absolute path to the CLI script from the given path which is 
relative to the `bin` directory of the original source tree layout."""
+    path_rewrite = {
+        '../lib/ansible/': ANSIBLE_LIB_ROOT,
+        '../test/lib/ansible_test/': ANSIBLE_TEST_ROOT,
+    }
+
+    for prefix, destination in path_rewrite.items():
+        if path.startswith(prefix):
+            return os.path.join(destination, path[len(prefix):])
+
+    raise RuntimeError(path)
+
+
+@mutex
 def get_ansible_python_path(args: CommonConfig) -> str:
     """
     Return a directory usable for PYTHONPATH, containing only the ansible 
package.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/commands/sanity/bin_symlinks.py
 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/commands/sanity/bin_symlinks.py
--- 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/commands/sanity/bin_symlinks.py
     2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/commands/sanity/bin_symlinks.py
     2023-09-12 00:11:17.000000000 +0200
@@ -32,7 +32,7 @@
 )
 
 from ...util import (
-    ANSIBLE_BIN_PATH,
+    ANSIBLE_SOURCE_ROOT,
 )
 
 
@@ -52,7 +52,7 @@
         return True
 
     def test(self, args: SanityConfig, targets: SanityTargets) -> TestResult:
-        bin_root = ANSIBLE_BIN_PATH
+        bin_root = os.path.join(ANSIBLE_SOURCE_ROOT, 'bin')
         bin_names = os.listdir(bin_root)
         bin_paths = sorted(os.path.join(bin_root, path) for path in bin_names)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/commands/sanity/mypy.py 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/commands/sanity/mypy.py
--- 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/commands/sanity/mypy.py 
    2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/commands/sanity/mypy.py 
    2023-09-12 00:11:17.000000000 +0200
@@ -73,7 +73,7 @@
         """Return the given list of test targets, filtered to include only 
those relevant for the test."""
         return [target for target in targets if 
os.path.splitext(target.path)[1] == '.py' and target.path not in 
self.vendored_paths and (
                 target.path.startswith('lib/ansible/') or 
target.path.startswith('test/lib/ansible_test/_internal/')
-                or target.path.startswith('packaging/cli-doc/')
+                or target.path.startswith('packaging/')
                 or 
target.path.startswith('test/lib/ansible_test/_util/target/sanity/import/'))]
 
     @property
@@ -117,7 +117,7 @@
             MyPyContext('ansible-test', ['test/lib/ansible_test/_internal/'], 
controller_python_versions),
             MyPyContext('ansible-core', ['lib/ansible/'], 
controller_python_versions),
             MyPyContext('modules', ['lib/ansible/modules/', 
'lib/ansible/module_utils/'], remote_only_python_versions),
-            MyPyContext('packaging', ['packaging/cli-doc/'], 
controller_python_versions),
+            MyPyContext('packaging', ['packaging/'], 
controller_python_versions),
         )
 
         unfiltered_messages: list[SanityMessage] = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
--- 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
 2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
 2023-09-12 00:11:17.000000000 +0200
@@ -159,7 +159,11 @@
                 temp_dir = process_scoped_temporary_directory(args)
 
                 with tarfile.open(path) as file:
-                    file.extractall(temp_dir)
+                    # deprecated: description='extractall fallback without 
filter' python_version='3.11'
+                    if hasattr(tarfile, 'data_filter'):
+                        file.extractall(temp_dir, filter='data')  # type: 
ignore[call-arg]
+                    else:
+                        file.extractall(temp_dir)
 
                 cmd.extend([
                     '--original-plugins', temp_dir,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/constants.py 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/constants.py
--- old/ansible-core-2.15.3/test/lib/ansible_test/_internal/constants.py        
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/lib/ansible_test/_internal/constants.py        
2023-09-12 00:11:17.000000000 +0200
@@ -33,6 +33,7 @@
 # This bin symlink map must exactly match the contents of the bin directory.
 # It is necessary for payload creation to reconstruct the bin directory when 
running ansible-test from an installed version of ansible.
 # It is also used to construct the injector directory at runtime.
+# It is also used to construct entry points when not running ansible-test from 
source.
 ANSIBLE_BIN_SYMLINK_MAP = {
     'ansible': '../lib/ansible/cli/adhoc.py',
     'ansible-config': '../lib/ansible/cli/config.py',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/delegation.py 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/delegation.py
--- old/ansible-core-2.15.3/test/lib/ansible_test/_internal/delegation.py       
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/lib/ansible_test/_internal/delegation.py       
2023-09-12 00:11:17.000000000 +0200
@@ -33,7 +33,6 @@
     SubprocessError,
     display,
     filter_args,
-    ANSIBLE_BIN_PATH,
     ANSIBLE_LIB_ROOT,
     ANSIBLE_TEST_ROOT,
     OutputStream,
@@ -44,6 +43,10 @@
     process_scoped_temporary_directory,
 )
 
+from .ansible_util import (
+    get_ansible_bin_path,
+)
+
 from .containers import (
     support_container_context,
     ContainerDatabase,
@@ -145,7 +148,7 @@
             con.extract_archive(chdir=working_directory, src=payload_file)
     else:
         content_root = working_directory
-        ansible_bin_path = ANSIBLE_BIN_PATH
+        ansible_bin_path = get_ansible_bin_path(args)
 
     command = generate_command(args, host_state.controller_profile.python, 
ansible_bin_path, content_root, exclude, require)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/lib/ansible_test/_internal/util.py 
new/ansible-core-2.15.4/test/lib/ansible_test/_internal/util.py
--- old/ansible-core-2.15.3/test/lib/ansible_test/_internal/util.py     
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/lib/ansible_test/_internal/util.py     
2023-09-12 00:11:17.000000000 +0200
@@ -74,14 +74,12 @@
 
 # assume running from install
 ANSIBLE_ROOT = os.path.dirname(ANSIBLE_TEST_ROOT)
-ANSIBLE_BIN_PATH = os.path.dirname(os.path.abspath(sys.argv[0]))
 ANSIBLE_LIB_ROOT = os.path.join(ANSIBLE_ROOT, 'ansible')
 ANSIBLE_SOURCE_ROOT = None
 
 if not os.path.exists(ANSIBLE_LIB_ROOT):
     # running from source
     ANSIBLE_ROOT = 
os.path.dirname(os.path.dirname(os.path.dirname(ANSIBLE_TEST_ROOT)))
-    ANSIBLE_BIN_PATH = os.path.join(ANSIBLE_ROOT, 'bin')
     ANSIBLE_LIB_ROOT = os.path.join(ANSIBLE_ROOT, 'lib', 'ansible')
     ANSIBLE_SOURCE_ROOT = ANSIBLE_ROOT
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/support/windows-integration/plugins/module_utils/Ansible.Service.cs
 
new/ansible-core-2.15.4/test/support/windows-integration/plugins/module_utils/Ansible.Service.cs
--- 
old/ansible-core-2.15.3/test/support/windows-integration/plugins/module_utils/Ansible.Service.cs
    2023-08-14 20:18:23.000000000 +0200
+++ 
new/ansible-core-2.15.4/test/support/windows-integration/plugins/module_utils/Ansible.Service.cs
    2023-09-12 00:11:17.000000000 +0200
@@ -2,7 +2,6 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Runtime.ConstrainedExecution;
 using System.Runtime.InteropServices;
 using System.Security.Principal;
 using System.Text;
@@ -274,7 +273,6 @@
             base.SetHandle(handle);
         }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             Marshal.FreeHGlobal(handle);
@@ -287,7 +285,6 @@
         public SafeServiceHandle() : base(true) { }
         public SafeServiceHandle(IntPtr handle) : base(true) { this.handle = 
handle; }
 
-        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
         protected override bool ReleaseHandle()
         {
             return NativeMethods.CloseServiceHandle(handle);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible-core-2.15.3/test/units/module_utils/basic/test_run_command.py 
new/ansible-core-2.15.4/test/units/module_utils/basic/test_run_command.py
--- old/ansible-core-2.15.3/test/units/module_utils/basic/test_run_command.py   
2023-08-14 20:18:23.000000000 +0200
+++ new/ansible-core-2.15.4/test/units/module_utils/basic/test_run_command.py   
2023-09-12 00:11:17.000000000 +0200
@@ -109,7 +109,7 @@
             super(MockSelector, self).close()
             self._file_objs = []
 
-    selectors.DefaultSelector = MockSelector
+    selectors.PollSelector = MockSelector
 
     subprocess = mocker.patch('ansible.module_utils.basic.subprocess')
     subprocess._output = {mocker.sentinel.stdout: SpecialBytesIO(b'', 
fh=mocker.sentinel.stdout),
@@ -147,7 +147,7 @@
                               for (arg, cmd_lst, cmd_str), sh in 
product(ARGS_DATA, (True, False))),
                              indirect=['stdin'])
     def test_args(self, cmd, expected, shell, rc_am):
-        rc_am.run_command(cmd, use_unsafe_shell=shell, 
prompt_regex='i_dont_exist')
+        rc_am.run_command(cmd, use_unsafe_shell=shell)
         assert rc_am._subprocess.Popen.called
         args, kwargs = rc_am._subprocess.Popen.call_args
         assert args == (expected, )
@@ -163,17 +163,17 @@
 class TestRunCommandCwd:
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
     def test_cwd(self, mocker, rc_am):
-        rc_am.run_command('/bin/ls', cwd='/new', prompt_regex='i_dont_exist')
+        rc_am.run_command('/bin/ls', cwd='/new')
         assert rc_am._subprocess.Popen.mock_calls[0][2]['cwd'] == b'/new'
 
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
     def test_cwd_relative_path(self, mocker, rc_am):
-        rc_am.run_command('/bin/ls', cwd='sub-dir', 
prompt_regex='i_dont_exist')
+        rc_am.run_command('/bin/ls', cwd='sub-dir')
         assert rc_am._subprocess.Popen.mock_calls[0][2]['cwd'] == 
b'/home/foo/sub-dir'
 
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
     def test_cwd_not_a_dir(self, mocker, rc_am):
-        rc_am.run_command('/bin/ls', cwd='/not-a-dir', 
prompt_regex='i_dont_exist')
+        rc_am.run_command('/bin/ls', cwd='/not-a-dir')
         assert rc_am._subprocess.Popen.mock_calls[0][2]['cwd'] == b'/not-a-dir'
 
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
@@ -212,14 +212,14 @@
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
     def test_check_rc_false(self, rc_am):
         rc_am._subprocess.Popen.return_value.returncode = 1
-        (rc, _, _) = rc_am.run_command('/bin/false', check_rc=False, 
prompt_regex='i_dont_exist')
+        (rc, _, _) = rc_am.run_command('/bin/false', check_rc=False)
         assert rc == 1
 
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
     def test_check_rc_true(self, rc_am):
         rc_am._subprocess.Popen.return_value.returncode = 1
         with pytest.raises(SystemExit):
-            rc_am.run_command('/bin/false', check_rc=True, 
prompt_regex='i_dont_exist')
+            rc_am.run_command('/bin/false', check_rc=True)
         assert rc_am.fail_json.called
         args, kwargs = rc_am.fail_json.call_args
         assert kwargs['rc'] == 1
@@ -228,7 +228,7 @@
 class TestRunCommandOutput:
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
     def test_text_stdin(self, rc_am):
-        (rc, stdout, stderr) = rc_am.run_command('/bin/foo', data='hello 
world', prompt_regex='i_dont_exist')
+        (rc, stdout, stderr) = rc_am.run_command('/bin/foo', data='hello 
world')
         assert rc_am._subprocess.Popen.return_value.stdin.getvalue() == 
b'hello world\n'
 
     @pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
@@ -237,7 +237,7 @@
                                      SpecialBytesIO(b'hello', 
fh=mocker.sentinel.stdout),
                                      mocker.sentinel.stderr:
                                      SpecialBytesIO(b'', 
fh=mocker.sentinel.stderr)}
-        (rc, stdout, stderr) = rc_am.run_command('/bin/cat hello.txt', 
prompt_regex='i_dont_exist')
+        (rc, stdout, stderr) = rc_am.run_command('/bin/cat hello.txt')
         assert rc == 0
         # module_utils function.  On py3 it returns text and py2 it returns
         # bytes because it's returning native strings
@@ -251,7 +251,7 @@
                                      mocker.sentinel.stderr:
                                      
SpecialBytesIO(u'لرئيسية'.encode('utf-8'),
                                                     fh=mocker.sentinel.stderr)}
-        (rc, stdout, stderr) = rc_am.run_command('/bin/something_ugly', 
prompt_regex='i_dont_exist')
+        (rc, stdout, stderr) = rc_am.run_command('/bin/something_ugly')
         assert rc == 0
         # module_utils function.  On py3 it returns text and py2 it returns
         # bytes because it's returning native strings

Reply via email to