Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ansible-core-2.16 for 
openSUSE:Factory checked in at 2024-09-16 17:40:42
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ansible-core-2.16 (Old)
 and      /work/SRC/openSUSE:Factory/.ansible-core-2.16.new.29891 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ansible-core-2.16"

Mon Sep 16 17:40:42 2024 rev:3 rq:1200969 version:2.16.11

Changes:
--------
--- /work/SRC/openSUSE:Factory/ansible-core-2.16/ansible-core-2.16.changes      
2024-08-15 09:58:41.039864846 +0200
+++ 
/work/SRC/openSUSE:Factory/.ansible-core-2.16.new.29891/ansible-core-2.16.changes
   2024-09-16 17:41:04.360086919 +0200
@@ -1,0 +2,40 @@
+Wed Sep 11 05:03:19 UTC 2024 - Johannes Kastl 
<opensuse_buildserv...@ojkastl.de>
+
+- update to 2.16.11:
+  
https://github.com/ansible/ansible/blob/v2.16.11/changelogs/CHANGELOG-v2.16.rst
+  * Bugfixes
+    - Fix SemanticVersion.parse() to store the version string so
+      that __repr__ reports it instead of None (#83831).
+    - Fix an issue where registered variable was not available for
+      templating in loop_control.label on skipped looped tasks
+      (#83619)
+    - Fix for meta tasks breaking host/fork affinity with
+      host_pinned strategy (#83294)
+    - Fix using the current task's directory for looking up
+      relative paths within roles (#82695).
+    - atomic_move - fix using the setgid bit on the parent
+      directory when creating files (#46742, #67177).
+    - connection plugins using the 'extras' option feature would
+      need variables to match the plugin's loaded name, sometimes
+      requiring fqcn, which is not the same as the
+      documented/declared/expected variables. Now we fall back to
+      the 'basename' of the fqcn, but plugin authors can still set
+      the expected value directly.
+    - csvfile lookup - give an error when no search term is
+      provided using modern config syntax (#83689).
+    - include_tasks - Display location when attempting to load a
+      task list where include_* did not specify any value - #83874
+    - module respawn - Address an issue with Python 2 where a
+      respawned module could not parse module args (#83812)
+    - powershell - Improve CLIXML decoding to decode all control
+      characters and unicode characters that are encoded as
+      surrogate pairs.
+    - psrp - Fix bug when attempting to fetch a file path that
+      contains special glob characters like []
+    - runtime-metadata sanity test - do not crash on deprecations
+      if galaxy.yml contains an empty version field (#83831).
+    - ssh - Fix bug when attempting to fetch a file path with
+      characters that should be quoted when using the piped
+      transfer method
+
+-------------------------------------------------------------------

Old:
----
  ansible_core-2.16.10.tar.gz
  ansible_core-2.16.10.tar.gz.sha256

New:
----
  ansible_core-2.16.11.tar.gz
  ansible_core-2.16.11.tar.gz.sha256

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

Other differences:
------------------
++++++ ansible-core-2.16.spec ++++++
--- /var/tmp/diff_new_pack.qVpFgW/_old  2024-09-16 17:41:05.408130296 +0200
+++ /var/tmp/diff_new_pack.qVpFgW/_new  2024-09-16 17:41:05.408130296 +0200
@@ -38,7 +38,7 @@
 %endif
 
 Name:           ansible-core-2.16
-Version:        2.16.10
+Version:        2.16.11
 Release:        0
 Summary:        Radically simple IT automation
 License:        GPL-3.0-or-later

++++++ ansible_core-2.16.10.tar.gz -> ansible_core-2.16.11.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/PKG-INFO 
new/ansible_core-2.16.11/PKG-INFO
--- old/ansible_core-2.16.10/PKG-INFO   2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/PKG-INFO   2024-09-09 21:26:18.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: ansible-core
-Version: 2.16.10
+Version: 2.16.11
 Summary: Radically simple IT automation
 Home-page: https://ansible.com/
 Author: Ansible, Inc.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/changelogs/CHANGELOG-v2.16.rst 
new/ansible_core-2.16.11/changelogs/CHANGELOG-v2.16.rst
--- old/ansible_core-2.16.10/changelogs/CHANGELOG-v2.16.rst     2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/changelogs/CHANGELOG-v2.16.rst     2024-09-09 
21:26:18.000000000 +0200
@@ -5,6 +5,33 @@
 .. contents:: Topics
 
 
+v2.16.11
+========
+
+Release Summary
+---------------
+
+| Release Date: 2024-09-09
+| `Porting Guide 
<https://docs.ansible.com/ansible-core/2.16/porting_guides/porting_guide_core_2.16.html>`__
+
+
+Bugfixes
+--------
+
+- Fix ``SemanticVersion.parse()`` to store the version string so that 
``__repr__`` reports it instead of ``None`` 
(https://github.com/ansible/ansible/pull/83831).
+- Fix an issue where registered variable was not available for templating in 
``loop_control.label`` on skipped looped tasks 
(https://github.com/ansible/ansible/issues/83619)
+- Fix for ``meta`` tasks breaking host/fork affinity with ``host_pinned`` 
strategy (https://github.com/ansible/ansible/issues/83294)
+- Fix using the current task's directory for looking up relative paths within 
roles (https://github.com/ansible/ansible/issues/82695).
+- atomic_move - fix using the setgid bit on the parent directory when creating 
files (https://github.com/ansible/ansible/issues/46742, 
https://github.com/ansible/ansible/issues/67177).
+- connection plugins using the 'extras' option feature would need variables to 
match the plugin's loaded name, sometimes requiring fqcn, which is not the same 
as the documented/declared/expected variables. Now we fall back to the 
'basename' of the fqcn, but plugin authors can still set the expected value 
directly.
+- csvfile lookup - give an error when no search term is provided using modern 
config syntax (https://github.com/ansible/ansible/issues/83689).
+- include_tasks - Display location when attempting to load a task list where 
``include_*`` did not specify any value - 
https://github.com/ansible/ansible/issues/83874
+- module respawn - Address an issue with Python 2 where a respawned module 
could not parse module args (https://github.com/ansible/ansible/issues/83812)
+- powershell - Improve CLIXML decoding to decode all control characters and 
unicode characters that are encoded as surrogate pairs.
+- psrp - Fix bug when attempting to fetch a file path that contains special 
glob characters like ``[]``
+- runtime-metadata sanity test - do not crash on deprecations if 
``galaxy.yml`` contains an empty ``version`` field 
(https://github.com/ansible/ansible/pull/83831).
+- ssh - Fix bug when attempting to fetch a file path with characters that 
should be quoted when using the ``piped`` transfer method
+
 v2.16.10
 ========
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/changelogs/changelog.yaml 
new/ansible_core-2.16.11/changelogs/changelog.yaml
--- old/ansible_core-2.16.10/changelogs/changelog.yaml  2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/changelogs/changelog.yaml  2024-09-09 
21:26:18.000000000 +0200
@@ -812,6 +812,69 @@
     - ansible-test-vyos.yml
     - mc_fix.yml
     release_date: '2024-08-05'
+  2.16.11:
+    changes:
+      release_summary: '| Release Date: 2024-09-09
+
+        | `Porting Guide 
<https://docs.ansible.com/ansible-core/2.16/porting_guides/porting_guide_core_2.16.html>`__
+
+        '
+    codename: All My Love
+    fragments:
+    - 2.16.11_summary.yaml
+    release_date: '2024-09-09'
+  2.16.11rc1:
+    changes:
+      bugfixes:
+      - Fix ``SemanticVersion.parse()`` to store the version string so that 
``__repr__``
+        reports it instead of ``None`` 
(https://github.com/ansible/ansible/pull/83831).
+      - Fix an issue where registered variable was not available for 
templating in
+        ``loop_control.label`` on skipped looped tasks 
(https://github.com/ansible/ansible/issues/83619)
+      - Fix for ``meta`` tasks breaking host/fork affinity with 
``host_pinned`` strategy
+        (https://github.com/ansible/ansible/issues/83294)
+      - Fix using the current task's directory for looking up relative paths 
within
+        roles (https://github.com/ansible/ansible/issues/82695).
+      - atomic_move - fix using the setgid bit on the parent directory when 
creating
+        files (https://github.com/ansible/ansible/issues/46742, 
https://github.com/ansible/ansible/issues/67177).
+      - connection plugins using the 'extras' option feature would need 
variables
+        to match the plugin's loaded name, sometimes requiring fqcn, which is 
not
+        the same as the documented/declared/expected variables. Now we fall 
back to
+        the 'basename' of the fqcn, but plugin authors can still set the 
expected
+        value directly.
+      - csvfile lookup - give an error when no search term is provided using 
modern
+        config syntax (https://github.com/ansible/ansible/issues/83689).
+      - include_tasks - Display location when attempting to load a task list 
where
+        ``include_*`` did not specify any value - 
https://github.com/ansible/ansible/issues/83874
+      - module respawn - Address an issue with Python 2 where a respawned 
module could
+        not parse module args (https://github.com/ansible/ansible/issues/83812)
+      - powershell - Improve CLIXML decoding to decode all control characters 
and
+        unicode characters that are encoded as surrogate pairs.
+      - psrp - Fix bug when attempting to fetch a file path that contains 
special
+        glob characters like ``[]``
+      - runtime-metadata sanity test - do not crash on deprecations if 
``galaxy.yml``
+        contains an empty ``version`` field 
(https://github.com/ansible/ansible/pull/83831).
+      - ssh - Fix bug when attempting to fetch a file path with characters 
that should
+        be quoted when using the ``piped`` transfer method
+      release_summary: '| Release Date: 2024-09-03
+
+        | `Porting Guide 
<https://docs.ansible.com/ansible-core/2.16/porting_guides/porting_guide_core_2.16.html>`__
+
+        '
+    codename: All My Love
+    fragments:
+    - 2.16.11rc1_summary.yaml
+    - 46742-atomic_move-fix-setgid.yml
+    - 83294-meta-host_pinned-affinity.yml
+    - 83619-loop-label-register.yml
+    - 83812-py2-respawn.yml
+    - 83831-runtime-metadata-fix.yml
+    - 83874-include-parse-error-location.yml
+    - dwim_is_role_fix_task_relative.yml
+    - extras_fix.yml
+    - fetch-filename.yml
+    - fix-inconsistent-csvfile-missing-search-error.yml
+    - powershell-clixml.yml
+    release_date: '2024-09-03'
   2.16.1rc1:
     changes:
       breaking_changes:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/executor/task_executor.py 
new/ansible_core-2.16.11/lib/ansible/executor/task_executor.py
--- old/ansible_core-2.16.10/lib/ansible/executor/task_executor.py      
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/executor/task_executor.py      
2024-09-09 21:26:18.000000000 +0200
@@ -32,7 +32,7 @@
 from ansible.utils.unsafe_proxy import to_unsafe_text, wrap_var
 from ansible.vars.clean import namespace_facts, clean_facts
 from ansible.utils.display import Display
-from ansible.utils.vars import combine_vars, isidentifier
+from ansible.utils.vars import combine_vars
 
 display = Display()
 
@@ -333,6 +333,13 @@
             (self._task, tmp_task) = (tmp_task, self._task)
             (self._play_context, tmp_play_context) = (tmp_play_context, 
self._play_context)
             res = self._execute(variables=task_vars)
+
+            if self._task.register:
+                # Ensure per loop iteration results are registered in case 
`_execute()`
+                # returns early (when conditional, failure, ...).
+                # This is needed in case the registered variable is used in 
the loop label template.
+                task_vars[self._task.register] = res
+
             task_fields = self._task.dump_attrs()
             (self._task, tmp_task) = (tmp_task, self._task)
             (self._play_context, tmp_play_context) = (tmp_play_context, 
self._play_context)
@@ -658,9 +665,6 @@
             # update the local copy of vars with the registered value, if 
specified,
             # or any facts which may have been generated by the module 
execution
             if self._task.register:
-                if not isidentifier(self._task.register):
-                    raise AnsibleError("Invalid variable name in 'register' 
specified: '%s'" % self._task.register)
-
                 vars_copy[self._task.register] = result
 
             if self._task.async_val > 0:
@@ -1049,7 +1053,7 @@
         # add extras if plugin supports them
         if getattr(self._connection, 'allow_extras', False):
             for k in variables:
-                if k.startswith('ansible_%s_' % self._connection._load_name) 
and k not in options:
+                if k.startswith('ansible_%s_' % 
self._connection.extras_prefix) and k not in options:
                     options['_extras'][k] = templar.template(variables[k])
 
         task_keys = self._task.dump_attrs()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/module_utils/ansible_release.py 
new/ansible_core-2.16.11/lib/ansible/module_utils/ansible_release.py
--- old/ansible_core-2.16.10/lib/ansible/module_utils/ansible_release.py        
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/module_utils/ansible_release.py        
2024-09-09 21:26:18.000000000 +0200
@@ -19,6 +19,6 @@
 from __future__ import (absolute_import, division, print_function)
 __metaclass__ = type
 
-__version__ = '2.16.10'
+__version__ = '2.16.11'
 __author__ = 'Ansible, Inc.'
 __codename__ = "All My Love"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/module_utils/basic.py 
new/ansible_core-2.16.11/lib/ansible/module_utils/basic.py
--- old/ansible_core-2.16.10/lib/ansible/module_utils/basic.py  2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/module_utils/basic.py  2024-09-09 
21:26:18.000000000 +0200
@@ -1759,8 +1759,12 @@
             umask = os.umask(0)
             os.umask(umask)
             os.chmod(b_dest, DEFAULT_PERM & ~umask)
+            dest_dir_stat = os.stat(os.path.dirname(b_dest))
             try:
-                os.chown(b_dest, os.geteuid(), os.getegid())
+                if dest_dir_stat.st_mode & stat.S_ISGID:
+                    os.chown(b_dest, os.geteuid(), dest_dir_stat.st_gid)
+                else:
+                    os.chown(b_dest, os.geteuid(), os.getegid())
             except OSError:
                 # We're okay with trying our best here.  If the user is not
                 # root (or old Unices) they won't be able to chown.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/module_utils/common/respawn.py 
new/ansible_core-2.16.11/lib/ansible/module_utils/common/respawn.py
--- old/ansible_core-2.16.10/lib/ansible/module_utils/common/respawn.py 
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/module_utils/common/respawn.py 
2024-09-09 21:26:18.000000000 +0200
@@ -92,6 +92,6 @@
     runpy.run_module(module_fqn, init_globals=dict(_respawned=True), 
run_name='__main__', alter_sys=True)
     '''
 
-    respawn_code = respawn_code_template.format(module_fqn=module_fqn, 
modlib_path=modlib_path, smuggled_args=smuggled_args.strip())
+    respawn_code = respawn_code_template.format(module_fqn=module_fqn, 
modlib_path=modlib_path, smuggled_args=to_bytes(smuggled_args).strip())
 
     return respawn_code
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/parsing/dataloader.py 
new/ansible_core-2.16.11/lib/ansible/parsing/dataloader.py
--- old/ansible_core-2.16.10/lib/ansible/parsing/dataloader.py  2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/parsing/dataloader.py  2024-09-09 
21:26:18.000000000 +0200
@@ -318,11 +318,10 @@
                 if (is_role or self._is_role(path)) and 
b_pb_base_dir.endswith(b'/tasks'):
                     search.append(os.path.join(os.path.dirname(b_pb_base_dir), 
b_dirname, b_source))
                     search.append(os.path.join(b_pb_base_dir, b_source))
-                else:
-                    # don't add dirname if user already is using it in source
-                    if b_source.split(b'/')[0] != dirname:
-                        search.append(os.path.join(b_upath, b_dirname, 
b_source))
-                    search.append(os.path.join(b_upath, b_source))
+                # don't add dirname if user already is using it in source
+                if b_source.split(b'/')[0] != dirname:
+                    search.append(os.path.join(b_upath, b_dirname, b_source))
+                search.append(os.path.join(b_upath, b_source))
 
             # always append basedir as last resort
             # don't add dirname if user already is using it in source
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/lib/ansible/playbook/task.py 
new/ansible_core-2.16.11/lib/ansible/playbook/task.py
--- old/ansible_core-2.16.10/lib/ansible/playbook/task.py       2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/playbook/task.py       2024-09-09 
21:26:18.000000000 +0200
@@ -39,6 +39,7 @@
 from ansible.utils.collection_loader import AnsibleCollectionConfig
 from ansible.utils.display import Display
 from ansible.utils.sentinel import Sentinel
+from ansible.utils.vars import isidentifier
 
 __all__ = ['Task']
 
@@ -276,6 +277,10 @@
         if not isinstance(value, list):
             setattr(self, name, [value])
 
+    def _validate_register(self, attr, name, value):
+        if value is not None and not isidentifier(value):
+            raise AnsibleParserError(f"Invalid variable name in 'register' 
specified: '{value}'")
+
     def post_validate(self, templar):
         '''
         Override of base class post_validate, to also do final validation on
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/playbook/task_include.py 
new/ansible_core-2.16.11/lib/ansible/playbook/task_include.py
--- old/ansible_core-2.16.10/lib/ansible/playbook/task_include.py       
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/playbook/task_include.py       
2024-09-09 21:26:18.000000000 +0200
@@ -76,7 +76,7 @@
         if not task.args.get('_raw_params'):
             task.args['_raw_params'] = task.args.pop('file', None)
             if not task.args['_raw_params']:
-                raise AnsibleParserError('No file specified for %s' % 
task.action)
+                raise AnsibleParserError('No file specified for %s' % 
task.action, obj=data)
 
         apply_attrs = task.args.get('apply', {})
         if apply_attrs and task.action not in C._ACTION_INCLUDE_TASKS:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/lib/ansible/plugins/__init__.py 
new/ansible_core-2.16.11/lib/ansible/plugins/__init__.py
--- old/ansible_core-2.16.10/lib/ansible/plugins/__init__.py    2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/plugins/__init__.py    2024-09-09 
21:26:18.000000000 +0200
@@ -52,16 +52,23 @@
 
 class AnsiblePlugin(ABC):
 
-    # allow extra passthrough parameters
-    allow_extras = False
-
     # Set by plugin loader
     _load_name: str
 
+    # allow extra passthrough parameters
+    allow_extras: bool = False
+    _extras_prefix: str | None = None
+
     def __init__(self):
         self._options = {}
         self._defs = None
 
+    @property
+    def extras_prefix(self):
+        if not self._extras_prefix:
+            self._extras_prefix = self._load_name.split('.')[-1]
+        return self._extras_prefix
+
     def matches_name(self, possible_names):
         possible_fqcns = set()
         for name in possible_names:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/plugins/connection/psrp.py 
new/ansible_core-2.16.11/lib/ansible/plugins/connection/psrp.py
--- old/ansible_core-2.16.10/lib/ansible/plugins/connection/psrp.py     
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/plugins/connection/psrp.py     
2024-09-09 21:26:18.000000000 +0200
@@ -633,39 +633,41 @@
         buffer_size = max_b64_size - (max_b64_size % 1024)
 
         # setup the file stream with read only mode
-        setup_script = '''$ErrorActionPreference = "Stop"
-$path = '%s'
+        setup_script = '''param([string]$Path)
+$ErrorActionPreference = "Stop"
 
-if (Test-Path -Path $path -PathType Leaf) {
+if (Test-Path -LiteralPath $path -PathType Leaf) {
     $fs = New-Object -TypeName System.IO.FileStream -ArgumentList @(
         $path,
         [System.IO.FileMode]::Open,
         [System.IO.FileAccess]::Read,
         [System.IO.FileShare]::Read
     )
-    $buffer_size = %d
 } elseif (Test-Path -Path $path -PathType Container) {
     Write-Output -InputObject "[DIR]"
 } else {
     Write-Error -Message "$path does not exist"
     $host.SetShouldExit(1)
-}''' % (self._shell._escape(in_path), buffer_size)
+}'''
 
         # read the file stream at the offset and return the b64 string
-        read_script = '''$ErrorActionPreference = "Stop"
-$fs.Seek(%d, [System.IO.SeekOrigin]::Begin) > $null
-$buffer = New-Object -TypeName byte[] -ArgumentList $buffer_size
-$bytes_read = $fs.Read($buffer, 0, $buffer_size)
-
-if ($bytes_read -gt 0) {
-    $bytes = $buffer[0..($bytes_read - 1)]
-    Write-Output -InputObject ([System.Convert]::ToBase64String($bytes))
+        read_script = '''param([int64]$Offset, [int]$BufferSize)
+$ErrorActionPreference = "Stop"
+$fs.Seek($Offset, [System.IO.SeekOrigin]::Begin) > $null
+$buffer = New-Object -TypeName byte[] -ArgumentList $BufferSize
+$read = $fs.Read($buffer, 0, $buffer.Length)
+
+if ($read -gt 0) {
+    [System.Convert]::ToBase64String($buffer, 0, $read)
 }'''
 
         # need to run the setup script outside of the local scope so the
         # file stream stays active between fetch operations
-        rc, stdout, stderr = self._exec_psrp_script(setup_script,
-                                                    use_local_scope=False)
+        rc, stdout, stderr = self._exec_psrp_script(
+            setup_script,
+            use_local_scope=False,
+            arguments=[in_path],
+        )
         if rc != 0:
             raise AnsibleError("failed to setup file stream for fetch '%s': %s"
                                % (out_path, to_native(stderr)))
@@ -680,7 +682,10 @@
             while True:
                 display.vvvvv("PSRP FETCH %s to %s (offset=%d" %
                               (in_path, out_path, offset), 
host=self._psrp_host)
-                rc, stdout, stderr = self._exec_psrp_script(read_script % 
offset)
+                rc, stdout, stderr = self._exec_psrp_script(
+                    read_script,
+                    arguments=[offset, buffer_size],
+                )
                 if rc != 0:
                     raise AnsibleError("failed to transfer file to '%s': %s"
                                        % (out_path, to_native(stderr)))
@@ -814,7 +819,7 @@
         script: str,
         input_data: bytes | str | t.Iterable | None = None,
         use_local_scope: bool = True,
-        arguments: t.Iterable[str] | None = None,
+        arguments: t.Iterable[t.Any] | None = None,
     ) -> tuple[int, bytes, bytes]:
         # Check if there's a command on the current pipeline that still needs 
to be closed.
         if self._last_pipeline:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/plugins/connection/ssh.py 
new/ansible_core-2.16.11/lib/ansible/plugins/connection/ssh.py
--- old/ansible_core-2.16.10/lib/ansible/plugins/connection/ssh.py      
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/plugins/connection/ssh.py      
2024-09-09 21:26:18.000000000 +0200
@@ -1286,7 +1286,7 @@
                 if sftp_action == 'get':
                     # we pass sudoable=False to disable pty allocation, which
                     # would end up mixing stdout/stderr and screwing with 
newlines
-                    (returncode, stdout, stderr) = self.exec_command('dd if=%s 
bs=%s' % (in_path, BUFSIZE), sudoable=False)
+                    (returncode, stdout, stderr) = self.exec_command('dd if=%s 
bs=%s' % (self._shell.quote(in_path), BUFSIZE), sudoable=False)
                     with open(to_bytes(out_path, 
errors='surrogate_or_strict'), 'wb+') as out_file:
                         out_file.write(stdout)
                 else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/plugins/lookup/csvfile.py 
new/ansible_core-2.16.11/lib/ansible/plugins/lookup/csvfile.py
--- old/ansible_core-2.16.10/lib/ansible/plugins/lookup/csvfile.py      
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/plugins/lookup/csvfile.py      
2024-09-09 21:26:18.000000000 +0200
@@ -13,6 +13,7 @@
       - The csvfile lookup reads the contents of a file in CSV 
(comma-separated value) format.
         The lookup looks for the row where the first column matches keyname 
(which can be multiple words)
         and returns the value in the O(col) column (default 1, which indexed 
from 0 means the second column in the file).
+      - At least one keyname is required, provided as a positional argument(s) 
to the lookup.
     options:
       col:
         description:  column to return (0 indexed).
@@ -59,6 +60,22 @@
   vars:
     csvline: "{{ lookup('ansible.builtin.csvfile', bgp_neighbor_ip, 
file='bgp_neighbors.csv', delimiter=',') }}"
   delegate_to: localhost
+
+# Contents of debug.csv
+# test1 ret1.1 ret2.1
+# test2 ret1.2 ret2.2
+# test3 ret1.3 ret2.3
+
+- name: "Lookup multiple keynames in the first column (index 0), returning the 
values from the second column (index 1)"
+  debug:
+    msg: "{{ lookup('csvfile', 'test1', 'test2', file='debug.csv', delimiter=' 
') }}"
+
+- name: Lookup multiple keynames using old style syntax
+  debug:
+    msg: "{{ lookup('csvfile', term1, term2) }}"
+  vars:
+    term1: "test1 file=debug.csv delimiter=' '"
+    term2: "test2 file=debug.csv delimiter=' '"
 """
 
 RETURN = """
@@ -146,6 +163,9 @@
         # populate options
         paramvals = self.get_options()
 
+        if not terms:
+            raise AnsibleError('Search key is required but was not found')
+
         for term in terms:
             kv = parse_kv(term)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/plugins/shell/powershell.py 
new/ansible_core-2.16.11/lib/ansible/plugins/shell/powershell.py
--- old/ansible_core-2.16.10/lib/ansible/plugins/shell/powershell.py    
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/plugins/shell/powershell.py    
2024-09-09 21:26:18.000000000 +0200
@@ -26,35 +26,70 @@
 from ansible.module_utils.common.text.converters import to_bytes, to_text
 from ansible.plugins.shell import ShellBase
 
+# This is weird, we are matching on byte sequences that match the utf-16-be
+# matches for '_x(a-fA-F0-9){4}_'. The \x00 and {8} will match the hex sequence
+# when it is encoded as utf-16-be.
+_STRING_DESERIAL_FIND = re.compile(rb"\x00_\x00x([\x00(a-fA-F0-9)]{8})\x00_")
 
 _common_args = ['PowerShell', '-NoProfile', '-NonInteractive', 
'-ExecutionPolicy', 'Unrestricted']
 
 
-def _parse_clixml(data, stream="Error"):
+def _parse_clixml(data: bytes, stream: str = "Error") -> bytes:
     """
     Takes a byte string like '#< CLIXML\r\n<Objs...' and extracts the stream
     message encoded in the XML data. CLIXML is used by PowerShell to encode
     multiple objects in stderr.
     """
-    lines = []
+    lines: list[str] = []
+
+    # A serialized string will serialize control chars and surrogate pairs as
+    # _xDDDD_ values where DDDD is the hex representation of a big endian
+    # UTF-16 code unit. As a surrogate pair uses 2 UTF-16 code units, we need
+    # to operate our text replacement on the utf-16-be byte encoding of the raw
+    # text. This allows us to replace the _xDDDD_ values with the actual byte
+    # values and then decode that back to a string from the utf-16-be bytes.
+    def rplcr(matchobj: re.Match) -> bytes:
+        match_hex = matchobj.group(1)
+        hex_string = match_hex.decode("utf-16-be")
+        return base64.b16decode(hex_string.upper())
 
     # There are some scenarios where the stderr contains a nested CLIXML 
element like
     # '<# CLIXML\r\n<# CLIXML\r\n<Objs>...</Objs><Objs>...</Objs>'.
     # Parse each individual <Objs> element and add the error strings to our 
stderr list.
     # https://github.com/ansible/ansible/issues/69550
     while data:
-        end_idx = data.find(b"</Objs>") + 7
-        current_element = data[data.find(b"<Objs "):end_idx]
+        start_idx = data.find(b"<Objs ")
+        end_idx = data.find(b"</Objs>")
+        if start_idx == -1 or end_idx == -1:
+            break
+
+        end_idx += 7
+        current_element = data[start_idx:end_idx]
         data = data[end_idx:]
 
         clixml = ET.fromstring(current_element)
         namespace_match = re.match(r'{(.*)}', clixml.tag)
-        namespace = "{%s}" % namespace_match.group(1) if namespace_match else 
""
+        namespace = f"{{{namespace_match.group(1)}}}" if namespace_match else 
""
+
+        entries = clixml.findall("./%sS" % namespace)
+        if not entries:
+            continue
+
+        # If this is a new CLIXML element, add a newline to separate the 
messages.
+        if lines:
+            lines.append("\r\n")
+
+        for string_entry in entries:
+            actual_stream = string_entry.attrib.get('S', None)
+            if actual_stream != stream:
+                continue
+
+            b_line = (string_entry.text or "").encode("utf-16-be")
+            b_escaped = re.sub(_STRING_DESERIAL_FIND, rplcr, b_line)
 
-        strings = clixml.findall("./%sS" % namespace)
-        lines.extend([e.text.replace('_x000D__x000A_', '') for e in strings if 
e.attrib.get('S') == stream])
+            lines.append(b_escaped.decode("utf-16-be", errors="surrogatepass"))
 
-    return to_bytes('\r\n'.join(lines))
+    return to_bytes(''.join(lines), errors="surrogatepass")
 
 
 class ShellModule(ShellBase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/plugins/strategy/__init__.py 
new/ansible_core-2.16.11/lib/ansible/plugins/strategy/__init__.py
--- old/ansible_core-2.16.10/lib/ansible/plugins/strategy/__init__.py   
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/plugins/strategy/__init__.py   
2024-09-09 21:26:18.000000000 +0200
@@ -56,7 +56,7 @@
 from ansible.utils.fqcn import add_internal_fqcns
 from ansible.utils.unsafe_proxy import wrap_var
 from ansible.utils.sentinel import Sentinel
-from ansible.utils.vars import combine_vars, isidentifier
+from ansible.utils.vars import combine_vars
 from ansible.vars.clean import strip_internal_keys, module_response_deepcopy
 
 display = Display()
@@ -775,10 +775,6 @@
 
             # register final results
             if original_task.register:
-
-                if not isidentifier(original_task.register):
-                    raise AnsibleError("Invalid variable name in 'register' 
specified: '%s'" % original_task.register)
-
                 host_list = self.get_task_hosts(iterator, original_host, 
original_task)
 
                 clean_copy = 
strip_internal_keys(module_response_deepcopy(task_result._result))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible/plugins/strategy/free.py 
new/ansible_core-2.16.11/lib/ansible/plugins/strategy/free.py
--- old/ansible_core-2.16.10/lib/ansible/plugins/strategy/free.py       
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/plugins/strategy/free.py       
2024-09-09 21:26:18.000000000 +0200
@@ -97,6 +97,7 @@
 
             # try and find an unblocked host with a task to run
             host_results = []
+            meta_task_dummy_results_count = 0
             while True:
                 host = hosts_left[last_host]
                 display.debug("next free host: %s" % host)
@@ -183,6 +184,9 @@
                                 continue
 
                         if task.action in C._ACTION_META:
+                            if self._host_pinned:
+                                meta_task_dummy_results_count += 1
+                                workers_free -= 1
                             self._execute_meta(task, play_context, iterator, 
target_host=host)
                             self._blocked_hosts[host_name] = False
                         else:
@@ -222,7 +226,7 @@
             host_results.extend(results)
 
             # each result is counted as a worker being free again
-            workers_free += len(results)
+            workers_free += len(results) + meta_task_dummy_results_count
 
             self.update_active_connections(results)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/lib/ansible/release.py 
new/ansible_core-2.16.11/lib/ansible/release.py
--- old/ansible_core-2.16.10/lib/ansible/release.py     2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/release.py     2024-09-09 
21:26:18.000000000 +0200
@@ -19,6 +19,6 @@
 from __future__ import (absolute_import, division, print_function)
 __metaclass__ = type
 
-__version__ = '2.16.10'
+__version__ = '2.16.11'
 __author__ = 'Ansible, Inc.'
 __codename__ = "All My Love"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/lib/ansible/utils/version.py 
new/ansible_core-2.16.11/lib/ansible/utils/version.py
--- old/ansible_core-2.16.10/lib/ansible/utils/version.py       2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible/utils/version.py       2024-09-09 
21:26:18.000000000 +0200
@@ -192,6 +192,7 @@
             raise ValueError("invalid semantic version '%s'" % vstring)
 
         (major, minor, patch, prerelease, buildmetadata) = match.group(1, 2, 
3, 4, 5)
+        self.vstring = vstring
         self.major = int(major)
         self.minor = int(minor)
         self.patch = int(patch)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible_core.egg-info/PKG-INFO 
new/ansible_core-2.16.11/lib/ansible_core.egg-info/PKG-INFO
--- old/ansible_core-2.16.10/lib/ansible_core.egg-info/PKG-INFO 2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible_core.egg-info/PKG-INFO 2024-09-09 
21:26:18.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: ansible-core
-Version: 2.16.10
+Version: 2.16.11
 Summary: Radically simple IT automation
 Home-page: https://ansible.com/
 Author: Ansible, Inc.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/lib/ansible_core.egg-info/SOURCES.txt 
new/ansible_core-2.16.11/lib/ansible_core.egg-info/SOURCES.txt
--- old/ansible_core-2.16.10/lib/ansible_core.egg-info/SOURCES.txt      
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/lib/ansible_core.egg-info/SOURCES.txt      
2024-09-09 21:26:18.000000000 +0200
@@ -1069,6 +1069,14 @@
 test/integration/targets/ansible-test-sanity-replace-urlopen/runme.sh
 
test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/do-not-check-me.py
 
test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/plugins/modules/check-me.py
+test/integration/targets/ansible-test-sanity-runtime-metadata/aliases
+test/integration/targets/ansible-test-sanity-runtime-metadata/expected-no_version.txt
+test/integration/targets/ansible-test-sanity-runtime-metadata/expected-version.txt
+test/integration/targets/ansible-test-sanity-runtime-metadata/runme.sh
+test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/galaxy.yml
+test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/meta/runtime.yml
+test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/galaxy.yml
+test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/meta/runtime.yml
 test/integration/targets/ansible-test-sanity-shebang/aliases
 test/integration/targets/ansible-test-sanity-shebang/expected.txt
 test/integration/targets/ansible-test-sanity-shebang/runme.sh
@@ -1726,6 +1734,7 @@
 test/integration/targets/copy/tasks/main.yml
 test/integration/targets/copy/tasks/no_log.yml
 test/integration/targets/copy/tasks/selinux.yml
+test/integration/targets/copy/tasks/setgid.yml
 test/integration/targets/copy/tasks/src_file_dest_file_in_non_existent_dir.yml
 
test/integration/targets/copy/tasks/src_file_dest_file_in_non_existent_dir_remote_src.yml
 test/integration/targets/copy/tasks/src_remote_file_is_not_file.yml
@@ -2191,6 +2200,7 @@
 test/integration/targets/include_import/test_loop_var_bleed.yaml
 test/integration/targets/include_import/test_nested_tasks.yml
 test/integration/targets/include_import/test_nested_tasks_fqcn.yml
+test/integration/targets/include_import/test_null_include_filename.yml
 test/integration/targets/include_import/test_role_recursion.yml
 test/integration/targets/include_import/test_role_recursion_fqcn.yml
 test/integration/targets/include_import/apply/import_apply.yml
@@ -2215,6 +2225,7 @@
 test/integration/targets/include_import/include_role_omit/playbook.yml
 
test/integration/targets/include_import/include_role_omit/roles/foo/tasks/main.yml
 test/integration/targets/include_import/nestedtasks/nested/nested.yml
+test/integration/targets/include_import/null_filename/tasks.yml
 test/integration/targets/include_import/parent_templating/playbook.yml
 
test/integration/targets/include_import/parent_templating/roles/test/tasks/localhost.yml
 
test/integration/targets/include_import/parent_templating/roles/test/tasks/main.yml
@@ -2597,6 +2608,9 @@
 test/integration/targets/lookup_first_found/files/bar1
 test/integration/targets/lookup_first_found/files/foo1
 test/integration/targets/lookup_first_found/files/vars file spaces.yml
+test/integration/targets/lookup_first_found/roles/a/tasks/main.yml
+test/integration/targets/lookup_first_found/roles/a/tasks/subdir/main.yml
+test/integration/targets/lookup_first_found/roles/a/tasks/subdir/relative
 test/integration/targets/lookup_first_found/tasks/main.yml
 test/integration/targets/lookup_first_found/vars/ishouldnotbefound.yml
 test/integration/targets/lookup_first_found/vars/itworks.yml
@@ -3419,6 +3433,11 @@
 test/integration/targets/strategy_free/last_include_tasks.yml
 test/integration/targets/strategy_free/runme.sh
 test/integration/targets/strategy_free/test_last_include_in_always.yml
+test/integration/targets/strategy_host_pinned/aliases
+test/integration/targets/strategy_host_pinned/hosts
+test/integration/targets/strategy_host_pinned/playbook.yml
+test/integration/targets/strategy_host_pinned/runme.sh
+test/integration/targets/strategy_host_pinned/callback_plugins/callback_host_count.py
 test/integration/targets/strategy_linear/aliases
 test/integration/targets/strategy_linear/inventory
 test/integration/targets/strategy_linear/runme.sh
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/packaging/release.py 
new/ansible_core-2.16.11/packaging/release.py
--- old/ansible_core-2.16.10/packaging/release.py       2024-08-12 
23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/packaging/release.py       2024-09-09 
21:26:18.000000000 +0200
@@ -369,6 +369,7 @@
 ANSIBLE_BIN_DIR = CHECKOUT_DIR / "bin"
 ANSIBLE_RELEASE_FILE = ANSIBLE_DIR / "release.py"
 ANSIBLE_REQUIREMENTS_FILE = CHECKOUT_DIR / "requirements.txt"
+ANSIBLE_PYPROJECT_TOML_FILE = CHECKOUT_DIR / "pyproject.toml"
 
 DIST_DIR = CHECKOUT_DIR / "dist"
 VENV_DIR = DIST_DIR / ".venv" / "release"
@@ -708,6 +709,35 @@
     return env
 
 
+def get_pypi_project(repository: str, project: str, version: Version | None = 
None) -> dict[str, t.Any]:
+    """Return the project JSON from PyPI for the specified repository, project 
and version (optional)."""
+    endpoint = PYPI_ENDPOINTS[repository]
+
+    if version:
+        url = f"{endpoint}/{project}/{version}/json"
+    else:
+        url = f"{endpoint}/{project}/json"
+
+    opener = urllib.request.build_opener()
+    response: http.client.HTTPResponse
+
+    try:
+        with opener.open(url) as response:
+            data = json.load(response)
+    except urllib.error.HTTPError as ex:
+        if version:
+            target = f'{project!r} version {version}'
+        else:
+            target = f'{project!r}'
+
+        if ex.status == http.HTTPStatus.NOT_FOUND:
+            raise ApplicationError(f"Could not find {target} on PyPI.") from 
None
+
+        raise RuntimeError(f"Failed to get {target} from PyPI.") from ex
+
+    return data
+
+
 def get_ansible_version(version: str | None = None, /, commit: str | None = 
None, mode: VersionMode = VersionMode.DEFAULT) -> Version:
     """Parse and return the current ansible-core version, the provided version 
or the version from the provided commit."""
     if version and commit:
@@ -802,6 +832,38 @@
     ANSIBLE_RELEASE_FILE.write_text(updated)
 
 
+def get_latest_setuptools_version() -> Version:
+    """Return the latest setuptools version found on PyPI."""
+    data = get_pypi_project('pypi', 'setuptools')
+    version = Version(data['info']['version'])
+
+    return version
+
+
+def set_setuptools_upper_bound(requested_version: Version) -> None:
+    """Set the upper bound on setuptools in pyproject.toml."""
+    current = ANSIBLE_PYPROJECT_TOML_FILE.read_text()
+    pattern = re.compile(r'^(?P<begin>requires = \["setuptools >= 
)(?P<lower>[^,]+)(?P<middle>, <= )(?P<upper>[^"]+)(?P<end>".*)$', re.MULTILINE)
+    match = pattern.search(current)
+
+    if not match:
+        raise ApplicationError(f"Unable to find the 'requires' entry in: 
{ANSIBLE_PYPROJECT_TOML_FILE.relative_to(CHECKOUT_DIR)}")
+
+    current_version = Version(match.group('upper'))
+
+    if requested_version == current_version:
+        return
+
+    display.show(f"Updating setuptools upper bound from {current_version} to 
{requested_version} ...")
+
+    updated = 
pattern.sub(fr'\g<begin>\g<lower>\g<middle>{requested_version}\g<end>', current)
+
+    if current == updated:
+        raise RuntimeError("Failed to set the setuptools upper bound.")
+
+    ANSIBLE_PYPROJECT_TOML_FILE.write_text(updated)
+
+
 def create_reproducible_sdist(original_path: pathlib.Path, output_path: 
pathlib.Path, mtime: int) -> None:
     """Read the specified sdist and write out a new copy with uniform file 
metadata at the specified location."""
     with tarfile.open(original_path) as original_archive:
@@ -878,21 +940,7 @@
 @functools.cache
 def get_release_artifact_details(repository: str, version: Version, validate: 
bool) -> list[ReleaseArtifact]:
     """Return information about the release artifacts hosted on PyPI."""
-    endpoint = PYPI_ENDPOINTS[repository]
-    url = f"{endpoint}/ansible-core/{version}/json"
-
-    opener = urllib.request.build_opener()
-    response: http.client.HTTPResponse
-
-    try:
-        with opener.open(url) as response:
-            data = json.load(response)
-    except urllib.error.HTTPError as ex:
-        if ex.status == http.HTTPStatus.NOT_FOUND:
-            raise ApplicationError(f"Version {version} not found on PyPI.") 
from None
-
-        raise RuntimeError(f"Failed to get {version} from PyPI: {ex}") from ex
-
+    data = get_pypi_project(repository, 'ansible-core', version)
     artifacts = [describe_release_artifact(version, item, validate) for item 
in data["urls"]]
 
     expected_artifact_types = {"bdist_wheel", "sdist"}
@@ -1138,6 +1186,7 @@
     mailto=dict(name="--mailto", action="store_true", help="write announcement 
to mailto link instead of console"),
     validate=dict(name="--no-validate", action="store_false", help="disable 
validation of PyPI artifacts against local ones"),
     prompt=dict(name="--no-prompt", action="store_false", help="disable 
interactive prompt before publishing with twine"),
+    setuptools=dict(name='--no-setuptools', action="store_false", 
help="disable updating setuptools upper bound"),
     allow_tag=dict(action="store_true", help="allow an existing release tag 
(for testing)"),
     allow_stale=dict(action="store_true", help="allow a stale checkout (for 
testing)"),
     allow_dirty=dict(action="store_true", help="allow untracked files and 
files with changes (for testing)"),
@@ -1194,10 +1243,11 @@
 
 # noinspection PyUnusedLocal
 @command
-def prepare(final: bool = False, pre: str | None = None, version: str | None = 
None) -> None:
+def prepare(final: bool = False, pre: str | None = None, version: str | None = 
None, setuptools: bool | None = None) -> None:
     """Prepare a release."""
     command.run(
         update_version,
+        update_setuptools,
         check_state,
         generate_summary,
         generate_changelog,
@@ -1219,6 +1269,16 @@
 
 
 @command
+def update_setuptools(setuptools: bool) -> None:
+    """Update the setuptools upper bound in pyproject.toml."""
+    if not setuptools:
+        return
+
+    requested_version = get_latest_setuptools_version()
+    set_setuptools_upper_bound(requested_version)
+
+
+@command
 def generate_summary() -> None:
     """Generate a summary changelog fragment for this release."""
     version = get_ansible_version()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.16.10/pyproject.toml 
new/ansible_core-2.16.11/pyproject.toml
--- old/ansible_core-2.16.10/pyproject.toml     2024-08-12 23:17:03.000000000 
+0200
+++ new/ansible_core-2.16.11/pyproject.toml     2024-09-09 21:26:18.000000000 
+0200
@@ -1,3 +1,3 @@
 [build-system]
-requires = ["setuptools >= 66.1.0"]  # minimum setuptools version supporting 
Python 3.12
+requires = ["setuptools >= 66.1.0, <= 72.1.0"]  # lower bound to support 
controller Python versions, upper bound for latest version tested at release
 build-backend = "setuptools.build_meta"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/aliases
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/aliases
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/aliases
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/aliases
      2024-09-09 21:26:18.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.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/galaxy.yml
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/galaxy.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/galaxy.yml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/galaxy.yml
 2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,5 @@
+namespace: ns
+name: no_version
+version: null
+authors:
+  - Ansible
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/meta/runtime.yml
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/meta/runtime.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/meta/runtime.yml
   1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/no_version/meta/runtime.yml
   2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,11 @@
+extra_key: true
+plugin_routing:
+  modules:
+    deprecated_module:
+      deprecation:
+        removal_version: 2.0.0
+        warning_text: Will no longer be there.
+    tombstoned_module:
+      tombstone:
+        removal_version: 1.0.0
+        warning_text: Is no longer there.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/galaxy.yml
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/galaxy.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/galaxy.yml
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/galaxy.yml
    2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,5 @@
+namespace: ns
+name: version
+version: 2.3.4
+authors:
+  - Ansible
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/meta/runtime.yml
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/meta/runtime.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/meta/runtime.yml
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/ansible_collections/ns/version/meta/runtime.yml
      2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,18 @@
+plugin_routing:
+  modules:
+    deprecated_module:
+      deprecation:
+        removal_version: 3.0.0
+        warning_text: Will no longer be there.
+    tombstoned_module:
+      tombstone:
+        removal_version: 2.0.0
+        warning_text: Is no longer there.
+    deprecated_module_wrong_version:
+      deprecation:
+        removal_version: 2.0.0
+        warning_text: Will no longer be there.
+    tombstoned_module_wrong_version:
+      tombstone:
+        removal_version: 3.0.0
+        warning_text: Is no longer there.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-no_version.txt
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-no_version.txt
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-no_version.txt
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-no_version.txt
      2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1 @@
+meta/runtime.yml:0:0: extra keys not allowed @ data['extra_key']. Got True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-version.txt
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-version.txt
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-version.txt
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/expected-version.txt
 2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,2 @@
+meta/runtime.yml:0:0: The deprecation removal_version ('2.0.0') must be after 
the current version (SemanticVersion('2.3.4')) for dictionary value @ 
data['plugin_routing']['modules']['deprecated_module_wrong_version']['deprecation']['removal_version'].
 Got '2.0.0'
+meta/runtime.yml:0:0: The tombstone removal_version ('3.0.0') must not be 
after the current version (SemanticVersion('2.3.4')) for dictionary value @ 
data['plugin_routing']['modules']['tombstoned_module_wrong_version']['tombstone']['removal_version'].
 Got '3.0.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/runme.sh
 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/runme.sh
--- 
old/ansible_core-2.16.10/test/integration/targets/ansible-test-sanity-runtime-metadata/runme.sh
     1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/ansible-test-sanity-runtime-metadata/runme.sh
     2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+COLLECTION_NAME=version source ../collection/setup.sh
+
+set -eux
+
+cd ../version
+ansible-test sanity --test runtime-metadata --color --truncate 0 --failure-ok 
--lint "${@}" 1> actual-stdout.txt 2> actual-stderr.txt
+diff -u "${TEST_DIR}/expected-version.txt" actual-stdout.txt
+grep -F -f "${TEST_DIR}/expected-version.txt" actual-stderr.txt
+
+cd ../no_version
+ansible-test sanity --test runtime-metadata --color --truncate 0 --failure-ok 
--lint "${@}" 1> actual-stdout.txt 2> actual-stderr.txt
+diff -u "${TEST_DIR}/expected-no_version.txt" actual-stdout.txt
+grep -F -f "${TEST_DIR}/expected-no_version.txt" actual-stderr.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/ansible-vault/runme.sh 
new/ansible_core-2.16.11/test/integration/targets/ansible-vault/runme.sh
--- old/ansible_core-2.16.10/test/integration/targets/ansible-vault/runme.sh    
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/ansible-vault/runme.sh    
2024-09-09 21:26:18.000000000 +0200
@@ -546,21 +546,22 @@
 ansible-vault encrypt_string content
 ansible-vault encrypt_string content --encrypt-vault-id id3
 
-set +e
-
 # Try to use a missing vault password file
-ansible-vault encrypt_string content --encrypt-vault-id id1 2>&1 | tee out.txt
-test $? -ne 0
-grep out.txt -e '[WARNING]: Error getting vault password file (id1)'
-grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id2 in the 
known vault-ids ['id3']"
+if ansible-vault encrypt_string content --encrypt-vault-id id1 > out.txt 2>&1; 
then
+  echo "command did not fail"
+  exit 1
+fi
+grep out.txt -e '\[WARNING\]: Error getting vault password file (id1)'
+grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id1 in the 
known vault-ids \['id3'\]"
 
 # Try to use an inaccessible vault password file
-ansible-vault encrypt_string content --encrypt-vault-id id2 2>&1 | tee out.txt
-test $? -ne 0
-grep out.txt -e "[WARNING]: Error in vault password file loading (id2)"
-grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id2 in the 
known vault-ids ['id3']"
+if ansible-vault encrypt_string content --encrypt-vault-id id2 > out.txt 2>&1; 
then
+  echo "command did not fail"
+  exit 1
+fi
+grep out.txt -e "\[WARNING\]: Error in vault password file loading (id2)"
+grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id2 in the 
known vault-ids \['id3'\]"
 
-set -e
 unset ANSIBLE_VAULT_IDENTITY_LIST
 
 # 'real script'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/collection/setup.sh 
new/ansible_core-2.16.11/test/integration/targets/collection/setup.sh
--- old/ansible_core-2.16.10/test/integration/targets/collection/setup.sh       
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/collection/setup.sh       
2024-09-09 21:26:18.000000000 +0200
@@ -24,6 +24,6 @@
 trap 'rm -rf "${WORK_DIR}"' EXIT
 
 cp -a "${TEST_DIR}/ansible_collections" "${WORK_DIR}"
-cd "${WORK_DIR}/ansible_collections/ns/col"
+cd "${WORK_DIR}/ansible_collections/ns/${COLLECTION_NAME:-col}"
 
 "${TEST_DIR}/../collection/update-ignore.py"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/connection/test_connection.yml
 
new/ansible_core-2.16.11/test/integration/targets/connection/test_connection.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/connection/test_connection.yml
    2024-08-12 23:17:03.000000000 +0200
+++ 
new/ansible_core-2.16.11/test/integration/targets/connection/test_connection.yml
    2024-09-09 21:26:18.000000000 +0200
@@ -3,6 +3,25 @@
   serial: 1
   tasks:
 
+  # SSH with scp has troubles with using complex filenames that require quoting
+  # or escaping. The more complex filename scenario is skipped in this mode.
+  # The default of sftp has no problems with these filenames.
+  - name: check if ssh with the scp file transfer is being tested
+    set_fact:
+      skip_complex_filename: >-
+        {{
+            ansible_connection == "ssh" and
+            lookup("ansible.builtin.config",
+                "ssh_transfer_method",
+                plugin_name=ansible_connection,
+                plugin_type="connection",
+            ) == "scp"
+        }}
+
+  - name: set test filename
+    set_fact:
+      test_filename: 汉语-{{ skip_complex_filename | ternary("file", "['foo 
bar']") }}.txt
+
   ### raw with unicode arg and output
 
   - name: raw with unicode arg and output
@@ -17,20 +36,20 @@
   ### copy local file with unicode filename and content
 
   - name: create local file with unicode filename and content
-    local_action: lineinfile dest={{ local_tmp }}-汉语/汉语.txt 
create=true line=汉语
+    local_action: lineinfile dest={{ local_tmp }}-汉语/{{ test_filename }} 
create=true line=汉语
   - name: remove remote file with unicode filename and content
-    action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语/汉语.txt 
state=absent"
+    action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语/{{ 
test_filename }} state=absent"
   - name: create remote directory with unicode name
     action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语 
state=directory"
   - name: copy local file with unicode filename and content
-    action: "{{ action_prefix }}copy src={{ local_tmp }}-汉语/汉语.txt 
dest={{ remote_tmp }}-汉语/汉语.txt"
+    action: "{{ action_prefix }}copy src={{ local_tmp }}-汉语/{{ 
test_filename }} dest={{ remote_tmp }}-汉语/{{ test_filename }}"
 
   ### fetch remote file with unicode filename and content
 
   - name: remove local file with unicode filename and content
-    local_action: file path={{ local_tmp }}-汉语/汉语.txt state=absent
+    local_action: file path={{ local_tmp }}-汉语/{{ test_filename }} 
state=absent
   - name: fetch remote file with unicode filename and content
-    fetch: src={{ remote_tmp }}-汉语/汉语.txt dest={{ local_tmp 
}}-汉语/汉语.txt fail_on_missing=true validate_checksum=true flat=true
+    fetch: src={{ remote_tmp }}-汉语/{{ test_filename }} dest={{ local_tmp 
}}-汉语/{{ test_filename }} fail_on_missing=true validate_checksum=true 
flat=true
 
   ### remove local and remote temp files
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/connection_psrp/tests.yml 
new/ansible_core-2.16.11/test/integration/targets/connection_psrp/tests.yml
--- old/ansible_core-2.16.10/test/integration/targets/connection_psrp/tests.yml 
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/connection_psrp/tests.yml 
2024-09-09 21:26:18.000000000 +0200
@@ -126,3 +126,16 @@
         path: /tmp/empty.txt
         state: absent
       delegate_to: localhost
+
+  - name: Test PSRP HTTP connection
+    win_ping:
+    vars:
+      ansible_port: 5985
+      ansible_psrp_protocol: http
+
+  - name: Test PSRP HTTPS connection
+    win_ping:
+    vars:
+      ansible_port: 5986
+      ansible_psrp_protocol: https
+      ansible_psrp_cert_validation: ignore
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/connection_winrm/tests.yml 
new/ansible_core-2.16.11/test/integration/targets/connection_winrm/tests.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/connection_winrm/tests.yml    
    2024-08-12 23:17:03.000000000 +0200
+++ 
new/ansible_core-2.16.11/test/integration/targets/connection_winrm/tests.yml    
    2024-09-09 21:26:18.000000000 +0200
@@ -41,3 +41,26 @@
   - assert:
       that:
       - timeout_cmd.msg == 'The win_shell action failed to execute in the 
expected time frame (5) and was terminated'
+
+  - name: Test WinRM HTTP connection
+    win_ping:
+    vars:
+      ansible_port: 5985
+      ansible_winrm_scheme: http
+      ansible_winrm_transport: ntlm  # Verifies message encryption over HTTP
+
+  - name: Test WinRM HTTPS connection
+    win_ping:
+    vars:
+      ansible_port: 5986
+      ansible_winrm_scheme: https
+      ansible_winrm_server_cert_validation: ignore
+
+  - name: emit raw CLIXML on stderr with special chars
+    raw: $host.UI.WriteErrorLine("Test 🎵 _x005F_ _x005Z_.")
+    register: stderr_clixml
+
+  - name: assert emit raw CLIXML on stderr with special chars
+    assert:
+      that:
+      - stderr_clixml.stderr_lines == ['Test 🎵 _x005F_ _x005Z_.']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/copy/defaults/main.yml 
new/ansible_core-2.16.11/test/integration/targets/copy/defaults/main.yml
--- old/ansible_core-2.16.10/test/integration/targets/copy/defaults/main.yml    
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/copy/defaults/main.yml    
2024-09-09 21:26:18.000000000 +0200
@@ -1,2 +1,3 @@
 ---
 remote_unprivileged_user: tmp_ansible_test_user
+remote_unprivileged_user_group: test_ansible_test_group
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/copy/tasks/main.yml 
new/ansible_core-2.16.11/test/integration/targets/copy/tasks/main.yml
--- old/ansible_core-2.16.10/test/integration/targets/copy/tasks/main.yml       
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/copy/tasks/main.yml       
2024-09-09 21:26:18.000000000 +0200
@@ -29,9 +29,15 @@
       with_dict: "{{ symlinks }}"
       delegate_to: localhost
 
+    - name: Create group for remote unprivileged user
+      group:
+        name: '{{ remote_unprivileged_user_group }}'
+      register: group
+
     - name: Create remote unprivileged remote user
       user:
         name: '{{ remote_unprivileged_user }}'
+        group: '{{ remote_unprivileged_user_group }}'
       register: user
 
     - name: Check sudoers dir
@@ -78,6 +84,8 @@
     - import_tasks: selinux.yml
       when: ansible_os_family == 'RedHat' and ansible_selinux.get('mode') == 
'enforcing'
 
+    - import_tasks: setgid.yml
+
     - import_tasks: no_log.yml
       delegate_to: localhost
 
@@ -122,6 +130,11 @@
         remove: yes
         force: yes
 
+    - name: Remove group for remote unprivileged user
+      group:
+        name: '{{ remote_unprivileged_user_group }}'
+        state: absent
+
     - name: Remove sudoers.d file
       file:
         path: "{{ sudoers_d_file }}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/copy/tasks/setgid.yml 
new/ansible_core-2.16.11/test/integration/targets/copy/tasks/setgid.yml
--- old/ansible_core-2.16.10/test/integration/targets/copy/tasks/setgid.yml     
1970-01-01 01:00:00.000000000 +0100
+++ new/ansible_core-2.16.11/test/integration/targets/copy/tasks/setgid.yml     
2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,27 @@
+- block:
+    - name: Create test directory
+      file:
+        path: "{{ remote_tmp_dir }}/test_setgid"
+        state: directory
+        mode: '2750'
+        recurse: yes
+        owner: '{{ remote_unprivileged_user }}'
+        group: '{{ remote_unprivileged_user_group }}'
+
+    - name: Test creating a file respects setgid on parent dir
+      copy:
+        content: |
+          test file
+        dest: "{{ remote_tmp_dir }}/test_setgid/test.txt"
+
+    - stat:
+        path: "{{ remote_tmp_dir }}/test_setgid/test.txt"
+      register: result
+
+    - assert:
+        that:
+          - result.stat.gr_name == remote_unprivileged_user_group
+  always:
+    - file:
+        path: "{{ remote_tmp_dir }}/test_setgid"
+        state: absent
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/include_import/null_filename/tasks.yml
 
new/ansible_core-2.16.11/test/integration/targets/include_import/null_filename/tasks.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/include_import/null_filename/tasks.yml
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/include_import/null_filename/tasks.yml
    2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,5 @@
+- name: ping task
+  ansible.builtin.ping:
+
+- name: invalid include_task definition
+  ansible.builtin.include_tasks:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/include_import/runme.sh 
new/ansible_core-2.16.11/test/integration/targets/include_import/runme.sh
--- old/ansible_core-2.16.10/test/integration/targets/include_import/runme.sh   
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/include_import/runme.sh   
2024-09-09 21:26:18.000000000 +0200
@@ -148,3 +148,9 @@
 # https://github.com/ansible/ansible/issues/73657
 ansible-playbook issue73657.yml 2>&1 | tee issue73657.out
 test "$(grep -c 'SHOULD_NOT_EXECUTE' issue73657.out)" = 0
+
+# https://github.com/ansible/ansible/issues/83874
+ansible-playbook test_null_include_filename.yml 2>&1 | tee 
test_null_include_filename.out
+test "$(grep -c 'ERROR! No file specified for ansible.builtin.include_tasks' 
test_null_include_filename.out)" = 1
+test "$(grep -c 'The error appears to be in 
'\''.*/include_import/null_filename/tasks.yml'\'': line 4, column 3.*' 
test_null_include_filename.out)" = 1
+test "$(grep -c '\- name: invalid include_task definition' 
test_null_include_filename.out)" = 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/include_import/test_null_include_filename.yml
 
new/ansible_core-2.16.11/test/integration/targets/include_import/test_null_include_filename.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/include_import/test_null_include_filename.yml
     1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/include_import/test_null_include_filename.yml
     2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,7 @@
+- name: Test include failure with invalid included include_task
+  hosts: localhost
+  gather_facts: false
+
+  tasks:
+  - ansible.builtin.include_tasks:
+      file: null_filename/tasks.yml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/lookup_csvfile/tasks/main.yml 
new/ansible_core-2.16.11/test/integration/targets/lookup_csvfile/tasks/main.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/lookup_csvfile/tasks/main.yml 
    2024-08-12 23:17:03.000000000 +0200
+++ 
new/ansible_core-2.16.11/test/integration/targets/lookup_csvfile/tasks/main.yml 
    2024-09-09 21:26:18.000000000 +0200
@@ -4,6 +4,12 @@
   ignore_errors: yes
   register: no_keyword
 
+- name: using modern syntax but missing keyword
+  set_fact:
+    this_will_error: "{{ lookup('csvfile', file='people.csv', delimiter=' ', 
col=1) }}"
+  ignore_errors: yes
+  register: modern_no_keyword
+
 - name: extra arg in k=v syntax (deprecated)
   set_fact:
     this_will_error: "{{ lookup('csvfile', 'foo file=people.csv delimiter=, 
col=1 thisarg=doesnotexist') }}"
@@ -27,6 +33,9 @@
       - no_keyword is failed
       - >
         "Search key is required but was not found" in no_keyword.msg
+      - modern_no_keyword is failed
+      - >
+        "Search key is required but was not found" in modern_no_keyword.msg
       - invalid_arg is failed
       - invalid_arg2 is failed
       - >
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/lookup_first_found/roles/a/tasks/main.yml
 
new/ansible_core-2.16.11/test/integration/targets/lookup_first_found/roles/a/tasks/main.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/lookup_first_found/roles/a/tasks/main.yml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/lookup_first_found/roles/a/tasks/main.yml
 2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1 @@
+- include_tasks: subdir/main.yml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/lookup_first_found/roles/a/tasks/subdir/main.yml
 
new/ansible_core-2.16.11/test/integration/targets/lookup_first_found/roles/a/tasks/subdir/main.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/lookup_first_found/roles/a/tasks/subdir/main.yml
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/lookup_first_found/roles/a/tasks/subdir/main.yml
  2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,7 @@
+- assert:
+    that:
+      - "lookup('first_found', task_adjacent) is is_file"
+  vars:
+    task_adjacent:
+      - files:
+          - relative
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/lookup_first_found/tasks/main.yml
 
new/ansible_core-2.16.11/test/integration/targets/lookup_first_found/tasks/main.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/lookup_first_found/tasks/main.yml
 2024-08-12 23:17:03.000000000 +0200
+++ 
new/ansible_core-2.16.11/test/integration/targets/lookup_first_found/tasks/main.yml
 2024-09-09 21:26:18.000000000 +0200
@@ -147,3 +147,7 @@
           - ishouldnotbefound.yml
         paths:
           - "{{role_path}}/vars"
+
+- name: Test relative paths in roles
+  include_role:
+    role: "{{ role_path }}/roles/a"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/loops/tasks/main.yml 
new/ansible_core-2.16.11/test/integration/targets/loops/tasks/main.yml
--- old/ansible_core-2.16.10/test/integration/targets/loops/tasks/main.yml      
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/loops/tasks/main.yml      
2024-09-09 21:26:18.000000000 +0200
@@ -405,3 +405,20 @@
 - assert:
     that:
       - foo[0] == 'foo1.0'
+
+# https://github.com/ansible/ansible/issues/83619
+- command: "echo {{ item }}"
+  register: r
+  loop:
+    - changed
+    - skipped
+  loop_control:
+    label: "{{ r.stdout }}"
+  when:
+    - item == "changed"
+  ignore_errors: true
+
+- name: test that the second iteration result was stored, since it was skipped 
no stdout should be present there
+  assert:
+    that:
+      - "r['results'][1]['msg'] is contains(\"no attribute 'stdout'\")"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/aliases 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/aliases
--- 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/aliases  
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/aliases  
    2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1 @@
+shippable/posix/group5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/callback_plugins/callback_host_count.py
 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/callback_plugins/callback_host_count.py
--- 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/callback_plugins/callback_host_count.py
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/callback_plugins/callback_host_count.py
      2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,43 @@
+# (c) 2024 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or 
https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import annotations
+
+from ansible.plugins.callback import CallbackBase
+
+
+class CallbackModule(CallbackBase):
+    CALLBACK_VERSION = 2.0
+    CALLBACK_TYPE = 'stdout'
+    CALLBACK_NAME = 'callback_host_count'
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self._executing_hosts_counter = 0
+
+    def v2_playbook_on_task_start(self, task, is_conditional):
+        self._display.display(task.name or task.action)
+
+        if task.name == "start":
+            self._executing_hosts_counter += 1
+
+        # NOTE assumes 2 forks
+        num_forks = 2
+        if self._executing_hosts_counter > num_forks:
+            # Exception is caught and turned into just a warning in TQM,
+            # so raise BaseException to fail the test
+            # To prevent seeing false positives in case the exception handling
+            # in TQM is changed and BaseException is swallowed, print something
+            # and ensure the test fails in runme.sh in such a case.
+            self._display.display("host_pinned_test_failed")
+            raise BaseException(
+                "host_pinned test failed, number of hosts executing: "
+                f"{self._executing_hosts_counter}, expected: {num_forks}"
+            )
+
+    def v2_playbook_on_handler_task_start(self, task):
+        self._display.display(task.name or task.action)
+
+    def v2_runner_on_ok(self, result):
+        if result._task.name == "end":
+            self._executing_hosts_counter -= 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/hosts 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/hosts
--- 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/hosts    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/hosts    
    2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,14 @@
+localhost0
+localhost1
+localhost2
+localhost3
+localhost4
+localhost5
+localhost6
+localhost7
+localhost8
+localhost9
+
+[all:vars]
+ansible_connection=local
+ansible_python_interpreter={{ansible_playbook_python}}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/playbook.yml
 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/playbook.yml
--- 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/playbook.yml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/playbook.yml
 2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,46 @@
+# README - even the name of the tasks matter in this test, see 
callback_plugins/callback_host_count.py
+- hosts: all
+  gather_facts: false
+  strategy: host_pinned
+  pre_tasks:
+    # by executing in pre_tasks we ensure that "start" is the first task in 
the play,
+    # not an implicit "meta: flush_handlers" after pre_tasks
+    - name: start
+      debug:
+        msg: start
+
+    - ping:
+
+    - meta: noop
+  post_tasks:
+    # notifying a handler in post_tasks ensures the handler is the last task 
in the play,
+    # not an implicit "meta: flush_handlers" after post_tasks
+    - debug:
+      changed_when: true
+      notify: end
+  handlers:
+    - name: end
+      debug:
+        msg: end
+
+- hosts: localhost0,localhost1,localhost2
+  gather_facts: false
+  strategy: host_pinned
+  pre_tasks:
+    - name: start
+      debug:
+        msg: start
+
+    - command: sleep 3
+      when: inventory_hostname == "localhost0"
+
+    - meta: noop
+    - meta: noop
+  post_tasks:
+    - debug:
+      changed_when: true
+      notify: end
+  handlers:
+    - name: end
+      debug:
+        msg: end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/runme.sh 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/runme.sh
--- 
old/ansible_core-2.16.10/test/integration/targets/strategy_host_pinned/runme.sh 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.16.11/test/integration/targets/strategy_host_pinned/runme.sh 
    2024-09-09 21:26:18.000000000 +0200
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -o pipefail
+
+export ANSIBLE_STDOUT_CALLBACK=callback_host_count
+
+# the number of forks matter, see callback_plugins/callback_host_count.py
+ansible-playbook --inventory hosts --forks 2 playbook.yml | tee 
"${OUTPUT_DIR}/out.txt"
+
+[ "$(grep -c 'host_pinned_test_failed' "${OUTPUT_DIR}/out.txt")" -eq 0 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/integration/targets/win_fetch/aliases 
new/ansible_core-2.16.11/test/integration/targets/win_fetch/aliases
--- old/ansible_core-2.16.10/test/integration/targets/win_fetch/aliases 
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/integration/targets/win_fetch/aliases 
2024-09-09 21:26:18.000000000 +0200
@@ -1 +1,2 @@
 shippable/windows/group1
+shippable/windows/smoketest
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/lib/ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py
 
new/ansible_core-2.16.11/test/lib/ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py
--- 
old/ansible_core-2.16.10/test/lib/ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py
       2024-08-12 23:17:03.000000000 +0200
+++ 
new/ansible_core-2.16.11/test/lib/ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py
       2024-09-09 21:26:18.000000000 +0200
@@ -123,7 +123,9 @@
     # noinspection PyBroadException
     try:
         result = collection_detail.read_manifest_json('.') or 
collection_detail.read_galaxy_yml('.')
-        return SemanticVersion(result['version'])
+        version = SemanticVersion()
+        version.parse(result['version'])
+        return version
     except Exception:  # pylint: disable=broad-except
         # We do not care why it fails, in case we cannot get the version
         # just return None to indicate "we don't know".
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/sanity/code-smell/package-data.py 
new/ansible_core-2.16.11/test/sanity/code-smell/package-data.py
--- old/ansible_core-2.16.10/test/sanity/code-smell/package-data.py     
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/sanity/code-smell/package-data.py     
2024-09-09 21:26:18.000000000 +0200
@@ -95,7 +95,7 @@
 def build(source_dir: str, tmp_dir: str) -> tuple[pathlib.Path, pathlib.Path]:
     """Create a sdist and wheel."""
     create = subprocess.run(
-        [sys.executable, '-m', 'build', '--no-isolation', '--outdir', tmp_dir],
+        [sys.executable, '-m', 'build', '--outdir', tmp_dir],
         stdin=subprocess.DEVNULL,
         capture_output=True,
         text=True,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/units/plugins/connection/test_psrp.py 
new/ansible_core-2.16.11/test/units/plugins/connection/test_psrp.py
--- old/ansible_core-2.16.10/test/units/plugins/connection/test_psrp.py 
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/units/plugins/connection/test_psrp.py 
2024-09-09 21:26:18.000000000 +0200
@@ -222,12 +222,13 @@
         pc = PlayContext()
         new_stdin = StringIO()
 
-        conn = connection_loader.get('psrp', pc, new_stdin)
-        conn.set_options(var_options={'_extras': {'ansible_psrp_mock_test3': 
True}})
+        for conn_name in ('psrp', 'ansible.legacy.psrp'):
+            conn = connection_loader.get(conn_name, pc, new_stdin)
+            conn.set_options(var_options={'_extras': 
{'ansible_psrp_mock_test3': True}})
 
-        mock_display = MagicMock()
-        monkeypatch.setattr(Display, "warning", mock_display)
-        conn._build_kwargs()
+            mock_display = MagicMock()
+            monkeypatch.setattr(Display, "warning", mock_display)
+            conn._build_kwargs()
 
-        assert mock_display.call_args[0][0] == \
-            'ansible_psrp_mock_test3 is unsupported by the current psrp 
version installed'
+            assert mock_display.call_args[0][0] == \
+                'ansible_psrp_mock_test3 is unsupported by the current psrp 
version installed'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.16.10/test/units/plugins/shell/test_powershell.py 
new/ansible_core-2.16.11/test/units/plugins/shell/test_powershell.py
--- old/ansible_core-2.16.10/test/units/plugins/shell/test_powershell.py        
2024-08-12 23:17:03.000000000 +0200
+++ new/ansible_core-2.16.11/test/units/plugins/shell/test_powershell.py        
2024-09-09 21:26:18.000000000 +0200
@@ -1,6 +1,8 @@
 from __future__ import (absolute_import, division, print_function)
 __metaclass__ = type
 
+import pytest
+
 from ansible.plugins.shell.powershell import _parse_clixml, ShellModule
 
 
@@ -28,7 +30,8 @@
                     b'<S S="Error">At line:1 char:1_x000D__x000A_</S>' \
                     b'<S S="Error">+ fake cmdlet_x000D__x000A_</S><S 
S="Error">+ ~~~~_x000D__x000A_</S>' \
                     b'<S S="Error">    + CategoryInfo          : 
ObjectNotFound: (fake:String) [], CommandNotFoundException_x000D__x000A_</S>' \
-                    b'<S S="Error">    + FullyQualifiedErrorId : 
CommandNotFoundException_x000D__x000A_</S><S S="Error"> _x000D__x000A_</S>' \
+                    b'<S S="Error">    + FullyQualifiedErrorId : 
CommandNotFoundException_x000D__x000A_</S>' \
+                    b'<S S="Error"> _x000D__x000A_</S>' \
                     b'</Objs>'
     expected = b"fake : The term 'fake' is not recognized as the name of a 
cmdlet. Check \r\n" \
                b"the spelling of the name, or if a path was included.\r\n" \
@@ -36,7 +39,8 @@
                b"+ fake cmdlet\r\n" \
                b"+ ~~~~\r\n" \
                b"    + CategoryInfo          : ObjectNotFound: (fake:String) 
[], CommandNotFoundException\r\n" \
-               b"    + FullyQualifiedErrorId : CommandNotFoundException\r\n "
+               b"    + FullyQualifiedErrorId : CommandNotFoundException\r\n" \
+               b" \r\n"
     actual = _parse_clixml(single_stream)
     assert actual == expected
 
@@ -50,8 +54,9 @@
                       b'<S S="Error">    + CategoryInfo          : 
ObjectNotFound: (fake:String) [], CommandNotFoundException_x000D__x000A_</S>' \
                       b'<S S="Error">    + FullyQualifiedErrorId : 
CommandNotFoundException_x000D__x000A_</S><S S="Error"> _x000D__x000A_</S>' \
                       b'<S S="Info">hi info</S>' \
+                      b'<S S="Info">other</S>' \
                       b'</Objs>'
-    expected = b"hi info"
+    expected = b"hi infoother"
     actual = _parse_clixml(multiple_stream, stream="Info")
     assert actual == expected
 
@@ -75,6 +80,32 @@
     assert actual == expected
 
 
+@pytest.mark.parametrize('clixml, expected', [
+    ('', ''),
+    ('just newline _x000A_', 'just newline \n'),
+    ('surrogate pair _xD83C__xDFB5_', 'surrogate pair 🎵'),
+    ('null char _x0000_', 'null char \0'),
+    ('normal char _x0061_', 'normal char a'),
+    ('escaped literal _x005F_x005F_', 'escaped literal _x005F_'),
+    ('underscope before escape _x005F__x000A_', 'underscope before escape 
_\n'),
+    ('surrogate high _xD83C_', 'surrogate high \uD83C'),
+    ('surrogate low _xDFB5_', 'surrogate low \uDFB5'),
+    ('lower case hex _x005f_', 'lower case hex _'),
+    ('invalid hex _x005G_', 'invalid hex _x005G_'),
+])
+def test_parse_clixml_with_comlex_escaped_chars(clixml, expected):
+    clixml_data = (
+        '<# CLIXML\r\n'
+        '<Objs Version="1.1.0.1" 
xmlns="http://schemas.microsoft.com/powershell/2004/04";>'
+        f'<S S="Error">{clixml}</S>'
+        '</Objs>'
+    ).encode()
+    b_expected = expected.encode(errors="surrogatepass")
+
+    actual = _parse_clixml(clixml_data)
+    assert actual == b_expected
+
+
 def test_join_path_unc():
     pwsh = ShellModule()
     unc_path_parts = ['\\\\host\\share\\dir1\\\\dir2\\', '\\dir3/dir4', 
'dir5', 'dir6\\']

++++++ ansible_core-2.16.10.tar.gz.sha256 -> ansible_core-2.16.11.tar.gz.sha256 
++++++
--- 
/work/SRC/openSUSE:Factory/ansible-core-2.16/ansible_core-2.16.10.tar.gz.sha256 
    2024-08-15 09:58:41.083866672 +0200
+++ 
/work/SRC/openSUSE:Factory/.ansible-core-2.16.new.29891/ansible_core-2.16.11.tar.gz.sha256
  2024-09-16 17:41:04.812105628 +0200
@@ -1 +1 @@
-a8b38790a67afb02f49e94f1fcdaafcf474c9567959233bcd711986acc7d4884  
ansible_core-2.16.10.tar.gz
+573829ccf4ca925816bf6b64860c86a986365b8b5d8861de82e319b63287e553  
ansible_core-2.16.11.tar.gz

Reply via email to