Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ansible-core-2.19 for 
openSUSE:Factory checked in at 2026-04-21 12:45:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ansible-core-2.19 (Old)
 and      /work/SRC/openSUSE:Factory/.ansible-core-2.19.new.11940 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ansible-core-2.19"

Tue Apr 21 12:45:49 2026 rev:6 rq:1348366 version:2.19.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/ansible-core-2.19/ansible-core-2.19.changes      
2026-03-27 06:36:06.684852122 +0100
+++ 
/work/SRC/openSUSE:Factory/.ansible-core-2.19.new.11940/ansible-core-2.19.changes
   2026-04-21 12:47:31.489154411 +0200
@@ -1,0 +2,16 @@
+Tue Apr 21 06:09:25 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- update to 2.19.9:
+  
https://github.com/ansible/ansible/blob/v2.19.9/changelogs/CHANGELOG-v2.19.rst
+  * Minor Changes
+    - ansible-test - Generate dist_info when running tests.
+    - ansible-test - Replace the parallels managed macOS provider
+      with a new mac provider.
+    - ansible-test - Switch managed macOS remotes from x86_64 to
+      aarch64.
+  * Bugfixes
+    - ansible-galaxy collection - Fix using the server
+      configuration for validate_certs when downloading
+      collections. (#86694)
+
+-------------------------------------------------------------------
@@ -4,0 +21 @@
+  
https://github.com/ansible/ansible/blob/v2.19.8/changelogs/CHANGELOG-v2.19.rst

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

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

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

Other differences:
------------------
++++++ ansible-core-2.19.spec ++++++
--- /var/tmp/diff_new_pack.tXrl5c/_old  2026-04-21 12:47:32.361190584 +0200
+++ /var/tmp/diff_new_pack.tXrl5c/_new  2026-04-21 12:47:32.365190750 +0200
@@ -43,7 +43,7 @@
 %endif
 
 Name:           ansible-core-2.19
-Version:        2.19.8
+Version:        2.19.9
 Release:        0
 Summary:        Radically simple IT automation
 License:        GPL-3.0-or-later

++++++ ansible_core-2.19.8.tar.gz -> ansible_core-2.19.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.19.8/PKG-INFO 
new/ansible_core-2.19.9/PKG-INFO
--- old/ansible_core-2.19.8/PKG-INFO    2026-03-23 18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/PKG-INFO    2026-04-21 02:40:29.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: ansible-core
-Version: 2.19.8
+Version: 2.19.9
 Summary: Radically simple IT automation
 Author: Ansible Project
 Project-URL: Homepage, https://ansible.com/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.19.8/ansible_core.egg-info/PKG-INFO 
new/ansible_core-2.19.9/ansible_core.egg-info/PKG-INFO
--- old/ansible_core-2.19.8/ansible_core.egg-info/PKG-INFO      2026-03-23 
18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/ansible_core.egg-info/PKG-INFO      2026-04-21 
02:40:29.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: ansible-core
-Version: 2.19.8
+Version: 2.19.9
 Summary: Radically simple IT automation
 Author: Ansible Project
 Project-URL: Homepage, https://ansible.com/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/ansible_core.egg-info/SOURCES.txt 
new/ansible_core-2.19.9/ansible_core.egg-info/SOURCES.txt
--- old/ansible_core-2.19.8/ansible_core.egg-info/SOURCES.txt   2026-03-23 
18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/ansible_core.egg-info/SOURCES.txt   2026-04-21 
02:40:29.000000000 +0200
@@ -562,6 +562,7 @@
 lib/ansible/plugins/callback/tree.py
 lib/ansible/plugins/cliconf/__init__.py
 lib/ansible/plugins/connection/__init__.py
+lib/ansible/plugins/connection/_paramiko_ssh.py
 lib/ansible/plugins/connection/local.py
 lib/ansible/plugins/connection/paramiko_ssh.py
 lib/ansible/plugins/connection/psrp.py
@@ -1167,6 +1168,8 @@
 
test/integration/targets/ansible-test-integration/ansible_collections/ns/col/plugins/modules/hello.py
 
test/integration/targets/ansible-test-integration/ansible_collections/ns/col/tests/integration/targets/hello/aliases
 
test/integration/targets/ansible-test-integration/ansible_collections/ns/col/tests/integration/targets/hello/tasks/main.yml
+test/integration/targets/ansible-test-metadata/aliases
+test/integration/targets/ansible-test-metadata/runme.sh
 test/integration/targets/ansible-test-no-tty/aliases
 test/integration/targets/ansible-test-no-tty/runme.sh
 
test/integration/targets/ansible-test-no-tty/ansible_collections/ns/col/run-with-pty.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.19.8/changelogs/CHANGELOG-v2.19.rst 
new/ansible_core-2.19.9/changelogs/CHANGELOG-v2.19.rst
--- old/ansible_core-2.19.8/changelogs/CHANGELOG-v2.19.rst      2026-03-23 
18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/changelogs/CHANGELOG-v2.19.rst      2026-04-21 
02:40:29.000000000 +0200
@@ -4,6 +4,27 @@
 
 .. contents:: Topics
 
+v2.19.9
+=======
+
+Release Summary
+---------------
+
+| Release Date: 2026-04-20
+| `Porting Guide 
<https://docs.ansible.com/ansible-core/2.19/porting_guides/porting_guide_core_2.19.html>`__
+
+Minor Changes
+-------------
+
+- ansible-test - Generate ``dist_info`` when running tests.
+- ansible-test - Replace the ``parallels`` managed macOS provider with a new 
``mac`` provider.
+- ansible-test - Switch managed macOS remotes from x86_64 to aarch64.
+
+Bugfixes
+--------
+
+- ansible-galaxy collection - Fix using the server configuration for 
``validate_certs`` when downloading collections. 
(https://github.com/ansible/ansible/issues/86694)
+
 v2.19.8
 =======
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.19.8/changelogs/changelog.yaml 
new/ansible_core-2.19.9/changelogs/changelog.yaml
--- old/ansible_core-2.19.8/changelogs/changelog.yaml   2026-03-23 
18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/changelogs/changelog.yaml   2026-04-21 
02:40:29.000000000 +0200
@@ -1559,3 +1559,36 @@
     - configlookupfix.yml
     - pyyaml-name.yml
     release_date: '2026-03-16'
+  2.19.9:
+    changes:
+      release_summary: '| Release Date: 2026-04-20
+
+        | `Porting Guide 
<https://docs.ansible.com/ansible-core/2.19/porting_guides/porting_guide_core_2.19.html>`__
+
+        '
+    codename: What Is and What Should Never Be
+    fragments:
+    - 2.19.9_summary.yaml
+    release_date: '2026-04-20'
+  2.19.9rc1:
+    changes:
+      bugfixes:
+      - ansible-galaxy collection - Fix using the server configuration for 
``validate_certs``
+        when downloading collections. 
(https://github.com/ansible/ansible/issues/86694)
+      minor_changes:
+      - ansible-test - Generate ``dist_info`` when running tests.
+      - ansible-test - Replace the ``parallels`` managed macOS provider with a 
new
+        ``mac`` provider.
+      - ansible-test - Switch managed macOS remotes from x86_64 to aarch64.
+      release_summary: '| Release Date: 2026-04-13
+
+        | `Porting Guide 
<https://docs.ansible.com/ansible-core/2.19/porting_guides/porting_guide_core_2.19.html>`__
+
+        '
+    codename: What Is and What Should Never Be
+    fragments:
+    - 2.19.9rc1_summary.yaml
+    - ansible-galaxy-validate_certs-server-config.yml
+    - ansible-test-dist-info.yml
+    - ansible-test-macos-aarch64.yml
+    release_date: '2026-04-13'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.19.8/lib/ansible/cli/galaxy.py 
new/ansible_core-2.19.9/lib/ansible/cli/galaxy.py
--- old/ansible_core-2.19.8/lib/ansible/cli/galaxy.py   2026-03-23 
18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/lib/ansible/cli/galaxy.py   2026-04-21 
02:40:29.000000000 +0200
@@ -80,8 +80,10 @@
         if 'artifacts_manager' in kwargs:
             return wrapped_method(*args, **kwargs)
 
-        # FIXME: use validate_certs context from Galaxy servers when 
downloading collections
-        # .get used here for when this is used in a non-CLI context
+        # configures validate_certs for type 'url' only
+        # type 'galaxy' inherits and overrides resolved_validate_certs
+        # type 'git' recalculates resolved_validate_certs
+        # NOTE: .get used here for when this is used in a non-CLI context
         artifacts_manager_kwargs = {'validate_certs': 
context.CLIARGS.get('resolved_validate_certs', True)}
 
         keyring = context.CLIARGS.get('keyring', None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/lib/ansible/galaxy/collection/concrete_artifact_manager.py
 
new/ansible_core-2.19.9/lib/ansible/galaxy/collection/concrete_artifact_manager.py
--- 
old/ansible_core-2.19.8/lib/ansible/galaxy/collection/concrete_artifact_manager.py
  2026-03-23 18:38:39.000000000 +0100
+++ 
new/ansible_core-2.19.9/lib/ansible/galaxy/collection/concrete_artifact_manager.py
  2026-04-21 02:40:29.000000000 +0200
@@ -28,7 +28,7 @@
 from ansible import context
 from ansible.errors import AnsibleError
 from ansible.galaxy import get_collections_galaxy_meta_info
-from ansible.galaxy.api import should_retry_error
+from ansible.galaxy.api import should_retry_error, CollectionVersionMetadata, 
GalaxyAPI
 from ansible.galaxy.dependency_resolution.dataclasses import _GALAXY_YAML
 from ansible.galaxy.user_agent import user_agent
 from ansible.module_utils.common.text.converters import to_bytes, to_native, 
to_text
@@ -68,8 +68,7 @@
         self._artifact_cache = {}  # type: dict[bytes, bytes]
         self._galaxy_artifact_cache = {}  # type: dict[Candidate | 
Requirement, bytes]
         self._artifact_meta_cache = {}  # type: dict[bytes, dict[str, str | 
list[str] | dict[str, str] | None | t.Type[Sentinel]]]
-        self._galaxy_collection_cache = {}  # type: dict[Candidate | 
Requirement, tuple[str, str, GalaxyToken]]
-        self._galaxy_collection_origin_cache = {}  # type: dict[Candidate, 
tuple[str, list[dict[str, str]]]]
+        self._galaxy_collection_cache: dict[Candidate, 
tuple[CollectionVersionMetadata, GalaxyAPI]] = {}
         self._b_working_directory = b_working_directory  # type: bytes
         self._supplemental_signature_cache = {}  # type: dict[str, str]
         self._keyring = keyring  # type: str
