Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ansible-core for openSUSE:Factory checked in at 2026-08-11 17:17:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible-core (Old) and /work/SRC/openSUSE:Factory/.ansible-core.new.17972 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible-core" Tue Aug 11 17:17:01 2026 rev:63 rq:1370641 version:2.21.3 Changes: -------- --- /work/SRC/openSUSE:Factory/ansible-core/ansible-core.changes 2026-07-14 13:51:53.823034133 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.17972/ansible-core.changes 2026-08-11 17:18:09.832660159 +0200 @@ -1,0 +2,26 @@ +Tue Aug 11 07:25:48 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 2.21.3: + https://github.com/ansible/ansible/blob/v2.21.3/changelogs/CHANGELOG-v2.21.rst + * Bugfixes + - Add deprecation status to the tree and oneline callback + DOCUMENTATION. (#87020) + - ansible-galaxy - Fix attempting to download the collection + again if the response from the server is shorter than + expected, instead of failing due to the mismatched artifact + hash on the first attempt. (#86025) + - ansible-test - Fix target filtering to preserve + user-specified versions that are not in the completion + configuration. + - collection loader - Fix the collection loader logic to + correctly return Python module when calling + pkgutil.iter_modules with a package that is inside a + collection path and contains compiled Python extension + modules. + - powershell exec_wrapper - fix handling when multiple pwsh + executables match by selecting the first result (#87228). + - rpm_key - ensure a trailing newline is present on PGP armor + data before passing it to librpm for parsing, fixing failures + on systems where pgpParsePkts requires it (#87303). + +------------------------------------------------------------------- Old: ---- ansible_core-2.21.2.tar.gz ansible_core-2.21.2.tar.gz.sha256 New: ---- ansible_core-2.21.3.tar.gz ansible_core-2.21.3.tar.gz.sha256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible-core.spec ++++++ --- /var/tmp/diff_new_pack.81Mxfm/_old 2026-08-11 17:18:13.076797631 +0200 +++ /var/tmp/diff_new_pack.81Mxfm/_new 2026-08-11 17:18:13.080797800 +0200 @@ -43,7 +43,7 @@ %endif Name: ansible-core -Version: 2.21.2 +Version: 2.21.3 Release: 0 Summary: Radically simple IT automation License: GPL-3.0-or-later ++++++ ansible_core-2.21.2.tar.gz -> ansible_core-2.21.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/PKG-INFO new/ansible_core-2.21.3/PKG-INFO --- old/ansible_core-2.21.2/PKG-INFO 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/PKG-INFO 2026-08-10 17:52:54.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.21.2 +Version: 2.21.3 Summary: Radically simple IT automation Author: Ansible Project License-Expression: GPL-3.0-or-later diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/ansible_core.egg-info/PKG-INFO new/ansible_core-2.21.3/ansible_core.egg-info/PKG-INFO --- old/ansible_core-2.21.2/ansible_core.egg-info/PKG-INFO 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/ansible_core.egg-info/PKG-INFO 2026-08-10 17:52:54.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.21.2 +Version: 2.21.3 Summary: Radically simple IT automation Author: Ansible Project License-Expression: GPL-3.0-or-later diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/ansible_core.egg-info/SOURCES.txt new/ansible_core-2.21.3/ansible_core.egg-info/SOURCES.txt --- old/ansible_core-2.21.2/ansible_core.egg-info/SOURCES.txt 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/ansible_core.egg-info/SOURCES.txt 2026-08-10 17:52:54.000000000 +0200 @@ -1763,6 +1763,12 @@ test/integration/targets/collection/aliases test/integration/targets/collection/setup.sh test/integration/targets/collection/update-ignore.py +test/integration/targets/collection_loader/aliases +test/integration/targets/collection_loader/main.yml +test/integration/targets/collection_loader/runme.sh +test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py +test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py +test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/sub/__init__.py test/integration/targets/collections/a.statichost.yml test/integration/targets/collections/aliases test/integration/targets/collections/cache.statichost.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/changelogs/CHANGELOG-v2.21.rst new/ansible_core-2.21.3/changelogs/CHANGELOG-v2.21.rst --- old/ansible_core-2.21.2/changelogs/CHANGELOG-v2.21.rst 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/changelogs/CHANGELOG-v2.21.rst 2026-08-10 17:52:54.000000000 +0200 @@ -4,6 +4,25 @@ .. contents:: Topics +v2.21.3 +======= + +Release Summary +--------------- + +| Release Date: 2026-08-10 +| `Porting Guide <https://docs.ansible.com/ansible-core/2.21/porting_guides/porting_guide_core_2.21.html>`__ + +Bugfixes +-------- + +- Add deprecation status to the tree and oneline callback DOCUMENTATION. (https://github.com/ansible/ansible/issues/87020) +- ansible-galaxy - Fix attempting to download the collection again if the response from the server is shorter than expected, instead of failing due to the mismatched artifact hash on the first attempt. (https://github.com/ansible/ansible/pull/86025) +- ansible-test - Fix target filtering to preserve user-specified versions that are not in the completion configuration. +- collection loader - Fix the collection loader logic to correctly return Python module when calling ``pkgutil.iter_modules`` with a package that is inside a collection path and contains compiled Python extension modules. +- powershell exec_wrapper - fix handling when multiple pwsh executables match by selecting the first result (https://github.com/ansible/ansible/issues/87228). +- rpm_key - ensure a trailing newline is present on PGP armor data before passing it to librpm for parsing, fixing failures on systems where ``pgpParsePkts`` requires it (https://github.com/ansible/ansible/issues/87303). + v2.21.2 ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/changelogs/changelog.yaml new/ansible_core-2.21.3/changelogs/changelog.yaml --- old/ansible_core-2.21.2/changelogs/changelog.yaml 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/changelogs/changelog.yaml 2026-08-10 17:52:54.000000000 +0200 @@ -727,3 +727,46 @@ - ansible-test-timeout-callback.yml - fix-short-timeout-async-wrapper.yml release_date: '2026-07-06' + 2.21.3: + changes: + release_summary: '| Release Date: 2026-08-10 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.21/porting_guides/porting_guide_core_2.21.html>`__ + + ' + codename: The Rain Song + fragments: + - 2.21.3_summary.yaml + release_date: '2026-08-10' + 2.21.3rc1: + changes: + bugfixes: + - Add deprecation status to the tree and oneline callback DOCUMENTATION. (https://github.com/ansible/ansible/issues/87020) + - ansible-galaxy - Fix attempting to download the collection again if the response + from the server is shorter than expected, instead of failing due to the mismatched + artifact hash on the first attempt. (https://github.com/ansible/ansible/pull/86025) + - ansible-test - Fix target filtering to preserve user-specified versions that + are not in the completion configuration. + - collection loader - Fix the collection loader logic to correctly return Python + module when calling ``pkgutil.iter_modules`` with a package that is inside + a collection path and contains compiled Python extension modules. + - powershell exec_wrapper - fix handling when multiple pwsh executables match + by selecting the first result (https://github.com/ansible/ansible/issues/87228). + - rpm_key - ensure a trailing newline is present on PGP armor data before passing + it to librpm for parsing, fixing failures on systems where ``pgpParsePkts`` + requires it (https://github.com/ansible/ansible/issues/87303). + release_summary: '| Release Date: 2026-08-03 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.21/porting_guides/porting_guide_core_2.21.html>`__ + + ' + codename: The Rain Song + fragments: + - 2.21.3rc1_summary.yaml + - 87304-rpm_key-armor-trailing-newline.yml + - ansible-galaxy-retry-IncompleteRead-download.yml + - ansible-test-target-filtering.yml + - collection-loader-extensions.yml + - fix-tree-and-oneline-callback-deprecation.yml + - powershell-exec-wrapper-select-first.yml + release_date: '2026-08-03' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/cli/galaxy.py new/ansible_core-2.21.3/lib/ansible/cli/galaxy.py --- old/ansible_core-2.21.2/lib/ansible/cli/galaxy.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/cli/galaxy.py 2026-08-10 17:52:54.000000000 +0200 @@ -203,7 +203,7 @@ if args[1:3] == ['role', 'login']: display.error( "The login command was removed in late 2020. An API key is now required to publish roles or collections " - "to Galaxy. The key can be found at https://galaxy.ansible.com/me/preferences, and passed to the " + "to Galaxy. The key can be found at https://galaxy.ansible.com/ui/token, and passed to the " "ansible-galaxy CLI via a file at {0} or (insecurely) via the `--token` " "command-line argument.".format(to_text(C.GALAXY_TOKEN_PATH))) sys.exit(1) @@ -238,7 +238,7 @@ common.add_argument('--api-version', type=int, choices=[2, 3], help=argparse.SUPPRESS) # Hidden argument that should only be used in our tests common.add_argument('--token', '--api-key', dest='api_key', help='The Ansible Galaxy API key which can be found at ' - 'https://galaxy.ansible.com/me/preferences.') + 'https://galaxy.ansible.com/ui/token.') common.add_argument('-c', '--ignore-certs', action='store_true', dest='ignore_certs', help='Ignore SSL certificate validation errors.', default=None) # --timeout uses the default None to handle two different scenarios. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/config/ansible_builtin_runtime.yml new/ansible_core-2.21.3/lib/ansible/config/ansible_builtin_runtime.yml --- old/ansible_core-2.21.2/lib/ansible/config/ansible_builtin_runtime.yml 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/config/ansible_builtin_runtime.yml 2026-08-10 17:52:54.000000000 +0200 @@ -9140,6 +9140,14 @@ mongodb: redirect: community.mongodb.mongodb callback: + tree: + deprecation: + removal_version: "2.23" + warning_text: "Use another callback plugin, or vendor and/or move the tree callback to a collection." + oneline: + deprecation: + removal_version: "2.23" + warning_text: "Use another callback plugin, or vendor and/or move the oneline callback to a collection." actionable: redirect: community.general.actionable cgroup_memory_recap: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/executor/powershell/exec_wrapper.ps1 new/ansible_core-2.21.3/lib/ansible/executor/powershell/exec_wrapper.ps1 --- old/ansible_core-2.21.2/lib/ansible/executor/powershell/exec_wrapper.ps1 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/executor/powershell/exec_wrapper.ps1 2026-08-10 17:52:54.000000000 +0200 @@ -82,6 +82,7 @@ $null = $PSBoundParameters.Remove('PwshPath') $targetPwsh = Get-Command -Name $PwshPath -CommandType Application -ErrorAction Ignore | + Select-Object -First 1 | ForEach-Object { [Path]::GetFullPath($_.Path) } if (-not $targetPwsh) { @{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/galaxy/collection/concrete_artifact_manager.py new/ansible_core-2.21.3/lib/ansible/galaxy/collection/concrete_artifact_manager.py --- old/ansible_core-2.21.2/lib/ansible/galaxy/collection/concrete_artifact_manager.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/galaxy/collection/concrete_artifact_manager.py 2026-08-10 17:52:54.000000000 +0200 @@ -17,6 +17,7 @@ from functools import cache from hashlib import sha256 from pathlib import Path +from http.client import IncompleteRead, HTTPResponse from urllib.error import URLError from urllib.parse import urldefrag from shutil import rmtree @@ -509,7 +510,10 @@ ) with open(b_file_path, 'wb') as download_file: # type: t.BinaryIO - actual_hash = _consume_file(resp, write_to=download_file) + try: + actual_hash = _consume_file(resp, write_to=download_file) + except IncompleteRead as orig_exc: + raise AnsibleError(f"Downloading {url} failed") from orig_exc if expected_hash: display.vvvv( @@ -523,17 +527,29 @@ return b_file_path -def _consume_file(read_from, write_to=None): - # type: (t.BinaryIO, t.BinaryIO) -> str +def _consume_file(read_from: t.BinaryIO | HTTPResponse, write_to: t.BinaryIO | None = None) -> str: bufsize = 65536 sha256_digest = sha256() - data = read_from.read(bufsize) - while data: + length = getattr(read_from, 'length', None) + actual_length = 0 + remaining = None + while True: + if isinstance(length, int) and (remaining := length - actual_length) <= 0: + break + + if not (data := read_from.read(min(bufsize, remaining or bufsize))): + break + if write_to is not None: write_to.write(data) write_to.flush() + sha256_digest.update(data) - data = read_from.read(bufsize) + actual_length += len(data) + + if isinstance(length, int) and length > actual_length: + read_len = b' ' * actual_length if actual_length else b'' + raise IncompleteRead(read_len, length - actual_length) return sha256_digest.hexdigest() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/module_utils/ansible_release.py new/ansible_core-2.21.3/lib/ansible/module_utils/ansible_release.py --- old/ansible_core-2.21.2/lib/ansible/module_utils/ansible_release.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/module_utils/ansible_release.py 2026-08-10 17:52:54.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.21.2' +__version__ = '2.21.3' __author__ = 'Ansible, Inc.' __codename__ = "The Rain Song" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/modules/rpm_key.py new/ansible_core-2.21.3/lib/ansible/modules/rpm_key.py --- old/ansible_core-2.21.2/lib/ansible/modules/rpm_key.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/modules/rpm_key.py 2026-08-10 17:52:54.000000000 +0200 @@ -161,6 +161,9 @@ pkt = ctypes.POINTER(ctypes.c_uint8)() pktlen = ctypes.c_size_t() + if not armor.endswith("\n"): + armor += "\n" + armor_bytes = armor.encode() result = self._lib.pgpParsePkts(armor_bytes, ctypes.byref(pkt), ctypes.byref(pktlen)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/plugins/callback/oneline.py new/ansible_core-2.21.3/lib/ansible/plugins/callback/oneline.py --- old/ansible_core-2.21.2/lib/ansible/plugins/callback/oneline.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/plugins/callback/oneline.py 2026-08-10 17:52:54.000000000 +0200 @@ -11,13 +11,16 @@ version_added: historical description: - This is the output callback used by the C(-o)/C(--one-line) command line option. + deprecated: + why: The P(ansible.builtin.oneline#callback) callback is no longer recommended due to long term instability. + alternatives: Use a stable callback with test coverage, or consider vendoring and/or adding this plugin to a collection. + removed_in: "2.23" """ from ansible import constants as C from ansible.plugins.callback import CallbackBase from ansible.template import Templar from ansible.executor.task_result import CallbackTaskResult -from ansible.module_utils._internal import _deprecator class CallbackModule(CallbackBase): @@ -31,15 +34,6 @@ CALLBACK_TYPE = 'stdout' CALLBACK_NAME = 'oneline' - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._display.deprecated( # pylint: disable=ansible-deprecated-unnecessary-collection-name - msg='The oneline callback plugin is deprecated.', - version='2.23', - deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR, # entire plugin being removed; this improves the messaging - ) - def _command_generic_msg(self, hostname, result, caption): stdout = result.get('stdout', '').replace('\n', '\\n').replace('\r', '\\r') if 'stderr' in result and result['stderr']: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/plugins/callback/tree.py new/ansible_core-2.21.3/lib/ansible/plugins/callback/tree.py --- old/ansible_core-2.21.2/lib/ansible/plugins/callback/tree.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/plugins/callback/tree.py 2026-08-10 17:52:54.000000000 +0200 @@ -25,6 +25,10 @@ description: - "This callback is used by the Ansible (adhoc) command line option C(-t|--tree)." - This produces a JSON dump of events in a directory, a file for each host, the directory used MUST be passed as a command line option. + deprecated: + why: The P(ansible.builtin.tree#callback) callback is no longer recommended due to long term instability. + alternatives: Use a stable callback with test coverage, or consider vendoring and/or moving this plugin to a collection. + removed_in: "2.23" """ import os @@ -34,7 +38,6 @@ from ansible.module_utils.common.text.converters import to_bytes from ansible.plugins.callback import CallbackBase from ansible.utils.path import makedirs_safe, unfrackpath -from ansible.module_utils._internal import _deprecator class CallbackModule(CallbackBase): @@ -47,15 +50,6 @@ CALLBACK_NAME = 'tree' CALLBACK_NEEDS_ENABLED = True - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self._display.deprecated( # pylint: disable=ansible-deprecated-unnecessary-collection-name - msg='The tree callback plugin is deprecated.', - version='2.23', - deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR, # entire plugin being removed; this improves the messaging - ) - def set_options(self, task_keys=None, var_options=None, direct=None): """ override to set self.tree """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/release.py new/ansible_core-2.21.3/lib/ansible/release.py --- old/ansible_core-2.21.2/lib/ansible/release.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/release.py 2026-08-10 17:52:54.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.21.2' +__version__ = '2.21.3' __author__ = 'Ansible, Inc.' __codename__ = "The Rain Song" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/lib/ansible/utils/collection_loader/_collection_finder.py new/ansible_core-2.21.3/lib/ansible/utils/collection_loader/_collection_finder.py --- old/ansible_core-2.21.2/lib/ansible/utils/collection_loader/_collection_finder.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/lib/ansible/utils/collection_loader/_collection_finder.py 2026-08-10 17:52:54.000000000 +0200 @@ -6,6 +6,7 @@ from __future__ import annotations +import inspect import itertools import os import os.path @@ -1237,23 +1238,33 @@ prefix = _to_text(prefix) # yield (module_loader, name, ispkg) for each module/pkg under path # TODO: implement ignore/silent catch for unreadable? - for b_path in map(_to_bytes, paths): - if not os.path.isdir(b_path): + # Mostly based off the logic in the builtin pkgutil.iter_modules importer. + # Only difference is we don't check if dirs contains __init__. + # https://github.com/python/cpython/blob/fda056e64bdfcac3dd3d13eebda0a24994d83cb8/Lib/pkgutil.py#L130-L168 + for path in paths: + if not os.path.isdir(path): continue - for b_basename in sorted(os.listdir(b_path)): - b_candidate_module_path = os.path.join(b_path, b_basename) - if os.path.isdir(b_candidate_module_path): + + yielded = {'__init__'} + for basename in sorted(os.listdir(path)): + modname = inspect.getmodulename(basename) + if modname in yielded: + continue + + mod_path = os.path.join(path, basename) + ispkg = False + if not modname and os.path.isdir(mod_path) and '.' not in basename: # exclude things that obviously aren't Python package dirs # FIXME: this dir is adjustable in py3.8+, check for it - if b'.' in b_basename or b_basename == b'__pycache__': + if basename == '__pycache__': continue - # TODO: proper string handling? - yield prefix + _to_text(b_basename), True - else: - # FIXME: match builtin ordering for package/dir/file, support compiled? - if b_basename.endswith(b'.py') and b_basename != b'__init__.py': - yield prefix + _to_text(os.path.splitext(b_basename)[0]), False + modname = basename + ispkg = True + + if modname and '.' not in modname: + yielded.add(modname) + yield prefix + modname, ispkg def _get_collection_metadata(collection_name): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/pyproject.toml new/ansible_core-2.21.3/pyproject.toml --- old/ansible_core-2.21.2/pyproject.toml 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/pyproject.toml 2026-08-10 17:52:54.000000000 +0200 @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools >= 77.0.3, <= 83.0.0"] # lower bound to support license/license-files (PEP 639), upper bound for latest version tested at release +requires = ["setuptools >= 77.0.3, <= 84.0.0"] # lower bound to support license/license-files (PEP 639), upper bound for latest version tested at release build-backend = "setuptools.build_meta" [project] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/async/tasks/main.yml new/ansible_core-2.21.3/test/integration/targets/async/tasks/main.yml --- old/ansible_core-2.21.2/test/integration/targets/async/tasks/main.yml 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/test/integration/targets/async/tasks/main.yml 2026-08-10 17:52:54.000000000 +0200 @@ -287,7 +287,7 @@ - name: Test that async has stdin command: > {{ ansible_python_interpreter|default('/usr/bin/python') }} -c 'import os; os.fdopen(os.dup(0), "r")' - async: 1 + async: 5 poll: 1 - name: run async poll callback test playbook @@ -314,6 +314,7 @@ - name: test 'hidden' async, gather_facts does internal on parallel true. Ensure it won't freeze in check_mode due to async_status not supporting it. gather_facts: parallel: true + gather_timeout: 10 timeout: 60 check_mode: true when: "ansible_facts['os_family'] != 'Darwin'" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/collection_loader/aliases new/ansible_core-2.21.3/test/integration/targets/collection_loader/aliases --- old/ansible_core-2.21.2/test/integration/targets/collection_loader/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.21.3/test/integration/targets/collection_loader/aliases 2026-08-10 17:52:54.000000000 +0200 @@ -0,0 +1,3 @@ +shippable/posix/group3 +context/controller +needs/target/collection diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py new/ansible_core-2.21.3/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py --- old/ansible_core-2.21.2/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.21.3/test/integration/targets/collection_loader/ansible_collections/ns/col/plugins/action/test_collection_loader.py 2026-08-10 17:52:54.000000000 +0200 @@ -0,0 +1,13 @@ +from __future__ import annotations + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + def run(self, tmp=None, task_vars=None): + import my_module + + result = super(ActionModule, self).run(tmp, task_vars) + result['modules'] = my_module.run() + + return result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py new/ansible_core-2.21.3/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py --- old/ansible_core-2.21.2/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.21.3/test/integration/targets/collection_loader/ansible_collections/ns/col/temp_pythonpath/my_module/__init__.py 2026-08-10 17:52:54.000000000 +0200 @@ -0,0 +1,9 @@ +from __future__ import annotations + +import pkgutil + +from my_module import sub + + +def run() -> list[str]: + return [m[1] for m in pkgutil.iter_modules(sub.__path__)] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/collection_loader/main.yml new/ansible_core-2.21.3/test/integration/targets/collection_loader/main.yml --- old/ansible_core-2.21.2/test/integration/targets/collection_loader/main.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.21.3/test/integration/targets/collection_loader/main.yml 2026-08-10 17:52:54.000000000 +0200 @@ -0,0 +1,11 @@ +- name: run collection loader tests + hosts: localhost + tasks: + - name: run module which imports Python library inside collection dir + ns.col.test_collection_loader: + register: result + + - name: assert module loader results + assert: + that: + - result.modules == ['foo'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/collection_loader/runme.sh new/ansible_core-2.21.3/test/integration/targets/collection_loader/runme.sh --- old/ansible_core-2.21.2/test/integration/targets/collection_loader/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.21.3/test/integration/targets/collection_loader/runme.sh 2026-08-10 17:52:54.000000000 +0200 @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eux + +PLAYBOOK_DIR="${PWD}" + +source ../collection/setup.sh + +EXT=$( python -c 'import importlib.machinery as i; print(next(iter([s for s in i.all_suffixes() if s != ".py"])))' ) +touch "temp_pythonpath/my_module/sub/foo${EXT}" + +ANSIBLE_COLLECTIONS_PATH="${PWD}../../../" \ + ANSIBLE_LOCALHOST_WARNING=false \ + PYTHONPATH="./temp_pythonpath:${PYTHONPATH}" \ + ansible-playbook "${PLAYBOOK_DIR}/main.yml" "$@" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/powershell_exec_wrapper/tasks/windows.yml new/ansible_core-2.21.3/test/integration/targets/powershell_exec_wrapper/tasks/windows.yml --- old/ansible_core-2.21.2/test/integration/targets/powershell_exec_wrapper/tasks/windows.yml 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/test/integration/targets/powershell_exec_wrapper/tasks/windows.yml 2026-08-10 17:52:54.000000000 +0200 @@ -159,3 +159,64 @@ failed_when: >- iso8601_date.value != '2040-10-27T14:30:00' or iso8601_date.value_type != 'System.String' + +# https://github.com/ansible/ansible/issues/87228 +- name: Prepend 32-bit PATH for interpreter check + ansible.windows.win_powershell: + script: | + $ErrorActionPreference = 'Stop' + + $keyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $sysWowPowerShell = 'C:\Windows\SysWow64\WindowsPowerShell\v1.0' + + $key = Get-Item -LiteralPath $keyPath + $rawPath = $key.GetValue('PATH', '', 'DoNotExpandEnvironmentNames') + $key.Dispose() + + [System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent($rawPath) + + $keyParams = @{ + Path = $keyPath + Name = 'PATH' + Value = "$sysWowPowerShell;$rawPath" + PropertyType = 'ExpandString' + Force = $true + Confirm = $false + } + New-ItemProperty @keyParams | Out-Null + register: path_info + +- name: Set reset PowerShell script to restore PATH to original value + set_fact: + reset_path_command: | + $ErrorActionPreference = 'Stop' + + $keyPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $rawPath = '{{ path_info.output[0] }}' + + $keyParams = @{ + Path = $keyPath + Name = 'PATH' + Value = $rawPath + PropertyType = 'ExpandString' + Force = $true + Confirm = $false + } + New-ItemProperty @keyParams | Out-Null + +- block: + - name: Reset connection to ensure refreshed env vars + meta: reset_connection + + - name: Test that multiple PATH entries continue to work + get_info: + + always: + # We use raw as we won't know if setting the PATH broke everything or not. + - name: Reset PATH back to the original value + raw: >- + C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe + -EncodedCommand {{ reset_path_command | b64encode(encoding='utf-16-le')}} + + - name: Reset connection to after resetting env vars + meta: reset_connection diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/integration/targets/rpm_key/tasks/rpm_key.yaml new/ansible_core-2.21.3/test/integration/targets/rpm_key/tasks/rpm_key.yaml --- old/ansible_core-2.21.2/test/integration/targets/rpm_key/tasks/rpm_key.yaml 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/test/integration/targets/rpm_key/tasks/rpm_key.yaml 2026-08-10 17:52:54.000000000 +0200 @@ -238,6 +238,42 @@ - result is success - result is not changed +# Test armor without trailing newline (https://github.com/ansible/ansible/issues/87303). +# Note that this particular issue is very dependent on the version of librpm installed. +# Tolerancy of a missing newline varies across older versions, but we add this specific +# test to validate our handling of the missing trailing newline. +- name: Remove all keys from key ring + shell: rpm -q gpg-pubkey | xargs rpm -e + +- name: Read test key content + slurp: + src: "{{ test_key_path }}" + register: test_key_content + +- name: Write key file without trailing newline + copy: + content: "{{ (test_key_content['content'] | b64decode).rstrip('\n') }}" + dest: "{{ remote_tmp_dir }}/key_no_newline.asc" + mode: "0644" + +- name: Verify key file has no trailing newline + shell: test "$(tail -c 1 {{ remote_tmp_dir }}/key_no_newline.asc)" != "" + +- name: Import key without trailing newline + rpm_key: + state: present + key: "{{ remote_tmp_dir }}/key_no_newline.asc" + +- name: Import key without trailing newline (idempotent) + rpm_key: + state: present + key: "{{ remote_tmp_dir }}/key_no_newline.asc" + register: no_newline_idempotence + +- name: Verify idempotence + assert: + that: no_newline_idempotence is not changed + # Test PGPv6 keys - name: Test PGPv6 Keys block: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/lib/ansible_test/_internal/host_configs.py new/ansible_core-2.21.3/test/lib/ansible_test/_internal/host_configs.py --- old/ansible_core-2.21.2/test/lib/ansible_test/_internal/host_configs.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/test/lib/ansible_test/_internal/host_configs.py 2026-08-10 17:52:54.000000000 +0200 @@ -420,7 +420,9 @@ super().apply_defaults(context, defaults) self.become = self.become or defaults.become - self.name = defaults.name + + if not defaults.is_default: + self.name = defaults.name @property def have_root(self) -> bool: @@ -451,7 +453,9 @@ super().apply_defaults(context, defaults) self.connection = self.connection or defaults.connection - self.name = defaults.name + + if not defaults.is_default: + self.name = defaults.name @dataclasses.dataclass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/units/cli/test_galaxy.py new/ansible_core-2.21.3/test/units/cli/test_galaxy.py --- old/ansible_core-2.21.2/test/units/cli/test_galaxy.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/test/units/cli/test_galaxy.py 2026-08-10 17:52:54.000000000 +0200 @@ -918,10 +918,21 @@ assert mock_install.call_args[0][6] is False # force_deps -def test_collection_install_with_url(monkeypatch, collection_install): +def test_collection_install_with_url( + monkeypatch: pytest.MonkeyPatch, + collection_install: tuple[MagicMock, MagicMock, str] +) -> None: mock_install, dummy, output_dir = collection_install - mock_open = MagicMock(return_value=BytesIO()) + class MockHTTPResponse: + def __init__(self) -> None: + self._stream = BytesIO() + self.length = 'UNKNOWN' + + def read(self, size: int = -1) -> bytes: + return self._stream.read(size) + + mock_open = MagicMock(return_value=MockHTTPResponse()) monkeypatch.setattr(collection.concrete_artifact_manager, 'open_url', mock_open) mock_metadata = MagicMock(return_value={'namespace': 'foo', 'name': 'bar', 'version': 'v1.0.0'}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.21.2/test/units/galaxy/test_collection.py new/ansible_core-2.21.3/test/units/galaxy/test_collection.py --- old/ansible_core-2.21.2/test/units/galaxy/test_collection.py 2026-07-13 21:37:14.000000000 +0200 +++ new/ansible_core-2.21.3/test/units/galaxy/test_collection.py 2026-08-10 17:52:54.000000000 +0200 @@ -890,7 +890,26 @@ % galaxy_server.api_server -def test_download_file(tmp_path_factory, monkeypatch): +class MockHTTPResponses: + def __init__(self, responses: list[tuple[bytes, int | None]]) -> None: + self._body, self.length = responses[0] + self._responses = responses[1:] + self._stream = BytesIO(self._body) + + def read(self, size: int = -1) -> bytes: + data = self._stream.read(size) + + if not data: + # reset the stream to simulate retries + if self._responses: + self._body, self.length = self._responses[0] + del self._responses[0] + self._stream = BytesIO(self._body) + + return data + + +def test_download_file(tmp_path_factory: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch) -> None: temp_dir = to_bytes(tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections')) data = b"\x00\x01\x02\x03" @@ -898,7 +917,7 @@ sha256_hash.update(data) mock_open = MagicMock() - mock_open.return_value = BytesIO(data) + mock_open.return_value = MockHTTPResponses([(data, len(data))]) monkeypatch.setattr(collection.concrete_artifact_manager, 'open_url', mock_open) expected = temp_dir @@ -913,13 +932,13 @@ assert mock_open.mock_calls[0][1][0] == 'http://google.com/file' -def test_download_file_hash_mismatch(tmp_path_factory, monkeypatch): +def test_download_file_hash_mismatch(tmp_path_factory: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch) -> None: temp_dir = to_bytes(tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections')) data = b"\x00\x01\x02\x03" mock_open = MagicMock() - mock_open.return_value = BytesIO(data) + mock_open.return_value = MockHTTPResponses([(data, len(data))]) monkeypatch.setattr(collection.concrete_artifact_manager, 'open_url', mock_open) expected = "Mismatch artifact hash with downloaded file" @@ -927,6 +946,35 @@ collection._download_file('http://google.com/file', temp_dir, 'bad', True) +def test_download_file_incomplete_read( + tmp_path_factory: pytest.TempPathFactory, + monkeypatch: pytest.MonkeyPatch +) -> None: + temp_dir = to_bytes(tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections')) + + incomplete_data = b"\x00\x01\x02\x03" + data = incomplete_data + b"\x04" + sha256_hash = sha256(data) + + # Test retries succeed + responses = [(incomplete_data, len(data)), (data, len(data))] + mock_open = MagicMock(return_value=MockHTTPResponses(responses)) + monkeypatch.setattr(collection.concrete_artifact_manager, 'open_url', mock_open) + + collection._download_file('http://google.com/file', temp_dir, sha256_hash.hexdigest(), True) + + # Test error is correct + mock_open.return_value = MockHTTPResponses([(data, len(data) + 1)]) + monkeypatch.setattr(collection.concrete_artifact_manager, 'open_url', mock_open) + + expected_error = re.escape( + "Downloading http://google.com/file failed: " + "IncompleteRead(5 bytes read, 1 more expected)" + ) + with pytest.raises(AnsibleError, match=expected_error): + collection._download_file('http://google.com/file', temp_dir, sha256_hash.hexdigest(), True) + + def test_extract_tar_file_invalid_hash(tmp_tarfile): temp_dir, tfile, filename, dummy = tmp_tarfile ++++++ ansible_core-2.21.2.tar.gz.sha256 -> ansible_core-2.21.3.tar.gz.sha256 ++++++ --- /work/SRC/openSUSE:Factory/ansible-core/ansible_core-2.21.2.tar.gz.sha256 2026-07-14 13:51:54.043041740 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.17972/ansible_core-2.21.3.tar.gz.sha256 2026-08-11 17:18:12.388768475 +0200 @@ -1 +1 @@ -c6139e662eda1bfb5af16029f12afa5edfc7f95f8cd44281b1bdb434a6d9dc70 ansible_core-2.21.2.tar.gz +4194fbd82273cbacfd06d86d74d2d7168c3c4b8426c03e93562cd7217f811ae1 ansible_core-2.21.3.tar.gz
