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-04-21 12:45:53 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible-core (Old) and /work/SRC/openSUSE:Factory/.ansible-core.new.11940 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible-core" Tue Apr 21 12:45:53 2026 rev:55 rq:1348370 version:2.20.5 Changes: -------- --- /work/SRC/openSUSE:Factory/ansible-core/ansible-core.changes 2026-03-27 06:35:58.620519745 +0100 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.11940/ansible-core.changes 2026-04-21 12:47:41.133554471 +0200 @@ -1,0 +2,26 @@ +Tue Apr 21 06:09:07 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 2.20.5: + https://github.com/ansible/ansible/blob/v2.20.5/changelogs/CHANGELOG-v2.20.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 + - Fix validate_argspec when tags are defined on the play. The + always tag is only added if the play has no tags. + - --start-at-task - fix starting at the requested task instead + of starting at the next block or play. Play level tasks run + first. (#86268) + - ansible-galaxy collection - Fix using the server + configuration for validate_certs when downloading + collections. (#86694) + - ansible_facts[os_*] - Contained wrong information, if + ClearLinux parsing was tried before falling back to general + os-release parsing + - templating - Fix traceback when using deepcopy on an imported + template (#86723). + +------------------------------------------------------------------- @@ -4,0 +31 @@ + https://github.com/ansible/ansible/blob/v2.20.4/changelogs/CHANGELOG-v2.20.rst Old: ---- ansible_core-2.20.4.tar.gz ansible_core-2.20.4.tar.gz.sha256 New: ---- ansible_core-2.20.5.tar.gz ansible_core-2.20.5.tar.gz.sha256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible-core.spec ++++++ --- /var/tmp/diff_new_pack.Az5NXc/_old 2026-04-21 12:47:43.005632126 +0200 +++ /var/tmp/diff_new_pack.Az5NXc/_new 2026-04-21 12:47:43.017632624 +0200 @@ -43,7 +43,7 @@ %endif Name: ansible-core -Version: 2.20.4 +Version: 2.20.5 Release: 0 Summary: Radically simple IT automation License: GPL-3.0-or-later ++++++ ansible_core-2.20.4.tar.gz -> ansible_core-2.20.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/PKG-INFO new/ansible_core-2.20.5/PKG-INFO --- old/ansible_core-2.20.4/PKG-INFO 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/PKG-INFO 2026-04-21 02:44:49.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.20.4 +Version: 2.20.5 Summary: Radically simple IT automation Author: Ansible Project Project-URL: Homepage, https://ansible.com/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/ansible_core.egg-info/PKG-INFO new/ansible_core-2.20.5/ansible_core.egg-info/PKG-INFO --- old/ansible_core-2.20.4/ansible_core.egg-info/PKG-INFO 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/ansible_core.egg-info/PKG-INFO 2026-04-21 02:44:49.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.20.4 +Version: 2.20.5 Summary: Radically simple IT automation Author: Ansible Project Project-URL: Homepage, https://ansible.com/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/ansible_core.egg-info/SOURCES.txt new/ansible_core-2.20.5/ansible_core.egg-info/SOURCES.txt --- old/ansible_core-2.20.4/ansible_core.egg-info/SOURCES.txt 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/ansible_core.egg-info/SOURCES.txt 2026-04-21 02:44:49.000000000 +0200 @@ -1173,6 +1173,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 @@ -3439,6 +3441,7 @@ test/integration/targets/play_arg_spec/playbooks/validate_facts.yml test/integration/targets/play_arg_spec/tasks/main.yml test/integration/targets/play_iterator/aliases +test/integration/targets/play_iterator/playbook.meta.yml test/integration/targets/play_iterator/playbook.yml test/integration/targets/play_iterator/runme.sh test/integration/targets/playbook/aliases @@ -5220,6 +5223,7 @@ test/units/module_utils/facts/system/distribution/fixtures/fedora_25.json test/units/module_utils/facts/system/distribution/fixtures/fedora_31.json test/units/module_utils/facts/system/distribution/fixtures/flatcar_3139.2.0.json +test/units/module_utils/facts/system/distribution/fixtures/gentoo.json test/units/module_utils/facts/system/distribution/fixtures/kali_2019.1.json test/units/module_utils/facts/system/distribution/fixtures/kde_neon_16.04.json test/units/module_utils/facts/system/distribution/fixtures/kylin_linux_advanced_server_v10.json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/changelogs/CHANGELOG-v2.20.rst new/ansible_core-2.20.5/changelogs/CHANGELOG-v2.20.rst --- old/ansible_core-2.20.4/changelogs/CHANGELOG-v2.20.rst 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/changelogs/CHANGELOG-v2.20.rst 2026-04-21 02:44:49.000000000 +0200 @@ -4,6 +4,31 @@ .. contents:: Topics +v2.20.5 +======= + +Release Summary +--------------- + +| Release Date: 2026-04-20 +| `Porting Guide <https://docs.ansible.com/ansible-core/2.20/porting_guides/porting_guide_core_2.20.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 +-------- + +- Fix ``validate_argspec`` when tags are defined on the play. The ``always`` tag is only added if the play has no tags. +- ``--start-at-task`` - fix starting at the requested task instead of starting at the next block or play. Play level tasks run first. (https://github.com/ansible/ansible/issues/86268) +- ansible-galaxy collection - Fix using the server configuration for ``validate_certs`` when downloading collections. (https://github.com/ansible/ansible/issues/86694) +- ansible_facts[os_*] - Contained wrong information, if ClearLinux parsing was tried before falling back to general os-release parsing +- templating - Fix traceback when using ``deepcopy`` on an imported template (https://github.com/ansible/ansible/issues/86723). + v2.20.4 ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/changelogs/changelog.yaml new/ansible_core-2.20.5/changelogs/changelog.yaml --- old/ansible_core-2.20.4/changelogs/changelog.yaml 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/changelogs/changelog.yaml 2026-04-21 02:44:49.000000000 +0200 @@ -575,3 +575,47 @@ - configlookupfix.yml - pyyaml-name.yml release_date: '2026-03-16' + 2.20.5: + changes: + release_summary: '| Release Date: 2026-04-20 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.20/porting_guides/porting_guide_core_2.20.html>`__ + + ' + codename: Good Times Bad Times + fragments: + - 2.20.5_summary.yaml + release_date: '2026-04-20' + 2.20.5rc1: + changes: + bugfixes: + - Fix ``validate_argspec`` when tags are defined on the play. The ``always`` + tag is only added if the play has no tags. + - '``--start-at-task`` - fix starting at the requested task instead of starting + at the next block or play. Play level tasks run first. (https://github.com/ansible/ansible/issues/86268)' + - ansible-galaxy collection - Fix using the server configuration for ``validate_certs`` + when downloading collections. (https://github.com/ansible/ansible/issues/86694) + - ansible_facts[os_*] - Contained wrong information, if ClearLinux parsing was + tried before falling back to general os-release parsing + - templating - Fix traceback when using ``deepcopy`` on an imported template + (https://github.com/ansible/ansible/issues/86723). + 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.20/porting_guides/porting_guide_core_2.20.html>`__ + + ' + codename: Good Times Bad Times + fragments: + - 2.20.5rc1_summary.yaml + - ansible-galaxy-validate_certs-server-config.yml + - ansible-template-deepcopy.yml + - ansible-test-dist-info.yml + - ansible-test-macos-aarch64.yml + - clearlinux-gentoo-parsing.yml + - start-at-task-fact-gathering.yml + release_date: '2026-04-13' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/lib/ansible/_internal/_templating/_jinja_bits.py new/ansible_core-2.20.5/lib/ansible/_internal/_templating/_jinja_bits.py --- old/ansible_core-2.20.4/lib/ansible/_internal/_templating/_jinja_bits.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/_internal/_templating/_jinja_bits.py 2026-04-21 02:44:49.000000000 +0200 @@ -332,6 +332,9 @@ def __call__(self, jinja_vars: c.Mapping[str, t.Any]) -> t.Any: return self.render(ArgSmuggler.package_jinja_vars(jinja_vars)) + def __deepcopy__(self, memo): + return self # templates are immutable, so a deep copy is not needed (it would also fail if not implemented here) + # noinspection PyShadowingBuiltins def new_context( self, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/lib/ansible/cli/galaxy.py new/ansible_core-2.20.5/lib/ansible/cli/galaxy.py --- old/ansible_core-2.20.4/lib/ansible/cli/galaxy.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/cli/galaxy.py 2026-04-21 02:44:49.000000000 +0200 @@ -78,8 +78,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.20.4/lib/ansible/executor/play_iterator.py new/ansible_core-2.20.5/lib/ansible/executor/play_iterator.py --- old/ansible_core-2.20.4/lib/ansible/executor/play_iterator.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/executor/play_iterator.py 2026-04-21 02:44:49.000000000 +0200 @@ -42,6 +42,7 @@ ALWAYS = 3 HANDLERS = 4 COMPLETE = 5 + VALIDATE = 6 class FailedStates(IntFlag): @@ -51,6 +52,7 @@ RESCUE = 4 ALWAYS = 8 HANDLERS = 16 # NOTE not in use anymore + VALIDATE = 32 class HostState: @@ -69,7 +71,6 @@ self.fail_state = FailedStates.NONE self.pre_flushing_run_state = None self.update_handlers = True - self.pending_setup = False self.tasks_child_state = None self.rescue_child_state = None self.always_child_state = None @@ -81,7 +82,7 @@ def __str__(self): return ("HOST STATE: block=%d, task=%d, rescue=%d, always=%d, handlers=%d, run_state=%s, fail_state=%s, " - "pre_flushing_run_state=%s, update_handlers=%s, pending_setup=%s, " + "pre_flushing_run_state=%s, update_handlers=%s, " "tasks child state? (%s), rescue child state? (%s), always child state? (%s), " "did rescue? %s, did start at task? %s" % ( self.cur_block, @@ -93,7 +94,6 @@ self.fail_state, self.pre_flushing_run_state, self.update_handlers, - self.pending_setup, self.tasks_child_state, self.rescue_child_state, self.always_child_state, @@ -107,7 +107,7 @@ for attr in ('_blocks', 'cur_block', 'cur_regular_task', 'cur_rescue_task', 'cur_always_task', 'cur_handlers_task', - 'run_state', 'fail_state', 'pre_flushing_run_state', 'update_handlers', 'pending_setup', + 'run_state', 'fail_state', 'pre_flushing_run_state', 'update_handlers', 'tasks_child_state', 'rescue_child_state', 'always_child_state'): if getattr(self, attr) != getattr(other, attr): return False @@ -130,7 +130,6 @@ new_state.fail_state = self.fail_state new_state.pre_flushing_run_state = self.pre_flushing_run_state new_state.update_handlers = self.update_handlers - new_state.pending_setup = self.pending_setup new_state.did_rescue = self.did_rescue new_state.did_start_at_task = self.did_start_at_task if self.tasks_child_state is not None: @@ -187,7 +186,7 @@ 'path': self._play._metadata_path, }, }, - 'tags': ['always'], + 'tags': ['always'] if not self._play.tags else [], }, block=setup_block) validation_task.set_loader(self._play._loader) @@ -292,51 +291,37 @@ return (state, None) if state.run_state == IteratingStates.SETUP: - # First, we check to see if we completed both setup tasks injected - # during play compilation in __init__ above. - # If not, below we will determine if we do in fact want to gather - # facts or validate arguments for the specified host. - state.pending_setup = state.cur_regular_task < len(block.block) - if state.pending_setup: - task = block.block[state.cur_regular_task] - - # Gather facts if the default is 'smart' and we have not yet - # done it for this host; or if 'explicit' and the play sets - # gather_facts to True; or if 'implicit' and the play does - # NOT explicitly set gather_facts to False. - gather_facts = bool(state.cur_regular_task == 0) - gathering = C.DEFAULT_GATHERING - implied = self._play.gather_facts is None or boolean(self._play.gather_facts, strict=False) - - if gather_facts and not ( - (gathering == 'implicit' and implied) or - (gathering == 'explicit' and boolean(self._play.gather_facts, strict=False)) or - (gathering == 'smart' and implied and not self._variable_manager._facts_gathered_for_host(host.name)) - ): - task = None - elif not gather_facts and not self._play.validate_argspec: - task = None + # Gather facts if the default is 'smart' and we have not yet + # done it for this host; or if 'explicit' and the play sets + # gather_facts to True; or if 'implicit' and the play does + # NOT explicitly set gather_facts to False. + gather_facts = len(self._blocks[0].block) >= 1 + gathering = C.DEFAULT_GATHERING + implied = self._play.gather_facts is None or boolean(self._play.gather_facts, strict=False) + if gather_facts and ( + (gathering == 'implicit' and implied) or + (gathering == 'explicit' and boolean(self._play.gather_facts, strict=False)) or + (gathering == 'smart' and implied and not self._variable_manager._facts_gathered_for_host(host.name)) + ): + task = self._blocks[0].block[0] - state.cur_regular_task += 1 - else: - # This is the last trip through IteratingStates.SETUP, so we - # move onto the next block in the list while setting the run - # state to IteratingStates.TASKS - state.run_state = IteratingStates.TASKS - if not state.did_start_at_task: - state.cur_block += 1 - state.cur_regular_task = 0 - state.cur_rescue_task = 0 - state.cur_always_task = 0 - state.tasks_child_state = None - state.rescue_child_state = None - state.always_child_state = None + state.run_state = IteratingStates.VALIDATE - elif state.run_state == IteratingStates.TASKS: - # clear the pending setup flag, since we're past that and it didn't fail - if state.pending_setup: - state.pending_setup = False + elif state.run_state == IteratingStates.VALIDATE: + if len(self._blocks[0].block) >= 2 and self._play.validate_argspec: + task = self._blocks[0].block[1] + + state.run_state = IteratingStates.TASKS + if not state.did_start_at_task: + state.cur_block += 1 + state.cur_regular_task = 0 + state.cur_rescue_task = 0 + state.cur_always_task = 0 + state.tasks_child_state = None + state.rescue_child_state = None + state.always_child_state = None + elif state.run_state == IteratingStates.TASKS: # First, we check for a child task state that is not failed, and if we # have one recurse into it for the next task. If we're done with the child # state, we clear it and drop back to getting the next task from the list. @@ -496,6 +481,9 @@ if state.run_state == IteratingStates.SETUP: state.fail_state |= FailedStates.SETUP state.run_state = IteratingStates.COMPLETE + elif state.run_state == IteratingStates.VALIDATE: + state.fail_state |= FailedStates.VALIDATE + state.run_state = IteratingStates.COMPLETE elif state.run_state == IteratingStates.TASKS: if state.tasks_child_state is not None: state.tasks_child_state = self._set_failed_state(state.tasks_child_state) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/lib/ansible/galaxy/collection/concrete_artifact_manager.py new/ansible_core-2.20.5/lib/ansible/galaxy/collection/concrete_artifact_manager.py --- old/ansible_core-2.20.4/lib/ansible/galaxy/collection/concrete_artifact_manager.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/galaxy/collection/concrete_artifact_manager.py 2026-04-21 02:44:49.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.20.4/lib/ansible/galaxy/collection/galaxy_api_proxy.py new/ansible_core-2.20.5/lib/ansible/galaxy/collection/galaxy_api_proxy.py --- old/ansible_core-2.20.4/lib/ansible/galaxy/collection/galaxy_api_proxy.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/galaxy/collection/galaxy_api_proxy.py 2026-04-21 02:44:49.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.20.4/lib/ansible/module_utils/ansible_release.py new/ansible_core-2.20.5/lib/ansible/module_utils/ansible_release.py --- old/ansible_core-2.20.4/lib/ansible/module_utils/ansible_release.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/module_utils/ansible_release.py 2026-04-21 02:44:49.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.20.4' +__version__ = '2.20.5' __author__ = 'Ansible, Inc.' __codename__ = "Good Times Bad Times" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/lib/ansible/module_utils/facts/system/distribution.py new/ansible_core-2.20.5/lib/ansible/module_utils/facts/system/distribution.py --- old/ansible_core-2.20.4/lib/ansible/module_utils/facts/system/distribution.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/module_utils/facts/system/distribution.py 2026-04-21 02:44:49.000000000 +0200 @@ -438,10 +438,10 @@ for line in data.splitlines(): distribution = re.search("^NAME=(.*)", line) if distribution and name == 'NA': - na_facts['distribution'] = distribution.group(1).strip('"') + na_facts['distribution'] = distribution.group(1).strip(DistributionFiles.STRIP_QUOTES) version = re.search("^VERSION=(.*)", line) if version and collected_facts['distribution_version'] == 'NA': - na_facts['distribution_version'] = version.group(1).strip('"') + na_facts['distribution_version'] = version.group(1).strip(DistributionFiles.STRIP_QUOTES) return True, na_facts def parse_distribution_file_Coreos(self, name, data, path, collected_facts): @@ -489,6 +489,8 @@ if 'Clear Linux' not in pname.groups()[0]: return False, clear_facts clear_facts['distribution'] = pname.groups()[0] + else: + return False, clear_facts version = re.search('VERSION_ID=(.*)', data) if version: clear_facts['distribution_major_version'] = version.groups()[0] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/lib/ansible/release.py new/ansible_core-2.20.5/lib/ansible/release.py --- old/ansible_core-2.20.4/lib/ansible/release.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/lib/ansible/release.py 2026-04-21 02:44:49.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.20.4' +__version__ = '2.20.5' __author__ = 'Ansible, Inc.' __codename__ = "Good Times Bad Times" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/test/integration/targets/ansible-test-cloud-azure/aliases new/ansible_core-2.20.5/test/integration/targets/ansible-test-cloud-azure/aliases --- old/ansible_core-2.20.4/test/integration/targets/ansible-test-cloud-azure/aliases 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/integration/targets/ansible-test-cloud-azure/aliases 2026-04-21 02:44:49.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.20.4/test/integration/targets/ansible-test-metadata/aliases new/ansible_core-2.20.5/test/integration/targets/ansible-test-metadata/aliases --- old/ansible_core-2.20.4/test/integration/targets/ansible-test-metadata/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.5/test/integration/targets/ansible-test-metadata/aliases 2026-04-21 02:44:49.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.20.4/test/integration/targets/ansible-test-metadata/runme.sh new/ansible_core-2.20.5/test/integration/targets/ansible-test-metadata/runme.sh --- old/ansible_core-2.20.4/test/integration/targets/ansible-test-metadata/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.5/test/integration/targets/ansible-test-metadata/runme.sh 2026-04-21 02:44:49.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.20.4/test/integration/targets/play_arg_spec/tasks/main.yml new/ansible_core-2.20.5/test/integration/targets/play_arg_spec/tasks/main.yml --- old/ansible_core-2.20.4/test/integration/targets/play_arg_spec/tasks/main.yml 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/integration/targets/play_arg_spec/tasks/main.yml 2026-04-21 02:44:49.000000000 +0200 @@ -186,12 +186,12 @@ vars: playbook_name: tagged_play -- name: Test validation always runs otherwise - command: ansible-playbook {{ playbook }} --tags task_level_tag -e 'required_str="success"' +- name: Test validation only runs when the play tag runs + command: ansible-playbook {{ playbook }} --tags mismatch vars: playbook_name: tagged_play register: result - assert: that: - - result.stdout is search("Validating arguments against arg spec Tagged Play") + - result.stdout is not search("Validating arguments against arg spec Tagged Play") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/test/integration/targets/play_iterator/playbook.meta.yml new/ansible_core-2.20.5/test/integration/targets/play_iterator/playbook.meta.yml --- old/ansible_core-2.20.4/test/integration/targets/play_iterator/playbook.meta.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.5/test/integration/targets/play_iterator/playbook.meta.yml 2026-04-21 02:44:49.000000000 +0200 @@ -0,0 +1,3 @@ +argument_specs: + test: + options: {} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/test/integration/targets/play_iterator/playbook.yml new/ansible_core-2.20.5/test/integration/targets/play_iterator/playbook.yml --- old/ansible_core-2.20.4/test/integration/targets/play_iterator/playbook.yml 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/integration/targets/play_iterator/playbook.yml 2026-04-21 02:44:49.000000000 +0200 @@ -1,10 +1,15 @@ --- - hosts: localhost - gather_facts: false + gather_facts: "{{ setup | default(False) }}" + validate_argspec: "{{ spec | default(omit) }}" tasks: - - name: - debug: - msg: foo + - name: "task 1" + fail: + - name: "task 2" debug: msg: bar + + - assert: + that: ansible_facts.gather_subset == ["all"] + when: setup | default(False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/test/integration/targets/play_iterator/runme.sh new/ansible_core-2.20.5/test/integration/targets/play_iterator/runme.sh --- old/ansible_core-2.20.4/test/integration/targets/play_iterator/runme.sh 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/integration/targets/play_iterator/runme.sh 2026-04-21 02:44:49.000000000 +0200 @@ -3,3 +3,10 @@ set -eux ansible-playbook playbook.yml --start-at-task 'task 2' "$@" + +ansible-playbook playbook.yml --start-at-task 'task 2' \ + --extra-vars 'setup="{{ True }}" spec="test"' "$@" | tee out.txt + +grep "TASK \[Gathering Facts\]" out.txt +grep "TASK \[Validating arguments against arg spec test\]" out.txt +grep "TASK \[task 2\]" out.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/test/lib/ansible_test/_data/completion/remote.txt new/ansible_core-2.20.5/test/lib/ansible_test/_data/completion/remote.txt --- old/ansible_core-2.20.4/test/lib/ansible_test/_data/completion/remote.txt 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/lib/ansible_test/_data/completion/remote.txt 2026-04-21 02:44:49.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.3 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.20.4/test/lib/ansible_test/_internal/ansible_util.py new/ansible_core-2.20.5/test/lib/ansible_test/_internal/ansible_util.py --- old/ansible_core-2.20.4/test/lib/ansible_test/_internal/ansible_util.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/lib/ansible_test/_internal/ansible_util.py 2026-04-21 02:44:49.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.20.4/test/lib/ansible_test/_internal/commands/integration/__init__.py new/ansible_core-2.20.5/test/lib/ansible_test/_internal/commands/integration/__init__.py --- old/ansible_core-2.20.4/test/lib/ansible_test/_internal/commands/integration/__init__.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/lib/ansible_test/_internal/commands/integration/__init__.py 2026-04-21 02:44:49.000000000 +0200 @@ -59,6 +59,7 @@ from ...util import ( ApplicationError, display, + get_ansible_version, SubprocessError, remove_tree, ) @@ -804,6 +805,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.20.4/test/lib/ansible_test/_internal/constants.py new/ansible_core-2.20.5/test/lib/ansible_test/_internal/constants.py --- old/ansible_core-2.20.4/test/lib/ansible_test/_internal/constants.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/lib/ansible_test/_internal/constants.py 2026-04-21 02:44:49.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.20.4/test/units/_internal/templating/test_jinja_bits.py new/ansible_core-2.20.5/test/units/_internal/templating/test_jinja_bits.py --- old/ansible_core-2.20.4/test/units/_internal/templating/test_jinja_bits.py 2026-03-23 18:38:45.000000000 +0100 +++ new/ansible_core-2.20.5/test/units/_internal/templating/test_jinja_bits.py 2026-04-21 02:44:49.000000000 +0200 @@ -1,5 +1,6 @@ from __future__ import annotations +import copy import pathlib import sys import typing as t @@ -467,3 +468,11 @@ TemplateEngine(variables=dict(adict={})).template(TRUST.tag(template)) assert type(tracker._markers[0]) is UndefinedMarker # pylint: disable=unidiomatic-typecheck + + +def test_ansible_template_deepcopy() -> None: + """Ensure that AnsibleTemplate instances return themselves when a deep copy is made.""" + template = AnsibleEnvironment().from_string("Hello") + deep_copy = copy.deepcopy(template) + + assert deep_copy is template diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.4/test/units/module_utils/facts/system/distribution/fixtures/gentoo.json new/ansible_core-2.20.5/test/units/module_utils/facts/system/distribution/fixtures/gentoo.json --- old/ansible_core-2.20.4/test/units/module_utils/facts/system/distribution/fixtures/gentoo.json 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.5/test/units/module_utils/facts/system/distribution/fixtures/gentoo.json 2026-04-21 02:44:49.000000000 +0200 @@ -0,0 +1,47 @@ +{ + "name": "Gentoo 2.18", + "distro": { + "codename": "n/a", + "id": "gentoo", + "name": "Gentoo", + "version": "2.18", + "version_best": "2.18", + "lsb_release_info": { + "lsb_version": "n/a", + "distributor_id": "Gentoo", + "description": "Gentoo Linux", + "release": "2.18", + "codename": "n/a" + }, + "os_release_info": { + "name": "Gentoo", + "id": "gentoo", + "pretty_name": "Gentoo Linux", + "version": "2.18", + "version_id": "2.18", + "home_url": "https://www.gentoo.org/", + "support_url": "https://www.gentoo.org/support/", + "bug_report_url": "https://bugs.gentoo.org/", + "ansi_color": "1;32" + } + }, + "input": { + "/etc/os-release": "NAME='Gentoo'\nID='gentoo'\nPRETTY_NAME='Gentoo Linux'\nVERSION='2.18'\nVERSION_ID='2.18'\nHOME_URL='https://www.gentoo.org/'\nSUPPORT_URL='https://www.gentoo.org/support/'\nBUG_REPORT_URL='https://bugs.gentoo.org/'\nANSI_COLOR='1;32'\n", + "/etc/gentoo-release": "Gentoo Base System release 2.18\n", + "/etc/lsb-release": "DISTRIB_ID=\"Gentoo\"\n", + "/usr/lib/os-release": "NAME='Gentoo'\nID='gentoo'\nPRETTY_NAME='Gentoo Linux'\nVERSION='2.18'\nVERSION_ID='2.18'\nHOME_URL='https://www.gentoo.org/'\nSUPPORT_URL='https://www.gentoo.org/support/'\nBUG_REPORT_URL='https://bugs.gentoo.org/'\nANSI_COLOR='1;32'\n" + }, + "platform.dist": [ + "gentoo", + "2.18", + "n/a" + ], + "result": { + "distribution": "Gentoo", + "distribution_version": "2.18", + "distribution_release": "n/a", + "distribution_major_version": "2", + "os_family": "Gentoo" + }, + "platform.release": "6.18.2-p1-gentoo-dist" +} ++++++ ansible_core-2.20.4.tar.gz.sha256 -> ansible_core-2.20.5.tar.gz.sha256 ++++++ --- /work/SRC/openSUSE:Factory/ansible-core/ansible_core-2.20.4.tar.gz.sha256 2026-03-27 06:35:58.924532275 +0100 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.11940/ansible_core-2.20.5.tar.gz.sha256 2026-04-21 12:47:41.229558453 +0200 @@ -1 +1 @@ -2060c06195ada0cca0ac3128025d1167f567479da465897d818982fbe28bed1f ansible_core-2.20.4.tar.gz +82e3049d95e6e02e5d20d4a5a8e10533a55e0cc52e878e4cf77166c45410f16f ansible_core-2.20.5.tar.gz