@@ -104,15 +103,12 @@
 
     def get_galaxy_artifact_source_info(self, collection):
         # type: (Candidate) -> dict[str, t.Union[str, list[dict[str, str]]]]
-        server = collection.src.api_server
 
         try:
-            download_url = self._galaxy_collection_cache[collection][0]
-            signatures_url, signatures = 
self._galaxy_collection_origin_cache[collection]
+            metadata, server = self._galaxy_collection_cache[collection]
         except KeyError as key_err:
             raise RuntimeError(
-                'The is no known source for {coll!s}'.
-                format(coll=collection),
+                f"There is no known source for {collection!s}"
             ) from key_err
 
         return {
@@ -120,14 +116,13 @@
             "namespace": collection.namespace,
             "name": collection.name,
             "version": collection.ver,
-            "server": server,
-            "version_url": signatures_url,
-            "download_url": download_url,
-            "signatures": signatures,
+            "server": server.api_server,
+            "version_url": metadata.signatures_url,
+            "download_url": metadata.download_url,
+            "signatures": metadata.signatures,
         }
 
-    def get_galaxy_artifact_path(self, collection):
-        # type: (t.Union[Candidate, Requirement]) -> bytes
+    def get_galaxy_artifact_path(self, collection: Candidate) -> bytes:
         """Given a Galaxy-stored collection, return a cached path.
 
         If it's not yet on disk, this method downloads the artifact first.
@@ -138,11 +133,10 @@
             pass
 
         try:
-            url, sha256_hash, token = self._galaxy_collection_cache[collection]
+            metadata, api = self._galaxy_collection_cache[collection]
         except KeyError as key_err:
             raise RuntimeError(
-                'There is no known source for {coll!s}'.
-                format(coll=collection),
+                f'There is no known source for {collection!s}'
             ) from key_err
 
         display.vvvv(
@@ -152,39 +146,27 @@
 
         try:
             b_artifact_path = _download_file(
-                url,
+                metadata.download_url,
                 self._b_working_directory,
-                expected_hash=sha256_hash,
-                validate_certs=self._validate_certs,
-                token=token,
+                expected_hash=metadata.artifact_sha256,
+                validate_certs=api.validate_certs,
+                token=api.token,
             )  # type: bytes
         except URLError as err:
             raise AnsibleError(
                 'Failed to download collection tar '
-                "from '{coll_src!s}': {download_err!s}".
-                format(
-                    coll_src=to_native(collection.src),
-                    download_err=to_native(err),
-                ),
+                f"from '{api!s}': {err!s}"
             ) from err
         except Exception as err:
             raise AnsibleError(
                 'Failed to download collection tar '
-                "from '{coll_src!s}' due to the following unforeseen error: "
-                '{download_err!s}'.
-                format(
-                    coll_src=to_native(collection.src),
-                    download_err=to_native(err),
-                ),
+                f"from '{api!s}' due to the following unforeseen error: "
+                f'{err!s}'
             ) from err
         else:
             display.vvv(
-                "Collection '{coll!s}' obtained from "
-                'server {server!s} {url!s}'.format(
-                    coll=collection, server=collection.src or 'Galaxy',
-                    url=collection.src.api_server if collection.src is not None
-                    else '',
-                )
+                f"Collection '{collection!s}' obtained from server {api!s} "
+                f"{api.api_server!s}"
             )
 
         self._galaxy_artifact_cache[collection] = b_artifact_path
@@ -338,15 +320,13 @@
         self._artifact_meta_cache[collection.src] = collection_meta
         return collection_meta
 
-    def save_collection_source(self, collection, url, sha256_hash, token, 
signatures_url, signatures):
-        # type: (Candidate, str, str, GalaxyToken, str, list[dict[str, str]]) 
-> None
-        """Store collection URL, SHA256 hash and Galaxy API token.
+    def save_collection_source(self, collection: Candidate, metadata: 
CollectionVersionMetadata, api: GalaxyAPI) -> None:
+        """Store collection version metadata and origin.
 
         This is a hook that is supposed to be called before attempting to
         download Galaxy-based collections with ``get_galaxy_artifact_path()``.
         """
-        self._galaxy_collection_cache[collection] = url, sha256_hash, token
-        self._galaxy_collection_origin_cache[collection] = signatures_url, 
signatures
+        self._galaxy_collection_cache[collection] = (metadata, api)
 
     @classmethod
     @contextmanager
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/lib/ansible/galaxy/collection/galaxy_api_proxy.py 
new/ansible_core-2.19.9/lib/ansible/galaxy/collection/galaxy_api_proxy.py
--- old/ansible_core-2.19.8/lib/ansible/galaxy/collection/galaxy_api_proxy.py   
2026-03-23 18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/lib/ansible/galaxy/collection/galaxy_api_proxy.py   
2026-04-21 02:40:29.000000000 +0200
@@ -146,11 +146,8 @@
             else:
                 self._concrete_art_mgr.save_collection_source(
                     collection_candidate,
-                    version_metadata.download_url,
-                    version_metadata.artifact_sha256,
-                    api.token,
-                    version_metadata.signatures_url,
-                    version_metadata.signatures,
+                    version_metadata,
+                    api
                 )
                 return version_metadata
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/lib/ansible/module_utils/ansible_release.py 
new/ansible_core-2.19.9/lib/ansible/module_utils/ansible_release.py
--- old/ansible_core-2.19.8/lib/ansible/module_utils/ansible_release.py 
2026-03-23 18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/lib/ansible/module_utils/ansible_release.py 
2026-04-21 02:40:29.000000000 +0200
@@ -17,6 +17,6 @@
 
 from __future__ import annotations
 
-__version__ = '2.19.8'
+__version__ = '2.19.9'
 __author__ = 'Ansible, Inc.'
 __codename__ = "What Is and What Should Never Be"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/lib/ansible/plugins/connection/_paramiko_ssh.py 
new/ansible_core-2.19.9/lib/ansible/plugins/connection/_paramiko_ssh.py
--- old/ansible_core-2.19.8/lib/ansible/plugins/connection/_paramiko_ssh.py     
1970-01-01 01:00:00.000000000 +0100
+++ new/ansible_core-2.19.9/lib/ansible/plugins/connection/_paramiko_ssh.py     
2026-04-21 02:40:29.000000000 +0200
@@ -0,0 +1,713 @@
+# (c) 2012, Michael DeHaan <[email protected]>
+# (c) 2017 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or 
https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import annotations
+
+DOCUMENTATION = """
+    author: Ansible Core Team
+    name: paramiko
+    short_description: Run tasks via Python SSH (paramiko)
+    deprecated:
+      removed_in: "2.21"
+      why: Paramiko versions older than 3.3.2 and 3.4.1 will be incompatible 
with cryptography 48.0.0.
+      alternatives: ansible.builtin.ssh
+    description:
+        - Use the Python SSH implementation (Paramiko) to connect to targets
+        - The paramiko transport is provided because many distributions, in 
particular EL6 and before do not support ControlPersist
+          in their SSH implementations.
+        - This is needed on the Ansible control machine to be reasonably 
efficient with connections.
+          Thus paramiko is faster for most users on these platforms.
+          Users with ControlPersist capability can consider using -c ssh or 
configuring the transport in the configuration file.
+        - This plugin also borrows a lot of settings from the ssh plugin as 
they both cover the same protocol.
+    version_added: "0.1"
+    options:
+      remote_addr:
+        description:
+            - Address of the remote target
+        default: inventory_hostname
+        type: string
+        vars:
+            - name: inventory_hostname
+            - name: ansible_host
+            - name: ansible_ssh_host
+            - name: ansible_paramiko_host
+      port:
+          description: Remote port to connect to.
+          type: int
+          default: 22
+          ini:
+            - section: defaults
+              key: remote_port
+            - section: paramiko_connection
+              key: remote_port
+              version_added: '2.15'
+          env:
+            - name: ANSIBLE_REMOTE_PORT
+            - name: ANSIBLE_REMOTE_PARAMIKO_PORT
+              version_added: '2.15'
+          vars:
+            - name: ansible_port
+            - name: ansible_ssh_port
+            - name: ansible_paramiko_port
+              version_added: '2.15'
+          keyword:
+            - name: port
+      remote_user:
+        description:
+            - User to login/authenticate as
+            - Can be set from the CLI via the C(--user) or C(-u) options.
+        type: string
+        vars:
+            - name: ansible_user
+            - name: ansible_ssh_user
+            - name: ansible_paramiko_user
+        env:
+            - name: ANSIBLE_REMOTE_USER
+            - name: ANSIBLE_PARAMIKO_REMOTE_USER
+              version_added: '2.5'
+        ini:
+            - section: defaults
+              key: remote_user
+            - section: paramiko_connection
+              key: remote_user
+              version_added: '2.5'
+        keyword:
+            - name: remote_user
+      password:
+        description:
+          - Secret used to either login the ssh server or as a passphrase for 
ssh keys that require it
+          - Can be set from the CLI via the C(--ask-pass) option.
+        type: string
+        vars:
+            - name: ansible_password
+            - name: ansible_ssh_pass
+            - name: ansible_ssh_password
+            - name: ansible_paramiko_pass
+            - name: ansible_paramiko_password
+              version_added: '2.5'
+      use_rsa_sha2_algorithms:
+        description:
+            - Whether or not to enable RSA SHA2 algorithms for pubkeys and 
hostkeys
+            - On paramiko versions older than 2.9, this only affects hostkeys
+            - For behavior matching paramiko<2.9 set this to V(False)
+        vars:
+            - name: ansible_paramiko_use_rsa_sha2_algorithms
+        ini:
+            - {key: use_rsa_sha2_algorithms, section: paramiko_connection}
+        env:
+            - {name: ANSIBLE_PARAMIKO_USE_RSA_SHA2_ALGORITHMS}
+        default: True
+        type: boolean
+        version_added: '2.14'
+      host_key_auto_add:
+        description: 'Automatically add host keys'
+        env: [{name: ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD}]
+        ini:
+          - {key: host_key_auto_add, section: paramiko_connection}
+        type: boolean
+      look_for_keys:
+        default: True
+        description: 'False to disable searching for private key files in 
~/.ssh/'
+        env: [{name: ANSIBLE_PARAMIKO_LOOK_FOR_KEYS}]
+        ini:
+        - {key: look_for_keys, section: paramiko_connection}
+        type: boolean
+      proxy_command:
+        default: ''
+        description:
+            - Proxy information for running the connection via a jumphost.
+        type: string
+        env: [{name: ANSIBLE_PARAMIKO_PROXY_COMMAND}]
+        ini:
+          - {key: proxy_command, section: paramiko_connection}
+        vars:
+          - name: ansible_paramiko_proxy_command
+            version_added: '2.15'
+      pty:
+        default: True
+        description: 'SUDO usually requires a PTY, True to give a PTY and 
False to not give a PTY.'
+        env:
+          - name: ANSIBLE_PARAMIKO_PTY
+        ini:
+          - section: paramiko_connection
+            key: pty
+        type: boolean
+      record_host_keys:
+        default: True
+        description: 'Save the host keys to a file'
+        env: [{name: ANSIBLE_PARAMIKO_RECORD_HOST_KEYS}]
+        ini:
+          - section: paramiko_connection
+            key: record_host_keys
+        type: boolean
+      host_key_checking:
+        description: 'Set this to "False" if you want to avoid host key 
checking by the underlying tools Ansible uses to connect to the host'
+        type: boolean
+        default: True
+        env:
+          - name: ANSIBLE_HOST_KEY_CHECKING
+          - name: ANSIBLE_SSH_HOST_KEY_CHECKING
+            version_added: '2.5'
+          - name: ANSIBLE_PARAMIKO_HOST_KEY_CHECKING
+            version_added: '2.5'
+        ini:
+          - section: defaults
+            key: host_key_checking
+          - section: paramiko_connection
+            key: host_key_checking
+            version_added: '2.5'
+        vars:
+          - name: ansible_host_key_checking
+            version_added: '2.5'
+          - name: ansible_ssh_host_key_checking
+            version_added: '2.5'
+          - name: ansible_paramiko_host_key_checking
+            version_added: '2.5'
+      use_persistent_connections:
+        description: 'Toggles the use of persistence for connections'
+        type: boolean
+        default: False
+        env:
+          - name: ANSIBLE_USE_PERSISTENT_CONNECTIONS
+        ini:
+          - section: defaults
+            key: use_persistent_connections
+      banner_timeout:
+        type: float
+        default: 30
+        version_added: '2.14'
+        description:
+          - Configures, in seconds, the amount of time to wait for the SSH
+            banner to be presented. This option is supported by paramiko
+            version 1.15.0 or newer.
+        ini:
+          - section: paramiko_connection
+            key: banner_timeout
+        env:
+          - name: ANSIBLE_PARAMIKO_BANNER_TIMEOUT
+      timeout:
+        type: int
+        default: 10
+        description: Number of seconds until the plugin gives up on failing to 
establish a TCP connection.
+        ini:
+          - section: defaults
+            key: timeout
+          - section: ssh_connection
+            key: timeout
+            version_added: '2.11'
+          - section: paramiko_connection
+            key: timeout
+            version_added: '2.15'
+        env:
+          - name: ANSIBLE_TIMEOUT
+          - name: ANSIBLE_SSH_TIMEOUT
+            version_added: '2.11'
+          - name: ANSIBLE_PARAMIKO_TIMEOUT
+            version_added: '2.15'
+        vars:
+          - name: ansible_ssh_timeout
+            version_added: '2.11'
+          - name: ansible_paramiko_timeout
+            version_added: '2.15'
+        cli:
+          - name: timeout
+      private_key_file:
+          description:
+              - Path to private key file to use for authentication.
+          type: string
+          ini:
+            - section: defaults
+              key: private_key_file
+            - section: paramiko_connection
+              key: private_key_file
+              version_added: '2.15'
+          env:
+            - name: ANSIBLE_PRIVATE_KEY_FILE
+            - name: ANSIBLE_PARAMIKO_PRIVATE_KEY_FILE
+              version_added: '2.15'
+          vars:
+            - name: ansible_private_key_file
+            - name: ansible_ssh_private_key_file
+            - name: ansible_paramiko_private_key_file
+              version_added: '2.15'
+          cli:
+            - name: private_key_file
+              option: '--private-key'
+"""
+
+import os
+import socket
+import tempfile
+import traceback
+import fcntl
+import re
+import typing as t
+
+from ansible.module_utils.compat.version import LooseVersion
+from binascii import hexlify
+
+from ansible.errors import (
+    AnsibleAuthenticationFailure,
+    AnsibleConnectionFailure,
+    AnsibleError,
+    AnsibleFileNotFound,
+)
+
+from ansible.module_utils.common.text.converters import to_bytes, to_native, 
to_text
+from ansible.module_utils.compat.paramiko import _PARAMIKO_IMPORT_ERR as 
PARAMIKO_IMPORT_ERR, _paramiko as paramiko
+from ansible.plugins.connection import ConnectionBase
+from ansible.utils.display import Display
+from ansible.utils.path import makedirs_safe
+from ansible.module_utils._internal import _deprecator
+
+display = Display()
+
+
+AUTHENTICITY_MSG = """
+paramiko: The authenticity of host '%s' can't be established.
+The %s key fingerprint is %s.
+Are you sure you want to continue connecting (yes/no)?
+"""
+
+# SSH Options Regex
+SETTINGS_REGEX = re.compile(r'(\w+)(?:\s*=\s*|\s+)(.+)')
+
+MissingHostKeyPolicy: type = object
+if paramiko:
+    MissingHostKeyPolicy = paramiko.MissingHostKeyPolicy
+
+
+class MyAddPolicy(MissingHostKeyPolicy):
+    """
+    Based on AutoAddPolicy in paramiko so we can determine when keys are added
+
+    and also prompt for input.
+
+    Policy for automatically adding the hostname and new host key to the
+    local L{HostKeys} object, and saving it.  This is used by L{SSHClient}.
+    """
+
+    def __init__(self, connection: Connection) -> None:
+        self.connection = connection
+        self._options = connection._options
+
+    def missing_host_key(self, client, hostname, key) -> None:
+
+        if all((self.connection.get_option('host_key_checking'), not 
self.connection.get_option('host_key_auto_add'))):
+
+            fingerprint = hexlify(key.get_fingerprint())
+            ktype = key.get_name()
+
+            if self.connection.get_option('use_persistent_connections') or 
self.connection.force_persistence:
+                # don't print the prompt string since the user cannot respond
+                # to the question anyway
+                raise AnsibleError(AUTHENTICITY_MSG[1:92] % (hostname, ktype, 
fingerprint))
+
+            inp = to_text(
+                display.prompt_until(AUTHENTICITY_MSG % (hostname, ktype, 
fingerprint), private=False),
+                errors='surrogate_or_strict'
+            )
+
+            if inp not in ['yes', 'y', '']:
+                raise AnsibleError("host connection rejected by user")
+
+        key._added_by_ansible_this_time = True
+
+        # existing implementation below:
+        client._host_keys.add(hostname, key.get_name(), key)
+
+        # host keys are actually saved in close() function below
+        # in order to control ordering.
+
+
+# keep connection objects on a per host basis to avoid repeated attempts to 
reconnect
+
+SSH_CONNECTION_CACHE: dict[str, paramiko.client.SSHClient] = {}
+SFTP_CONNECTION_CACHE: dict[str, paramiko.sftp_client.SFTPClient] = {}
+
+
+class Connection(ConnectionBase):
+    """ SSH based connections with Paramiko """
+
+    transport = 'paramiko'
+    _log_channel: str | None = None
+
+    def __init__(self, *args, **kwargs):
+        display.deprecated(  # pylint: 
disable=ansible-deprecated-unnecessary-collection-name
+            msg='The paramiko connection plugin is deprecated.',
+            version='2.21',
+            deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR,  # entire plugin 
being removed; this improves the messaging
+        )
+
+        super().__init__(*args, **kwargs)
+
+    def _cache_key(self) -> str:
+        return "%s__%s__" % (self.get_option('remote_addr'), 
self.get_option('remote_user'))
+
+    def _connect(self) -> Connection:
+        cache_key = self._cache_key()
+        if cache_key in SSH_CONNECTION_CACHE:
+            self.ssh = SSH_CONNECTION_CACHE[cache_key]
+        else:
+            self.ssh = SSH_CONNECTION_CACHE[cache_key] = 
self._connect_uncached()
+
+        self._connected = True
+        return self
+
+    def _set_log_channel(self, name: str) -> None:
+        """Mimic paramiko.SSHClient.set_log_channel"""
+        self._log_channel = name
+
+    def _parse_proxy_command(self, port: int = 22) -> dict[str, t.Any]:
+        proxy_command = self.get_option('proxy_command') or None
+
+        sock_kwarg = {}
+        if proxy_command:
+            replacers = {
+                '%h': self.get_option('remote_addr'),
+                '%p': port,
+                '%r': self.get_option('remote_user')
+            }
+            for find, replace in replacers.items():
+                proxy_command = proxy_command.replace(find, str(replace))
+            try:
+                sock_kwarg = {'sock': paramiko.ProxyCommand(proxy_command)}
+                display.vvv("CONFIGURE PROXY COMMAND FOR CONNECTION: %s" % 
proxy_command, host=self.get_option('remote_addr'))
+            except AttributeError:
+                display.warning('Paramiko ProxyCommand support unavailable. '
+                                'Please upgrade to Paramiko 1.9.0 or newer. '
+                                'Not using configured ProxyCommand')
+
+        return sock_kwarg
+
+    def _connect_uncached(self) -> paramiko.SSHClient:
+        """ activates the connection object """
+
+        if paramiko is None:
+            raise AnsibleError("paramiko is not installed: %s" % 
to_native(PARAMIKO_IMPORT_ERR))
+
+        port = self.get_option('port')
+        display.vvv("ESTABLISH PARAMIKO SSH CONNECTION FOR USER: %s on PORT %s 
TO %s" % (self.get_option('remote_user'), port, self.get_option('remote_addr')),
+                    host=self.get_option('remote_addr'))
+
+        ssh = paramiko.SSHClient()
+
+        # Set pubkey and hostkey algorithms to disable, the only manipulation 
allowed currently
+        # is keeping or omitting rsa-sha2 algorithms
+        # default_keys: t.Tuple[str] = ()
+        paramiko_preferred_pubkeys = getattr(paramiko.Transport, 
'_preferred_pubkeys', ())
+        paramiko_preferred_hostkeys = getattr(paramiko.Transport, 
'_preferred_keys', ())
+        use_rsa_sha2_algorithms = self.get_option('use_rsa_sha2_algorithms')
+        disabled_algorithms: t.Dict[str, t.Iterable[str]] = {}
+        if not use_rsa_sha2_algorithms:
+            if paramiko_preferred_pubkeys:
+                disabled_algorithms['pubkeys'] = tuple(a for a in 
paramiko_preferred_pubkeys if 'rsa-sha2' in a)
+            if paramiko_preferred_hostkeys:
+                disabled_algorithms['keys'] = tuple(a for a in 
paramiko_preferred_hostkeys if 'rsa-sha2' in a)
+
+        # override paramiko's default logger name
+        if self._log_channel is not None:
+            ssh.set_log_channel(self._log_channel)
+
+        self.keyfile = os.path.expanduser("~/.ssh/known_hosts")
+
+        if self.get_option('host_key_checking'):
+            for ssh_known_hosts in ("/etc/ssh/ssh_known_hosts", 
"/etc/openssh/ssh_known_hosts"):
+                try:
+                    # TODO: check if we need to look at several possible 
locations, possible for loop
+                    ssh.load_system_host_keys(ssh_known_hosts)
+                    break
+                except OSError:
+                    pass  # file was not found, but not required to function
+            ssh.load_system_host_keys()
+
+        ssh_connect_kwargs = self._parse_proxy_command(port)
+
+        ssh.set_missing_host_key_policy(MyAddPolicy(self))
+
+        conn_password = self.get_option('password')
+
+        allow_agent = True
+
+        if conn_password is not None:
+            allow_agent = False
+
+        try:
+            key_filename = None
+            if self.get_option('private_key_file'):
+                key_filename = 
os.path.expanduser(self.get_option('private_key_file'))
+
+            # paramiko 2.2 introduced auth_timeout parameter
+            if LooseVersion(paramiko.__version__) >= LooseVersion('2.2.0'):
+                ssh_connect_kwargs['auth_timeout'] = self.get_option('timeout')
+
+            # paramiko 1.15 introduced banner timeout parameter
+            if LooseVersion(paramiko.__version__) >= LooseVersion('1.15.0'):
+                ssh_connect_kwargs['banner_timeout'] = 
self.get_option('banner_timeout')
+
+            ssh.connect(
+                self.get_option('remote_addr').lower(),
+                username=self.get_option('remote_user'),
+                allow_agent=allow_agent,
+                look_for_keys=self.get_option('look_for_keys'),
+                key_filename=key_filename,
+                password=conn_password,
+                timeout=self.get_option('timeout'),
+                port=port,
+                disabled_algorithms=disabled_algorithms,
+                **ssh_connect_kwargs,
+            )
+        except paramiko.ssh_exception.BadHostKeyException as e:
+            raise AnsibleConnectionFailure('host key mismatch for %s' % 
e.hostname)
+        except paramiko.ssh_exception.AuthenticationException as ex:
+            raise AnsibleAuthenticationFailure() from ex
+        except Exception as ex:
+            msg = str(ex)
+            if u"PID check failed" in msg:
+                raise AnsibleError("paramiko version issue, please upgrade 
paramiko on the machine running ansible") from ex
+            elif u"Private key file is encrypted" in msg:
+                msg = 'ssh %s@%s:%s : %s\nTo connect as a different user, use 
-u <username>.' % (
+                    self.get_option('remote_user'), 
self.get_options('remote_addr'), port, msg)
+                raise AnsibleConnectionFailure(msg) from ex
+            else:
+                raise AnsibleConnectionFailure(msg) from ex
+
+        return ssh
+
+    def exec_command(self, cmd: str, in_data: bytes | None = None, sudoable: 
bool = True) -> tuple[int, bytes, bytes]:
+        """ run a command on the remote host """
+
+        super(Connection, self).exec_command(cmd, in_data=in_data, 
sudoable=sudoable)
+
+        if in_data:
+            raise AnsibleError("Internal Error: this module does not support 
optimized module pipelining")
+
+        bufsize = 4096
+
+        try:
+            self.ssh.get_transport().set_keepalive(5)
+            chan = self.ssh.get_transport().open_session()
+        except Exception as e:
+            text_e = to_text(e)
+            msg = u"Failed to open session"
+            if text_e:
+                msg += u": %s" % text_e
+            raise AnsibleConnectionFailure(to_native(msg))
+
+        # sudo usually requires a PTY (cf. requiretty option), therefore
+        # we give it one by default (pty=True in ansible.cfg), and we try
+        # to initialise from the calling environment when sudoable is enabled
+        if self.get_option('pty') and sudoable:
+            chan.get_pty(term=os.getenv('TERM', 'vt100'), 
width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0)))
+
+        display.vvv("EXEC %s" % cmd, host=self.get_option('remote_addr'))
+
+        cmd = to_bytes(cmd, errors='surrogate_or_strict')
+
+        no_prompt_out = b''
+        no_prompt_err = b''
+        become_output = b''
+
+        try:
+            chan.exec_command(cmd)
+            if self.become and self.become.expect_prompt():
+                passprompt = False
+                become_sucess = False
+                while not (become_sucess or passprompt):
+                    display.debug('Waiting for Privilege Escalation input')
+
+                    chunk = chan.recv(bufsize)
+                    display.debug("chunk is: %r" % chunk)
+                    if not chunk:
+                        if b'unknown user' in become_output:
+                            n_become_user = 
to_native(self.become.get_option('become_user'))
+                            raise AnsibleError('user %s does not exist' % 
n_become_user)
+                        else:
+                            break
+                            # raise AnsibleError('ssh connection closed 
waiting for password prompt')
+                    become_output += chunk
+
+                    # need to check every line because we might get lectured
+                    # and we might get the middle of a line in a chunk
+                    for line in become_output.splitlines(True):
+                        if self.become.check_success(line):
+                            become_sucess = True
+                            break
+                        elif self.become.check_password_prompt(line):
+                            passprompt = True
+                            break
+
+                if passprompt:
+                    if self.become:
+                        become_pass = self.become.get_option('become_pass')
+                        chan.sendall(to_bytes(become_pass, 
errors='surrogate_or_strict') + b'\n')
+                    else:
+                        raise AnsibleError("A password is required but none 
was supplied")
+                else:
+                    no_prompt_out += become_output
+                    no_prompt_err += become_output
+        except socket.timeout:
+            raise AnsibleError('ssh timed out waiting for privilege 
escalation.\n' + to_text(become_output))
+
+        stdout = b''.join(chan.makefile('rb', bufsize))
+        stderr = b''.join(chan.makefile_stderr('rb', bufsize))
+
+        return (chan.recv_exit_status(), no_prompt_out + stdout, no_prompt_out 
+ stderr)
+
+    def put_file(self, in_path: str, out_path: str) -> None:
+        """ transfer a file from local to remote """
+
+        super(Connection, self).put_file(in_path, out_path)
+
+        display.vvv("PUT %s TO %s" % (in_path, out_path), 
host=self.get_option('remote_addr'))
+
+        if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')):
+            raise AnsibleFileNotFound("file or module does not exist: %s" % 
in_path)
+
+        try:
+            self.sftp = self.ssh.open_sftp()
+        except Exception as e:
+            raise AnsibleError("failed to open a SFTP connection (%s)" % e)
+
+        try:
+            self.sftp.put(to_bytes(in_path, errors='surrogate_or_strict'), 
to_bytes(out_path, errors='surrogate_or_strict'))
+        except OSError as ex:
+            raise AnsibleError(f"Failed to transfer file to {out_path!r}.") 
from ex
+
+    def _connect_sftp(self) -> paramiko.sftp_client.SFTPClient:
+
+        cache_key = "%s__%s__" % (self.get_option('remote_addr'), 
self.get_option('remote_user'))
+        if cache_key in SFTP_CONNECTION_CACHE:
+            return SFTP_CONNECTION_CACHE[cache_key]
+        else:
+            result = SFTP_CONNECTION_CACHE[cache_key] = 
self._connect().ssh.open_sftp()
+            return result
+
+    def fetch_file(self, in_path: str, out_path: str) -> None:
+        """ save a remote file to the specified path """
+
+        super(Connection, self).fetch_file(in_path, out_path)
+
+        display.vvv("FETCH %s TO %s" % (in_path, out_path), 
host=self.get_option('remote_addr'))
+
+        try:
+            self.sftp = self._connect_sftp()
+        except Exception as e:
+            raise AnsibleError("failed to open a SFTP connection (%s)" % 
to_native(e))
+
+        try:
+            self.sftp.get(to_bytes(in_path, errors='surrogate_or_strict'), 
to_bytes(out_path, errors='surrogate_or_strict'))
+        except OSError as ex:
+            raise AnsibleError(f"Failed to transfer file from {in_path!r}.") 
from ex
+
+    def _any_keys_added(self) -> bool:
+
+        for hostname, keys in self.ssh._host_keys.items():
+            for keytype, key in keys.items():
+                added_this_time = getattr(key, '_added_by_ansible_this_time', 
False)
+                if added_this_time:
+                    return True
+        return False
+
+    def _save_ssh_host_keys(self, filename: str) -> None:
+        """
+        not using the paramiko save_ssh_host_keys function as we want to add 
new SSH keys at the bottom so folks
+        don't complain about it :)
+        """
+
+        if not self._any_keys_added():
+            return
+
+        path = os.path.expanduser("~/.ssh")
+        makedirs_safe(path)
+
+        with open(filename, 'w') as f:
+
+            for hostname, keys in self.ssh._host_keys.items():
+
+                for keytype, key in keys.items():
+
+                    # was f.write
+                    added_this_time = getattr(key, 
'_added_by_ansible_this_time', False)
+                    if not added_this_time:
+                        f.write("%s %s %s\n" % (hostname, keytype, 
key.get_base64()))
+
+            for hostname, keys in self.ssh._host_keys.items():
+
+                for keytype, key in keys.items():
+                    added_this_time = getattr(key, 
'_added_by_ansible_this_time', False)
+                    if added_this_time:
+                        f.write("%s %s %s\n" % (hostname, keytype, 
key.get_base64()))
+
+    def reset(self) -> None:
+        if not self._connected:
+            return
+        self.close()
+        self._connect()
+
+    def close(self) -> None:
+        """ terminate the connection """
+
+        cache_key = self._cache_key()
+        SSH_CONNECTION_CACHE.pop(cache_key, None)
+        SFTP_CONNECTION_CACHE.pop(cache_key, None)
+
+        if hasattr(self, 'sftp'):
+            if self.sftp is not None:
+                self.sftp.close()
+
+        if self.get_option('host_key_checking') and 
self.get_option('record_host_keys') and self._any_keys_added():
+
+            # add any new SSH host keys -- warning -- this could be slow
+            # (This doesn't acquire the connection lock because it needs
+            # to exclude only other known_hosts writers, not connections
+            # that are starting up.)
+            lockfile = self.keyfile.replace("known_hosts", ".known_hosts.lock")
+            dirname = os.path.dirname(self.keyfile)
+            makedirs_safe(dirname)
+
+            KEY_LOCK = open(lockfile, 'w')
+            fcntl.lockf(KEY_LOCK, fcntl.LOCK_EX)
+
+            try:
+                # just in case any were added recently
+
+                self.ssh.load_system_host_keys()
+                self.ssh._host_keys.update(self.ssh._system_host_keys)
+
+                # gather information about the current key file, so
+                # we can ensure the new file has the correct mode/owner
+
+                key_dir = os.path.dirname(self.keyfile)
+                if os.path.exists(self.keyfile):
+                    key_stat = os.stat(self.keyfile)
+                    mode = key_stat.st_mode
+                    uid = key_stat.st_uid
+                    gid = key_stat.st_gid
+                else:
+                    mode = 33188
+                    uid = os.getuid()
+                    gid = os.getgid()
+
+                # Save the new keys to a temporary file and move it into place
+                # rather than rewriting the file. We set delete=False because
+                # the file will be moved into place rather than cleaned up.
+
+                tmp_keyfile = tempfile.NamedTemporaryFile(dir=key_dir, 
delete=False)
+                os.chmod(tmp_keyfile.name, mode & 0o7777)
+                os.chown(tmp_keyfile.name, uid, gid)
+
+                self._save_ssh_host_keys(tmp_keyfile.name)
+                tmp_keyfile.close()
+
+                os.rename(tmp_keyfile.name, self.keyfile)
+
+            except Exception:
+
+                # unable to save keys, including scenario when key was invalid
+                # and caught earlier
+                traceback.print_exc()
+            fcntl.lockf(KEY_LOCK, fcntl.LOCK_UN)
+
+        self.ssh.close()
+        self._connected = False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/lib/ansible/plugins/connection/paramiko_ssh.py 
new/ansible_core-2.19.9/lib/ansible/plugins/connection/paramiko_ssh.py
--- old/ansible_core-2.19.8/lib/ansible/plugins/connection/paramiko_ssh.py      
2026-03-23 18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/lib/ansible/plugins/connection/paramiko_ssh.py      
2026-04-21 02:40:29.000000000 +0200
@@ -7,6 +7,10 @@
     author: Ansible Core Team
     name: paramiko
     short_description: Run tasks via Python SSH (paramiko)
+    deprecated:
+      removed_in: "2.21"
+      why: Paramiko versions older than 3.3.2 and 3.4.1 will be incompatible 
with cryptography 48.0.0.
+      alternatives: ansible.builtin.ssh
     description:
         - Use the Python SSH implementation (Paramiko) to connect to targets
         - The paramiko transport is provided because many distributions, in 
particular EL6 and before do not support ControlPersist
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ansible_core-2.19.8/lib/ansible/release.py 
new/ansible_core-2.19.9/lib/ansible/release.py
--- old/ansible_core-2.19.8/lib/ansible/release.py      2026-03-23 
18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/lib/ansible/release.py      2026-04-21 
02:40:29.000000000 +0200
@@ -17,6 +17,6 @@
 
 from __future__ import annotations
 
-__version__ = '2.19.8'
+__version__ = '2.19.9'
 __author__ = 'Ansible, Inc.'
 __codename__ = "What Is and What Should Never Be"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/integration/targets/ansible-test-cloud-azure/aliases
 
new/ansible_core-2.19.9/test/integration/targets/ansible-test-cloud-azure/aliases
--- 
old/ansible_core-2.19.8/test/integration/targets/ansible-test-cloud-azure/aliases
   2026-03-23 18:38:39.000000000 +0100
+++ 
new/ansible_core-2.19.9/test/integration/targets/ansible-test-cloud-azure/aliases
   2026-04-21 02:40:29.000000000 +0200
@@ -1,3 +1,4 @@
 cloud/azure
 shippable/generic/group1
 context/controller
+disabled/yes  # Azure credential provisioner pool broken in Core CI (likely SP 
policy)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/integration/targets/ansible-test-metadata/aliases 
new/ansible_core-2.19.9/test/integration/targets/ansible-test-metadata/aliases
--- 
old/ansible_core-2.19.8/test/integration/targets/ansible-test-metadata/aliases  
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.19.9/test/integration/targets/ansible-test-metadata/aliases  
    2026-04-21 02:40:29.000000000 +0200
@@ -0,0 +1,3 @@
+shippable/posix/group3  # runs in the distro test containers
+shippable/generic/group1  # runs in the default test container
+context/controller
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/integration/targets/ansible-test-metadata/runme.sh 
new/ansible_core-2.19.9/test/integration/targets/ansible-test-metadata/runme.sh
--- 
old/ansible_core-2.19.8/test/integration/targets/ansible-test-metadata/runme.sh 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/ansible_core-2.19.9/test/integration/targets/ansible-test-metadata/runme.sh 
    2026-04-21 02:40:29.000000000 +0200
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Verify that importlib.metadata can find ansible-core using the PYTHONPATH 
set by ansible-test.
+# Regression test for https://github.com/ansible/ansible/issues/86695
+
+set -eux
+
+VERSION=$(python -c "from importlib.metadata import version; 
print(version('ansible-core'))")
+
+test "$VERSION" = "$ANSIBLE_TEST_ANSIBLE_VERSION"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/lib/ansible_test/_data/completion/remote.txt 
new/ansible_core-2.19.9/test/lib/ansible_test/_data/completion/remote.txt
--- old/ansible_core-2.19.8/test/lib/ansible_test/_data/completion/remote.txt   
2026-03-23 18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/test/lib/ansible_test/_data/completion/remote.txt   
2026-04-21 02:40:29.000000000 +0200
@@ -5,8 +5,8 @@
 freebsd/13.5 python=3.11 python_dir=/usr/local/bin become=su_sudo provider=aws 
arch=x86_64 alias=freebsd/13
 freebsd/14.2 python=3.11 python_dir=/usr/local/bin become=su_sudo provider=aws 
arch=x86_64 alias=freebsd/14,freebsd/latest
 freebsd python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
-macos/15.3 python=3.13 python_dir=/usr/local/bin become=sudo 
provider=parallels arch=x86_64 alias=macos/15,macos/latest
-macos python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64
+macos/15.3 python=3.13 python_dir=/usr/local/bin become=sudo provider=mac 
arch=aarch64 alias=macos/15,macos/latest
+macos python_dir=/usr/local/bin become=sudo provider=mac arch=aarch64
 rhel/9.7 python=3.9,3.12 become=sudo provider=aws arch=x86_64 alias=rhel/9
 rhel/10.1 python=3.12 become=sudo provider=aws arch=x86_64 
alias=rhel/10,rhel/latest
 rhel become=sudo provider=aws arch=x86_64
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/lib/ansible_test/_internal/ansible_util.py 
new/ansible_core-2.19.9/test/lib/ansible_test/_internal/ansible_util.py
--- old/ansible_core-2.19.8/test/lib/ansible_test/_internal/ansible_util.py     
2026-03-23 18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/test/lib/ansible_test/_internal/ansible_util.py     
2026-04-21 02:40:29.000000000 +0200
@@ -4,6 +4,7 @@
 
 import json
 import os
+import pathlib
 import shutil
 import typing as t
 
@@ -12,6 +13,10 @@
     SOFT_RLIMIT_NOFILE,
 )
 
+from .io import (
+    write_text_file,
+)
+
 from .util import (
     common_environment,
     ApplicationError,
@@ -22,6 +27,7 @@
     ANSIBLE_SOURCE_ROOT,
     ANSIBLE_TEST_TOOLS_ROOT,
     MODE_FILE_EXECUTE,
+    get_ansible_version,
     raw_command,
     verified_chmod,
 )
@@ -248,15 +254,12 @@
     raise RuntimeError(path)
 
 
-# noinspection PyUnusedLocal
 @mutex
 def get_ansible_python_path(args: CommonConfig) -> str:
     """
     Return a directory usable for PYTHONPATH, containing only the ansible 
package.
     If a temporary directory is required, it will be cached for the lifetime 
of the process and cleaned up at exit.
     """
-    del args  # not currently used
-
     try:
         return get_ansible_python_path.python_path  # type: 
ignore[attr-defined]
     except AttributeError:
@@ -273,11 +276,38 @@
 
         os.symlink(ANSIBLE_LIB_ROOT, os.path.join(python_path, 'ansible'))
 
+    if not args.explain:
+        generate_dist_info(python_path)
+
     get_ansible_python_path.python_path = python_path  # type: 
ignore[attr-defined]
 
     return python_path
 
 
+def generate_dist_info(path: str) -> None:
+    """Generate a dist-info in the specified base directory."""
+    version = get_ansible_version()
+    metadata = f'''\
+Metadata-Version: 2.1
+Name: ansible-core
+Version: {version}
+'''
+    python_path = pathlib.Path(path)
+
+    current_dist_info = python_path / f'ansible_core-{version}.dist-info'
+
+    for dist_info in pathlib.Path(path).glob('ansible_core-*.dist-info'):
+        if dist_info == current_dist_info:
+            continue
+        shutil.rmtree(dist_info, ignore_errors=True)
+
+    metadata_path = current_dist_info / 'METADATA'
+    if metadata_path.is_file():
+        return
+
+    write_text_file(str(metadata_path), metadata, create_directories=True)
+
+
 class CollectionDetail:
     """Collection detail."""
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/lib/ansible_test/_internal/commands/integration/__init__.py
 
new/ansible_core-2.19.9/test/lib/ansible_test/_internal/commands/integration/__init__.py
--- 
old/ansible_core-2.19.8/test/lib/ansible_test/_internal/commands/integration/__init__.py
    2026-03-23 18:38:39.000000000 +0100
+++ 
new/ansible_core-2.19.9/test/lib/ansible_test/_internal/commands/integration/__init__.py
    2026-04-21 02:40:29.000000000 +0200
@@ -61,6 +61,7 @@
 from ...util import (
     ApplicationError,
     display,
+    get_ansible_version,
     SubprocessError,
     remove_tree,
 )
@@ -808,6 +809,7 @@
         ANSIBLE_CALLBACKS_ENABLED=','.join(sorted(set(callback_plugins))),
         ANSIBLE_TEST_CI=args.metadata.ci_provider or get_ci_provider().code,
         ANSIBLE_TEST_COVERAGE='check' if args.coverage_check else ('yes' if 
args.coverage else ''),
+        ANSIBLE_TEST_ANSIBLE_VERSION=get_ansible_version(),
         OUTPUT_DIR=test_dir,
         INVENTORY_PATH=os.path.abspath(inventory_path),
     )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/lib/ansible_test/_internal/constants.py 
new/ansible_core-2.19.9/test/lib/ansible_test/_internal/constants.py
--- old/ansible_core-2.19.8/test/lib/ansible_test/_internal/constants.py        
2026-03-23 18:38:39.000000000 +0100
+++ new/ansible_core-2.19.9/test/lib/ansible_test/_internal/constants.py        
2026-04-21 02:40:29.000000000 +0200
@@ -23,7 +23,7 @@
     'default',
     'aws',
     'azure',
-    'parallels',
+    'mac',
 ]
 
 SECCOMP_CHOICES = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/ansible_core-2.19.8/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
 
new/ansible_core-2.19.9/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
--- 
old/ansible_core-2.19.8/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
   2026-03-23 18:38:39.000000000 +0100
+++ 
new/ansible_core-2.19.9/test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
   2026-04-21 02:40:29.000000000 +0200
@@ -795,11 +795,10 @@
         Required('why'): doc_string,
         'alternative': doc_string,
         'alternatives': doc_string,
+        Required('removed_from_collection'): collection_name,
+        'removed': Any(True),
     }
 
-    if for_collection:
-        main_fields.update({Required('removed_from_collection'): 
collection_name, 'removed': Any(True)})
-
     date_schema = {
         Required('removed_at_date'): date(),
     }

++++++ ansible_core-2.19.8.tar.gz.sha256 -> ansible_core-2.19.9.tar.gz.sha256 
++++++
--- 
/work/SRC/openSUSE:Factory/ansible-core-2.19/ansible_core-2.19.8.tar.gz.sha256  
    2026-03-27 06:36:06.788856408 +0100
+++ 
/work/SRC/openSUSE:Factory/.ansible-core-2.19.new.11940/ansible_core-2.19.9.tar.gz.sha256
   2026-04-21 12:47:31.557157232 +0200
@@ -1 +1 @@
-676e70925b775d2c8526b2bcea59c4b2195221bd6e4e3c0537e89387e08608bd  
ansible_core-2.19.8.tar.gz
+74107de13d188ff579fb215bc3eb875c9198803215d6378ed588c7f35aba12f5  
ansible_core-2.19.9.tar.gz

Reply via email to