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 2025-11-06 18:12:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible-core (Old) and /work/SRC/openSUSE:Factory/.ansible-core.new.1980 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible-core" Thu Nov 6 18:12:52 2025 rev:49 rq:1315706 version:2.19.4 Changes: -------- --- /work/SRC/openSUSE:Factory/ansible-core/ansible-core.changes 2025-10-07 18:30:04.021764562 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.1980/ansible-core.changes 2025-11-06 18:13:58.129355750 +0100 @@ -1,0 +2,32 @@ +Wed Nov 5 10:18:14 UTC 2025 - Johannes Kastl <[email protected]> + +- update to 2.19.4: + * Bugfixes + - Fix issue where play tags prevented executing notified + handlers (#85475) + - Fix issues with keywords being incorrectly validated on + import_tasks (#85855, #85856) + - Fix traceback when trying to import non-existing file via + nested import_tasks (#69882) + - SIGINT/SIGTERM Handling - Make SIGINT/SIGTERM handling more + robust by splitting concerns between forks and the parent. + - Windows - ignore temporary file cleanup warning when using + AnsibleModule to compile C# utils. This should reduce the + number of warnings that can safely be ignored when running + PowerShell modules - #85976 + - ansible-doc - prevent crash when scanning collections in + paths that have more than one ansible_collections in it + (#84909, #85361). + - callback plugins - improve consistency accessing the Task + object's resolved_action attribute. + - config lookup now properly factors in variables and + show_origin when checking entries from the global + configuration. + - option argument deprecations now have a proper alternative + help text. + - package_facts - typecast bytes to string while returning + facts (#85937). + - psrp - ReadTimeout exceptions now mark host as unreachable + instead of fatal (#85966) + +------------------------------------------------------------------- Old: ---- ansible_core-2.19.3.tar.gz ansible_core-2.19.3.tar.gz.sha256 New: ---- ansible_core-2.19.4.tar.gz ansible_core-2.19.4.tar.gz.sha256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible-core.spec ++++++ --- /var/tmp/diff_new_pack.J6eueE/_old 2025-11-06 18:13:59.141398735 +0100 +++ /var/tmp/diff_new_pack.J6eueE/_new 2025-11-06 18:13:59.145398905 +0100 @@ -43,7 +43,7 @@ %endif Name: ansible-core -Version: 2.19.3 +Version: 2.19.4 Release: 0 Summary: Radically simple IT automation License: GPL-3.0-or-later ++++++ ansible_core-2.19.3.tar.gz -> ansible_core-2.19.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/PKG-INFO new/ansible_core-2.19.4/PKG-INFO --- old/ansible_core-2.19.3/PKG-INFO 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/PKG-INFO 2025-11-05 00:27:03.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.19.3 +Version: 2.19.4 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.3/ansible_core.egg-info/PKG-INFO new/ansible_core-2.19.4/ansible_core.egg-info/PKG-INFO --- old/ansible_core-2.19.3/ansible_core.egg-info/PKG-INFO 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/ansible_core.egg-info/PKG-INFO 2025-11-05 00:27:03.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.19.3 +Version: 2.19.4 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.3/ansible_core.egg-info/SOURCES.txt new/ansible_core-2.19.4/ansible_core.egg-info/SOURCES.txt --- old/ansible_core-2.19.3/ansible_core.egg-info/SOURCES.txt 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/ansible_core.egg-info/SOURCES.txt 2025-11-05 00:27:03.000000000 +0100 @@ -876,6 +876,7 @@ test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/vars/noop_vars_plugin.py test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/MANIFEST.json +test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/playbooks/collections/ansible_collections/ns/col/plugins/modules/test.py test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/__init__.py test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/cache/notjsonfile.py test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/filter/__init__.py @@ -1800,6 +1801,7 @@ test/integration/targets/collections/roles/standalone/tasks/main.yml test/integration/targets/collections/roles/testrole/tasks/main.yml test/integration/targets/collections/test_plugins/override_formerly_core_masked_test.py +test/integration/targets/collections/test_task_resolved_plugin/dynamic_action.yml test/integration/targets/collections/test_task_resolved_plugin/fqcn.yml test/integration/targets/collections/test_task_resolved_plugin/unqualified.yml test/integration/targets/collections/test_task_resolved_plugin/unqualified_and_collections_kw.yml @@ -2457,6 +2459,7 @@ test/integration/targets/include_import/test_include_loop.yml test/integration/targets/include_import/test_include_loop_fqcn.yml test/integration/targets/include_import/test_loop_var_bleed.yaml +test/integration/targets/include_import/test_nested_non_existent_tasks.yml test/integration/targets/include_import/test_nested_tasks.yml test/integration/targets/include_import/test_nested_tasks_fqcn.yml test/integration/targets/include_import/test_null_include_filename.yml @@ -2558,6 +2561,9 @@ test/integration/targets/include_import/roles/nested_include_task/meta/main.yml test/integration/targets/include_import/roles/nested_include_task/tasks/main.yml test/integration/targets/include_import/roles/nested_include_task/tasks/runa.yml +test/integration/targets/include_import/roles/nested_tasks/tasks/bar.yml +test/integration/targets/include_import/roles/nested_tasks/tasks/foo.yml +test/integration/targets/include_import/roles/nested_tasks/tasks/main.yml test/integration/targets/include_import/roles/role1/tasks/canary1.yml test/integration/targets/include_import/roles/role1/tasks/canary2.yml test/integration/targets/include_import/roles/role1/tasks/canary3.yml @@ -3761,6 +3767,9 @@ test/integration/targets/shell/meta/main.yml test/integration/targets/shell/tasks/command-building.yml test/integration/targets/shell/tasks/main.yml +test/integration/targets/signal_propagation/aliases +test/integration/targets/signal_propagation/inventory +test/integration/targets/signal_propagation/runme.sh test/integration/targets/slurp/aliases test/integration/targets/slurp/files/bar.bin test/integration/targets/slurp/meta/main.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/changelogs/CHANGELOG-v2.19.rst new/ansible_core-2.19.4/changelogs/CHANGELOG-v2.19.rst --- old/ansible_core-2.19.3/changelogs/CHANGELOG-v2.19.rst 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/changelogs/CHANGELOG-v2.19.rst 2025-11-05 00:27:03.000000000 +0100 @@ -4,6 +4,30 @@ .. contents:: Topics +v2.19.4 +======= + +Release Summary +--------------- + +| Release Date: 2025-11-04 +| `Porting Guide <https://docs.ansible.com/ansible-core/2.19/porting_guides/porting_guide_core_2.19.html>`__ + +Bugfixes +-------- + +- Fix issue where play tags prevented executing notified handlers (https://github.com/ansible/ansible/issues/85475) +- Fix issues with keywords being incorrectly validated on ``import_tasks`` (https://github.com/ansible/ansible/issues/85855, https://github.com/ansible/ansible/issues/85856) +- Fix traceback when trying to import non-existing file via nested ``import_tasks`` (https://github.com/ansible/ansible/issues/69882) +- SIGINT/SIGTERM Handling - Make SIGINT/SIGTERM handling more robust by splitting concerns between forks and the parent. +- Windows - ignore temporary file cleanup warning when using AnsibleModule to compile C# utils. This should reduce the number of warnings that can safely be ignored when running PowerShell modules - https://github.com/ansible/ansible/issues/85976 +- ansible-doc - prevent crash when scanning collections in paths that have more than one ``ansible_collections`` in it (https://github.com/ansible/ansible/issues/84909, https://github.com/ansible/ansible/pull/85361). +- callback plugins - improve consistency accessing the Task object's resolved_action attribute. +- config lookup now properly factors in variables and show_origin when checking entries from the global configuration. +- option argument deprecations now have a proper alternative help text. +- package_facts - typecast bytes to string while returning facts (https://github.com/ansible/ansible/issues/85937). +- psrp - ReadTimeout exceptions now mark host as unreachable instead of fatal (https://github.com/ansible/ansible/issues/85966) + v2.19.3 ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/changelogs/changelog.yaml new/ansible_core-2.19.4/changelogs/changelog.yaml --- old/ansible_core-2.19.3/changelogs/changelog.yaml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/changelogs/changelog.yaml 2025-11-05 00:27:03.000000000 +0100 @@ -1354,3 +1354,57 @@ - run_command_output_selector.yml - win_async-junk-output.yml release_date: '2025-09-29' + 2.19.4: + changes: + release_summary: '| Release Date: 2025-11-04 + + | `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.4_summary.yaml + release_date: '2025-11-04' + 2.19.4rc1: + changes: + bugfixes: + - Fix issue where play tags prevented executing notified handlers (https://github.com/ansible/ansible/issues/85475) + - Fix issues with keywords being incorrectly validated on ``import_tasks`` (https://github.com/ansible/ansible/issues/85855, + https://github.com/ansible/ansible/issues/85856) + - Fix traceback when trying to import non-existing file via nested ``import_tasks`` + (https://github.com/ansible/ansible/issues/69882) + - SIGINT/SIGTERM Handling - Make SIGINT/SIGTERM handling more robust by splitting + concerns between forks and the parent. + - Windows - ignore temporary file cleanup warning when using AnsibleModule to + compile C# utils. This should reduce the number of warnings that can safely + be ignored when running PowerShell modules - https://github.com/ansible/ansible/issues/85976 + - ansible-doc - prevent crash when scanning collections in paths that have more + than one ``ansible_collections`` in it (https://github.com/ansible/ansible/issues/84909, + https://github.com/ansible/ansible/pull/85361). + - callback plugins - improve consistency accessing the Task object's resolved_action + attribute. + - config lookup now properly factors in variables and show_origin when checking + entries from the global configuration. + - option argument deprecations now have a proper alternative help text. + - package_facts - typecast bytes to string while returning facts (https://github.com/ansible/ansible/issues/85937). + - psrp - ReadTimeout exceptions now mark host as unreachable instead of fatal + (https://github.com/ansible/ansible/issues/85966) + release_summary: '| Release Date: 2025-10-29 + + | `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.4rc1_summary.yaml + - 85361-collection-name-from-path-none.yml + - 85475-fix-flush_handlers-play-tags.yml + - 85524-resolve-task-resolved_action-early.yml + - 85966-psrp-readtimeout.yml + - add-type-warning.yml + - config_lookup_fix.yml + - fix-signal-propagation.yml + - import_tasks-fixes.yml + - option_deprecation_help.yml + - package_facts.yml + release_date: '2025-10-29' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/cli/arguments/option_helpers.py new/ansible_core-2.19.4/lib/ansible/cli/arguments/option_helpers.py --- old/ansible_core-2.19.3/lib/ansible/cli/arguments/option_helpers.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/cli/arguments/option_helpers.py 2025-11-05 00:27:03.000000000 +0100 @@ -44,6 +44,9 @@ option: str | None = None """The specific option string that is deprecated; None applies to all options for this argument.""" + alternatives: str | None = None + """The options to use instead.""" + def is_deprecated(self, option: str) -> bool: """Return True if the given option is deprecated, otherwise False.""" return self.option is None or option == self.option @@ -58,6 +61,7 @@ Display().deprecated( # pylint: disable=ansible-invalid-deprecated-version msg=f'The {option!r} argument is deprecated.', version=self.version, + help_text=f'Use {self.alternatives} instead.' if self.alternatives else None ) @@ -419,7 +423,7 @@ """Add options for commands that utilize inventory""" parser.add_argument('-i', '--inventory', '--inventory-file', dest='inventory', action="append", help="specify inventory host path or comma separated host list", - deprecated=DeprecatedArgument(version='2.23', option='--inventory-file')) + deprecated=DeprecatedArgument(version='2.23', option='--inventory-file', alternatives="-i or --inventory")) parser.add_argument('--list-hosts', dest='listhosts', action='store_true', help='outputs a list of matching hosts; does not execute anything else') parser.add_argument('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset', @@ -444,10 +448,10 @@ def add_output_options(parser): """Add options for commands which can change their output""" - parser.add_argument('-o', '--one-line', dest='one_line', action='store_true', - help='condense output', deprecated=DeprecatedArgument(version='2.23')) - parser.add_argument('-t', '--tree', dest='tree', default=None, - help='log output to this directory', deprecated=DeprecatedArgument(version='2.23')) + parser.add_argument('-o', '--one-line', dest='one_line', action='store_true', help='condense output', + deprecated=DeprecatedArgument(version='2.23', alternatives='callback configuration to enable the oneline callback')) + parser.add_argument('-t', '--tree', dest='tree', default=None, help='log output to this directory', + deprecated=DeprecatedArgument(version='2.23', alternatives='callback configuration to enable the tree callback')) def add_runas_options(parser): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/cli/doc.py new/ansible_core-2.19.4/lib/ansible/cli/doc.py --- old/ansible_core-2.19.3/lib/ansible/cli/doc.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/cli/doc.py 2025-11-05 00:27:03.000000000 +0100 @@ -237,7 +237,9 @@ b_colldirs = list_collection_dirs(coll_filter=collection_filter) for b_path in b_colldirs: path = to_text(b_path, errors='surrogate_or_strict') - collname = _get_collection_name_from_path(b_path) + if not (collname := _get_collection_name_from_path(b_path)): + display.debug(f'Skipping invalid path {b_path!r}') + continue roles_dir = os.path.join(path, 'roles') if os.path.exists(roles_dir): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/collections/list.py new/ansible_core-2.19.4/lib/ansible/collections/list.py --- old/ansible_core-2.19.3/lib/ansible/collections/list.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/collections/list.py 2025-11-05 00:27:03.000000000 +0100 @@ -17,8 +17,10 @@ collections = {} for candidate in list_collection_dirs(search_paths=search_paths, coll_filter=coll_filter, artifacts_manager=artifacts_manager, dedupe=dedupe): - collection = _get_collection_name_from_path(candidate) - collections[collection] = candidate + if collection := _get_collection_name_from_path(candidate): + collections[collection] = candidate + else: + display.debug(f'Skipping invalid collection in path: {candidate!r}') return collections diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/config/base.yml new/ansible_core-2.19.4/lib/ansible/config/base.yml --- old/ansible_core-2.19.3/lib/ansible/config/base.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/config/base.yml 2025-11-05 00:27:03.000000000 +0100 @@ -2257,6 +2257,8 @@ why: for testing version: '3.30' alternatives: nothing + vars: + - name: _z_test_entry _Z_TEST_ENTRY_2: version_added: '2.18' name: testentry diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/executor/process/worker.py new/ansible_core-2.19.4/lib/ansible/executor/process/worker.py --- old/ansible_core-2.19.3/lib/ansible/executor/process/worker.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/executor/process/worker.py 2025-11-05 00:27:03.000000000 +0100 @@ -17,6 +17,7 @@ from __future__ import annotations +import errno import io import os import signal @@ -103,11 +104,19 @@ self._cliargs = cliargs def _term(self, signum, frame) -> None: - """ - terminate the process group created by calling setsid when - a terminate signal is received by the fork - """ - os.killpg(self.pid, signum) + """In child termination when notified by the parent""" + signal.signal(signum, signal.SIG_DFL) + + try: + os.killpg(self.pid, signum) + os.kill(self.pid, signum) + except OSError as e: + if e.errno != errno.ESRCH: + signame = signal.strsignal(signum) + display.error(f'Unable to send {signame} to child[{self.pid}]: {e}') + + # fallthrough, if we are still here, just die + os._exit(1) def start(self) -> None: """ @@ -121,11 +130,6 @@ # FUTURE: this lock can be removed once a more generalized pre-fork thread pause is in place with display._lock: super(WorkerProcess, self).start() - # Since setsid is called later, if the worker is termed - # it won't term the new process group - # register a handler to propagate the signal - signal.signal(signal.SIGTERM, self._term) - signal.signal(signal.SIGINT, self._term) def _hard_exit(self, e: str) -> t.NoReturn: """ @@ -170,7 +174,6 @@ # to give better errors, and to prevent fd 0 reuse sys.stdin.close() except Exception as e: - display.debug(f'Could not detach from stdio: {traceback.format_exc()}') display.error(f'Could not detach from stdio: {e}') os._exit(1) @@ -187,6 +190,9 @@ # Set the queue on Display so calls to Display.display are proxied over the queue display.set_queue(self._final_q) self._detach() + # propagate signals + signal.signal(signal.SIGINT, self._term) + signal.signal(signal.SIGTERM, self._term) try: with _task.TaskContext(self._task): return self._run() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/executor/task_queue_manager.py new/ansible_core-2.19.4/lib/ansible/executor/task_queue_manager.py --- old/ansible_core-2.19.3/lib/ansible/executor/task_queue_manager.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/executor/task_queue_manager.py 2025-11-05 00:27:03.000000000 +0100 @@ -18,8 +18,10 @@ from __future__ import annotations import dataclasses +import errno import os import sys +import signal import tempfile import threading import time @@ -187,8 +189,48 @@ # plugins for inter-process locking. self._connection_lockfile = tempfile.TemporaryFile() + self._workers: list[WorkerProcess | None] = [] + + # signal handlers to propagate signals to workers + signal.signal(signal.SIGTERM, self._signal_handler) + signal.signal(signal.SIGINT, self._signal_handler) + def _initialize_processes(self, num: int) -> None: - self._workers: list[WorkerProcess | None] = [None] * num + # mutable update to ensure the reference stays the same + self._workers[:] = [None] * num + + def _signal_handler(self, signum, frame) -> None: + """ + terminate all running process groups created as a result of calling + setsid from within a WorkerProcess. + + Since the children become process leaders, signals will not + automatically propagate to them. + """ + signal.signal(signum, signal.SIG_DFL) + + for worker in self._workers: + if worker is None or not worker.is_alive(): + continue + if worker.pid: + try: + # notify workers + os.kill(worker.pid, signum) + except OSError as e: + if e.errno != errno.ESRCH: + signame = signal.strsignal(signum) + display.error(f'Unable to send {signame} to child[{worker.pid}]: {e}') + + if signum == signal.SIGINT: + # Defer to CLI handling + raise KeyboardInterrupt() + + pid = os.getpid() + try: + os.kill(pid, signum) + except OSError as e: + signame = signal.strsignal(signum) + display.error(f'Unable to send {signame} to {pid}: {e}') def load_callbacks(self): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/module_utils/ansible_release.py new/ansible_core-2.19.4/lib/ansible/module_utils/ansible_release.py --- old/ansible_core-2.19.3/lib/ansible/module_utils/ansible_release.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/module_utils/ansible_release.py 2025-11-05 00:27:03.000000000 +0100 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.19.3' +__version__ = '2.19.4' __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.3/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 new/ansible_core-2.19.4/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 --- old/ansible_core-2.19.3/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 2025-11-05 00:27:03.000000000 +0100 @@ -278,10 +278,16 @@ if ($PSCmdlet.ParameterSetName -eq "Module") { $temp_path = $AnsibleModule.Tmpdir $include_debug = $AnsibleModule.Verbosity -ge 3 + + # AnsibleModule will handle the cleanup after module execution + # which should be enough time for AVs or other processes to release + # any locks on the temp files. + $tmpdir_clean_is_error = $false } else { $temp_path = [System.IO.Path]::GetTempPath() $include_debug = $IncludeDebugInfo.IsPresent + $tmpdir_clean_is_error = $true } $temp_path = Join-Path -Path $temp_path -ChildPath ([Guid]::NewGuid().Guid) @@ -388,17 +394,13 @@ } finally { # Try to delete the temp path, if this fails and we are running - # with a module object write a warning instead of failing. + # with a module object, ignore and let it cleanup later. try { [System.IO.Directory]::Delete($temp_path, $true) } catch { - $msg = "Failed to cleanup temporary directory '$temp_path' used for compiling C# code." - if ($AnsibleModule) { - $AnsibleModule.Warn("$msg Files may still be present after the task is complete. Error: $_") - } - else { - throw "$msg Error: $_" + if ($tmpdir_clean_is_error) { + throw "Failed to cleanup temporary directory '$temp_path' used for compiling C# code. Error: $_" } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/modules/package_facts.py new/ansible_core-2.19.4/lib/ansible/modules/package_facts.py --- old/ansible_core-2.19.3/lib/ansible/modules/package_facts.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/modules/package_facts.py 2025-11-05 00:27:03.000000000 +0100 @@ -278,11 +278,11 @@ return self._lib.TransactionSet().dbMatch() def get_package_details(self, package): - return dict(name=package[self._lib.RPMTAG_NAME], - version=package[self._lib.RPMTAG_VERSION], - release=package[self._lib.RPMTAG_RELEASE], - epoch=package[self._lib.RPMTAG_EPOCH], - arch=package[self._lib.RPMTAG_ARCH],) + return dict(name=to_text(package[self._lib.RPMTAG_NAME]), + version=to_text(package[self._lib.RPMTAG_VERSION]), + release=to_text(package[self._lib.RPMTAG_RELEASE]), + epoch=to_text(package[self._lib.RPMTAG_EPOCH]), + arch=to_text(package[self._lib.RPMTAG_ARCH]),) class APT(RespawningLibMgr): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/parsing/mod_args.py new/ansible_core-2.19.4/lib/ansible/parsing/mod_args.py --- old/ansible_core-2.19.3/lib/ansible/parsing/mod_args.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/parsing/mod_args.py 2025-11-05 00:27:03.000000000 +0100 @@ -130,6 +130,7 @@ # HACK: why are these not FieldAttributes on task with a post-validate to check usage? self._task_attrs.update(['local_action', 'static']) self._task_attrs = frozenset(self._task_attrs) + self._resolved_action = None def _split_module_string(self, module_string: str) -> tuple[str, str]: """ @@ -344,6 +345,8 @@ raise e is_action_candidate = context.resolved and bool(context.redirect_list) + if is_action_candidate: + self._resolved_action = context.resolved_fqcn if is_action_candidate: # finding more than one module name is a problem diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/playbook/block.py new/ansible_core-2.19.4/lib/ansible/playbook/block.py --- old/ansible_core-2.19.3/lib/ansible/playbook/block.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/playbook/block.py 2025-11-05 00:27:03.000000000 +0100 @@ -17,7 +17,6 @@ from __future__ import annotations -import ansible.constants as C from ansible.errors import AnsibleParserError from ansible.module_utils.common.sentinel import Sentinel from ansible.playbook.attribute import NonInheritableFieldAttribute @@ -376,8 +375,7 @@ filtered_block = evaluate_block(task) if filtered_block.has_tasks(): tmp_list.append(filtered_block) - elif ((task.action in C._ACTION_META and task.implicit) or - task.evaluate_tags(self._play.only_tags, self._play.skip_tags, all_vars=all_vars)): + elif task.evaluate_tags(self._play.only_tags, self._play.skip_tags, all_vars=all_vars): tmp_list.append(task) return tmp_list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/playbook/helpers.py new/ansible_core-2.19.4/lib/ansible/playbook/helpers.py --- old/ansible_core-2.19.3/lib/ansible/playbook/helpers.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/playbook/helpers.py 2025-11-05 00:27:03.000000000 +0100 @@ -165,17 +165,29 @@ subdir = 'tasks' if use_handlers: subdir = 'handlers' + try: + include_target = templar.template(task.args['_raw_params']) + except AnsibleUndefinedVariable as ex: + raise AnsibleParserError( + message=f"Error when evaluating variable in import path {task.args['_raw_params']!r}.", + help_text="When using static imports, ensure that any variables used in their names are defined in vars/vars_files\n" + "or extra-vars passed in from the command line. Static imports cannot use variables from facts or inventory\n" + "sources like group or host vars.", + obj=task_ds, + ) from ex + # FIXME this appears to be (almost?) duplicate code as in IncludedFile for include_tasks while parent_include is not None: if not isinstance(parent_include, TaskInclude): parent_include = parent_include._parent continue - parent_include.post_validate(templar=templar) - parent_include_dir = os.path.dirname(parent_include.args.get('_raw_params')) + if isinstance(parent_include, IncludeRole): + parent_include_dir = parent_include._role_path + else: + parent_include_dir = os.path.dirname(templar.template(parent_include.args.get('_raw_params'))) if cumulative_path is None: cumulative_path = parent_include_dir elif not os.path.isabs(cumulative_path): cumulative_path = os.path.join(parent_include_dir, cumulative_path) - include_target = templar.template(task.args['_raw_params']) if task._role: new_basedir = os.path.join(task._role._role_path, subdir, cumulative_path) include_file = loader.path_dwim_relative(new_basedir, subdir, include_target) @@ -189,16 +201,6 @@ parent_include = parent_include._parent if not found: - try: - include_target = templar.template(task.args['_raw_params']) - except AnsibleUndefinedVariable as ex: - raise AnsibleParserError( - message=f"Error when evaluating variable in import path {task.args['_raw_params']!r}.", - help_text="When using static imports, ensure that any variables used in their names are defined in vars/vars_files\n" - "or extra-vars passed in from the command line. Static imports cannot use variables from facts or inventory\n" - "sources like group or host vars.", - obj=task_ds, - ) from ex if task._role: include_file = loader.path_dwim_relative(task._role._role_path, subdir, include_target) else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/playbook/play.py new/ansible_core-2.19.4/lib/ansible/playbook/play.py --- old/ansible_core-2.19.3/lib/ansible/playbook/play.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/playbook/play.py 2025-11-05 00:27:03.000000000 +0100 @@ -303,23 +303,13 @@ t = Task(block=flush_block) t.action = 'meta' - t.resolved_action = 'ansible.builtin.meta' + t._resolved_action = 'ansible.builtin.meta' t.args['_raw_params'] = 'flush_handlers' t.implicit = True t.set_loader(self._loader) + t.tags = ['always'] - if self.tags: - # Avoid calling flush_handlers in case the whole play is skipped on tags, - # this could be performance improvement since calling flush_handlers on - # large inventories could be expensive even if no hosts are notified - # since we call flush_handlers per host. - # Block.filter_tagged_tasks ignores evaluating tags on implicit meta - # tasks so we need to explicitly call Task.evaluate_tags here. - t.tags = self.tags - if t.evaluate_tags(self.only_tags, self.skip_tags, all_vars=self.vars): - flush_block.block = [t] - else: - flush_block.block = [t] + flush_block.block = [t] # NOTE keep flush_handlers tasks even if a section has no regular tasks, # there may be notified handlers from the previous section diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/playbook/task.py new/ansible_core-2.19.4/lib/ansible/playbook/task.py --- old/ansible_core-2.19.3/lib/ansible/playbook/task.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/playbook/task.py 2025-11-05 00:27:03.000000000 +0100 @@ -41,7 +41,7 @@ from ansible.playbook.taggable import Taggable from ansible._internal import _task from ansible._internal._templating import _marker_behaviors -from ansible._internal._templating._jinja_bits import is_possibly_all_template +from ansible._internal._templating._jinja_bits import is_possibly_all_template, is_possibly_template from ansible._internal._templating._engine import TemplateEngine, TemplateOptions from ansible.utils.collection_loader import AnsibleCollectionConfig from ansible.utils.display import Display @@ -101,7 +101,7 @@ self._role = role self._parent = None self.implicit = False - self.resolved_action: str | None = None + self._resolved_action: str | None = None if task_include: self._parent = task_include @@ -110,6 +110,38 @@ super(Task, self).__init__() + _resolved_action_warning = ( + "A plugin is sampling the task's resolved_action when it is not resolved. " + "This can be caused by callback plugins using the resolved_action attribute too " + "early (such as in v2_playbook_on_task_start for a task using the action/local_action " + "keyword), or too late (such as in v2_runner_on_ok for a task with a loop). " + "To maximize compatibility with user features, callback plugins should " + "only use this attribute in v2_runner_on_ok/v2_runner_on_failed for tasks " + "without a loop, and v2_runner_item_on_ok/v2_runner_item_on_failed otherwise." + ) + + @property + def resolved_action(self) -> str | None: + """The templated and resolved FQCN of the task action or None. + + If the action is a template, callback plugins can only use this value in certain methods. + - v2_runner_on_ok and v2_runner_on_failed if there's no task loop + - v2_runner_item_on_ok and v2_runner_item_on_failed if there is a task loop + """ + # Consider deprecating this because it's difficult to use? + # Moving it to the task result would improve the no-loop limitation on v2_runner_on_ok + # but then wouldn't be accessible to v2_playbook_on_task_start, *_on_skipped, etc. + if self._resolved_action is not None: + return self._resolved_action + if not is_possibly_template(self.action): + try: + return self._resolve_action(self.action) + except AnsibleParserError: + display.warning(self._resolved_action_warning, obj=self.action) + else: + display.warning(self._resolved_action_warning, obj=self.action) + return None + def get_name(self, include_role_fqcn=True): """ return the name of the task """ @@ -168,7 +200,7 @@ else: module_or_action_context = action_context.plugin_load_context - self.resolved_action = module_or_action_context.resolved_fqcn + self._resolved_action = module_or_action_context.resolved_fqcn action_type: type[ActionBase] = action_context.object @@ -282,6 +314,9 @@ # But if it wasn't, we can add the yaml object now to get more detail raise AnsibleParserError("Error parsing task arguments.", obj=ds) from ex + if args_parser._resolved_action is not None: + self._resolved_action = args_parser._resolved_action + new_ds['action'] = action new_ds['args'] = args new_ds['delegate_to'] = delegate_to @@ -465,7 +500,7 @@ new_me._role = self._role new_me.implicit = self.implicit - new_me.resolved_action = self.resolved_action + new_me._resolved_action = self._resolved_action new_me._uuid = self._uuid return new_me @@ -482,7 +517,7 @@ data['role'] = self._role.serialize() data['implicit'] = self.implicit - data['resolved_action'] = self.resolved_action + data['_resolved_action'] = self._resolved_action return data @@ -513,7 +548,7 @@ del data['role'] self.implicit = data.get('implicit', False) - self.resolved_action = data.get('resolved_action') + self._resolved_action = data.get('_resolved_action') super(Task, self).deserialize(data) @@ -591,7 +626,7 @@ def dump_attrs(self): """Override to smuggle important non-FieldAttribute values back to the controller.""" attrs = super().dump_attrs() - attrs.update(resolved_action=self.resolved_action) + attrs.update(_resolved_action=self._resolved_action) return attrs def _resolve_conditional( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/plugins/connection/psrp.py new/ansible_core-2.19.4/lib/ansible/plugins/connection/psrp.py --- old/ansible_core-2.19.3/lib/ansible/plugins/connection/psrp.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/plugins/connection/psrp.py 2025-11-05 00:27:03.000000000 +0100 @@ -331,7 +331,7 @@ from pypsrp.host import PSHost, PSHostUserInterface from pypsrp.powershell import PowerShell, RunspacePool from pypsrp.wsman import WSMan - from requests.exceptions import ConnectionError, ConnectTimeout + from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout except ImportError as err: HAS_PYPSRP = False PYPSRP_IMP_ERR = err @@ -479,11 +479,16 @@ pwsh_in_data = in_data display.vvv(u"PSRP: EXEC %s" % script, host=self._psrp_host) - rc, stdout, stderr = self._exec_psrp_script( - script=script, - input_data=pwsh_in_data.splitlines() if pwsh_in_data else None, - arguments=script_args, - ) + try: + rc, stdout, stderr = self._exec_psrp_script( + script=script, + input_data=pwsh_in_data.splitlines() if pwsh_in_data else None, + arguments=script_args, + ) + except ReadTimeout as e: + raise AnsibleConnectionFailure( + "HTTP read timeout during PSRP script execution" + ) from e return rc, stdout, stderr def put_file(self, in_path: str, out_path: str) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/plugins/lookup/config.py new/ansible_core-2.19.4/lib/ansible/plugins/lookup/config.py --- old/ansible_core-2.19.3/lib/ansible/plugins/lookup/config.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/plugins/lookup/config.py 2025-11-05 00:27:03.000000000 +0100 @@ -88,31 +88,6 @@ from ansible.plugins.lookup import LookupBase -def _get_plugin_config(pname, ptype, config, variables): - # plugin creates settings on load, this is cached so not too expensive to redo - loader = getattr(plugin_loader, '%s_loader' % ptype) - p = loader.get(pname, class_only=True) - - if p is None: - raise AnsibleError(f"Unable to load {ptype} plugin {pname!r}.") - - result, origin = C.config.get_config_value_and_origin(config, plugin_type=ptype, plugin_name=p._load_name, variables=variables) - - return result, origin - - -def _get_global_config(config): - try: - result = getattr(C, config) - except AttributeError: - raise AnsibleUndefinedConfigEntry(f"Setting {config!r} does not exist.") from None - - if callable(result): - raise ValueError(f"Invalid setting {config!r} attempted.") - - return result - - class LookupModule(LookupBase): def run(self, terms, variables=None, **kwargs): @@ -135,18 +110,26 @@ result = Sentinel origin = None + + # plugin creates settings on load, we ensure that happens here + if pname: + # this is cached so not too expensive + loader = getattr(plugin_loader, f'{ptype}_loader') + p = loader.get(pname, class_only=True) + if p is None: + raise AnsibleError(f"Unable to load {ptype} plugin {pname!r}.") try: - if pname: - result, origin = _get_plugin_config(pname, ptype, term, variables) - else: - result = _get_global_config(term) - except AnsibleUndefinedConfigEntry: - if missing == 'error': - raise - elif missing == 'warn': - self._display.warning(f"Skipping, did not find setting {term!r}.") - elif missing == 'skip': - pass # this is not needed, but added to have all 3 options stated + result, origin = C.config.get_config_value_and_origin(term, plugin_type=ptype, plugin_name=pname, variables=variables) + except AnsibleUndefinedConfigEntry as e: + match missing: + case 'error': + raise + case 'skip': + pass + case 'warn': + self._display.error_as_warning(msg=f"Skipping {term}.", exception=e) + case _: + raise AnsibleError(f"Invalid option for error handling, missing must be error, warn or skip, got: {missing}.") from e if result is not Sentinel: if show_origin: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/plugins/strategy/__init__.py new/ansible_core-2.19.4/lib/ansible/plugins/strategy/__init__.py --- old/ansible_core-2.19.3/lib/ansible/plugins/strategy/__init__.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/plugins/strategy/__init__.py 2025-11-05 00:27:03.000000000 +0100 @@ -900,7 +900,7 @@ display.warning("%s task does not support when conditional" % task_name) def _execute_meta(self, task: Task, play_context, iterator, target_host: Host): - task.resolved_action = 'ansible.builtin.meta' # _post_validate_args is never called for meta actions, so resolved_action hasn't been set + task._resolved_action = 'ansible.builtin.meta' # _post_validate_args is never called for meta actions, so resolved_action hasn't been set # meta tasks store their args in the _raw_params field of args, # since they do not use k=v pairs, so get that diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/lib/ansible/release.py new/ansible_core-2.19.4/lib/ansible/release.py --- old/ansible_core-2.19.3/lib/ansible/release.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/lib/ansible/release.py 2025-11-05 00:27:03.000000000 +0100 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.19.3' +__version__ = '2.19.4' __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.3/test/integration/targets/ansible-doc/runme.sh new/ansible_core-2.19.4/test/integration/targets/ansible-doc/runme.sh --- old/ansible_core-2.19.3/test/integration/targets/ansible-doc/runme.sh 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/ansible-doc/runme.sh 2025-11-05 00:27:03.000000000 +0100 @@ -211,6 +211,13 @@ output=$(ANSIBLE_LIBRARY='./nolibrary' ansible-doc --metadata-dump --playbook-dir broken-docs testns.testcol 2>&1 | grep -c 'ERROR' || true) test "${output}" -eq 1 +# ensure --metadata-dump does not crash if the ansible_collections is nested (https://github.com/ansible/ansible/issues/84909) +testdir="$(pwd)" +pbdir="collections/ansible_collections/testns/testcol/playbooks" +cd "$pbdir" +ANSIBLE_COLLECTIONS_PATH="$testdir/$pbdir/collections" ansible-doc -vvv --metadata-dump --no-fail-on-errors +cd "$testdir" + echo "test doc list on broken role metadata" # ensure that role doc does not fail when --no-fail-on-errors is supplied ANSIBLE_LIBRARY='./nolibrary' ansible-doc --no-fail-on-errors --playbook-dir broken-docs testns.testcol.testrole -t role 1>/dev/null 2>&1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py --- old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py 2025-11-05 00:27:03.000000000 +0100 @@ -9,6 +9,12 @@ short_description: Displays the requested and resolved actions at the end of a playbook. description: - Displays the requested and resolved actions in the format "requested == resolved". + options: + test_on_task_start: + description: Test using task.resolved_action before it is reliably resolved. + default: False + env: + - name: ANSIBLE_TEST_ON_TASK_START requirements: - Enable in configuration. """ @@ -25,11 +31,14 @@ def __init__(self, *args, **kwargs): super(CallbackModule, self).__init__(*args, **kwargs) - self.requested_to_resolved = {} - def v2_runner_on_ok(self, result): - self.requested_to_resolved[result.task.action] = result.task.resolved_action + def v2_playbook_on_task_start(self, task, is_conditional): + if self.get_option("test_on_task_start"): + self._display.display(f"v2_playbook_on_task_start: {task.action} == {task.resolved_action}") + + def v2_runner_item_on_ok(self, result): + self._display.display(f"v2_runner_item_on_ok: {result.task.action} == {result.task.resolved_action}") - def v2_playbook_on_stats(self, stats): - for requested, resolved in self.requested_to_resolved.items(): - self._display.display("%s == %s" % (requested, resolved), screen_only=True) + def v2_runner_on_ok(self, result): + if not result.task.loop: + self._display.display(f"v2_runner_on_ok: {result.task.action} == {result.task.resolved_action}") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin/dynamic_action.yml new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin/dynamic_action.yml --- old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin/dynamic_action.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin/dynamic_action.yml 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1,10 @@ +--- +- hosts: all + gather_facts: no + tasks: + - name: Run dynamic action + action: "{{ inventory_hostname }}" + + - name: Run dynamic action in loop + action: "{{ inventory_hostname }}" + loop: [1] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin/unqualified.yml new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin/unqualified.yml --- old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin/unqualified.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin/unqualified.yml 2025-11-05 00:27:03.000000000 +0100 @@ -4,5 +4,5 @@ tasks: - legacy_action: - legacy_module: - - debug: - - ping: + - local_action: debug + - action: ping diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin.sh new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin.sh --- old/ansible_core-2.19.3/test/integration/targets/collections/test_task_resolved_plugin.sh 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/collections/test_task_resolved_plugin.sh 2025-11-05 00:27:03.000000000 +0100 @@ -15,6 +15,22 @@ grep -q out.txt -e "$result" done +# Test local_action/action warning +export ANSIBLE_TEST_ON_TASK_START=True +ansible-playbook -i debug, test_task_resolved_plugin/dynamic_action.yml "$@" 2>&1 | tee out.txt +grep -q out.txt -e "A plugin is sampling the task's resolved_action when it is not resolved" +grep -q out.txt -e "v2_playbook_on_task_start: {{ inventory_hostname }} == None" +grep -q out.txt -e "v2_runner_on_ok: debug == ansible.builtin.debug" +grep -q out.txt -e "v2_runner_item_on_ok: debug == ansible.builtin.debug" + +# Test static actions don't cause a warning +ansible-playbook test_task_resolved_plugin/unqualified.yml "$@" 2>&1 | tee out.txt +grep -v out.txt -e "A plugin is sampling the task's resolved_action when it is not resolved" +for result in "${action_resolution[@]}"; do + grep -q out.txt -e "v2_playbook_on_task_start: $result" +done +unset ANSIBLE_TEST_ON_TASK_START + ansible-playbook test_task_resolved_plugin/unqualified_and_collections_kw.yml "$@" | tee out.txt action_resolution=( "legacy_action == legacy_action" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/connection_ssh/aliases new/ansible_core-2.19.4/test/integration/targets/connection_ssh/aliases --- old/ansible_core-2.19.3/test/integration/targets/connection_ssh/aliases 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/connection_ssh/aliases 2025-11-05 00:27:03.000000000 +0100 @@ -2,4 +2,5 @@ shippable/posix/group3 needs/target/connection needs/target/setup_test_user +needs/target/test_utils setup/always/setup_passlib_controller # required for setup_test_user diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/connection_ssh/runme.sh new/ansible_core-2.19.4/test/integration/targets/connection_ssh/runme.sh --- old/ansible_core-2.19.3/test/integration/targets/connection_ssh/runme.sh 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/connection_ssh/runme.sh 2025-11-05 00:27:03.000000000 +0100 @@ -17,7 +17,7 @@ # ansible with timeout. If we time out, our custom prompt was successfully # searched for. It's a weird way of doing things, but it does ensure # that the flag gets passed to sshpass. - timeout 5 ansible -m ping \ + ../test_utils/scripts/timeout.py 5 -- ansible -m ping \ -e ansible_connection=ssh \ -e ansible_ssh_password_mechanism=sshpass \ -e ansible_sshpass_prompt=notThis: \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/deprecations/runme.sh new/ansible_core-2.19.4/test/integration/targets/deprecations/runme.sh --- old/ansible_core-2.19.3/test/integration/targets/deprecations/runme.sh 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/deprecations/runme.sh 2025-11-05 00:27:03.000000000 +0100 @@ -25,8 +25,9 @@ # check for entry key deprecation including the name of the option, must be defined to trigger [ "$(ANSIBLE_CONFIG='entry_key_deprecated.cfg' ansible -m meta -a 'noop' localhost 2>&1 | grep -c "\[DEPRECATION WARNING\]: \[testing\]deprecated option.")" -eq "1" ] +# DTFIX: fix issue with x2 deprecation and wrong pllugin attribution # check for deprecation of entry itself, must be consumed to trigger -[ "$(ANSIBLE_TEST_ENTRY2=1 ansible -m debug -a 'msg={{q("config", "_Z_TEST_ENTRY_2")}}' localhost 2>&1 | grep -c 'DEPRECATION')" -eq "1" ] +[ "$(ANSIBLE_TEST_ENTRY2=1 ansible -m debug -a 'msg={{q("config", "_Z_TEST_ENTRY_2")}}' localhost 2>&1 | grep -c 'DEPRECATION')" -eq "2" ] # check for entry deprecation, just need key defined to trigger [ "$(ANSIBLE_CONFIG='entry_key_deprecated2.cfg' ansible -m meta -a 'noop' localhost 2>&1 | grep -c 'DEPRECATION')" -eq "1" ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/get_url/tasks/main.yml new/ansible_core-2.19.4/test/integration/targets/get_url/tasks/main.yml --- old/ansible_core-2.19.3/test/integration/targets/get_url/tasks/main.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/get_url/tasks/main.yml 2025-11-05 00:27:03.000000000 +0100 @@ -396,6 +396,8 @@ src: "testserver.py" dest: "{{ remote_tmp_dir }}/testserver.py" +# NOTE: This http test server will live for only the timeout specified in "async", so all uses +# of it must be grouped relatively close together. - name: start SimpleHTTPServer for issues 27617 shell: cd {{ files_dir }} && {{ ansible_python.executable }} {{ remote_tmp_dir}}/testserver.py {{ http_port }} async: 90 @@ -578,6 +580,19 @@ - "stat_result_sha256_with_file_scheme_71420.stat.exists == true" - "stat_result_sha256_checksum_only.stat.exists == true" +- name: Test for incomplete data read (issue 85164) + get_url: + url: 'http://localhost:{{ http_port }}/incompleteRead' + dest: '{{ remote_tmp_dir }}/85164.txt' + ignore_errors: true + register: result + +- name: Assert we have an incomplete read failure + assert: + that: + - result is failed + - '"Incomplete read" in result.msg' + #https://github.com/ansible/ansible/issues/16191 - name: Test url split with no filename get_url: @@ -761,16 +776,3 @@ - assert: that: - get_dir_filename.dest == remote_tmp_dir ~ "/filename.json" - -- name: Test for incomplete data read (issue 85164) - get_url: - url: 'http://localhost:{{ http_port }}/incompleteRead' - dest: '{{ remote_tmp_dir }}/85164.txt' - ignore_errors: true - register: result - -- name: Assert we have an incomplete read failure - assert: - that: - - result is failed - - '"Incomplete read" in result.msg' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/handlers/runme.sh new/ansible_core-2.19.4/test/integration/targets/handlers/runme.sh --- old/ansible_core-2.19.3/test/integration/targets/handlers/runme.sh 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/handlers/runme.sh 2025-11-05 00:27:03.000000000 +0100 @@ -229,6 +229,13 @@ ANSIBLE_DEBUG=1 ansible-playbook tagged_play.yml --skip-tags the_whole_play "$@" 2>&1 | tee out.txt [ "$(grep out.txt -ce 'META: triggered running handlers')" = "0" ] +[ "$(grep out.txt -ce 'No handler notifications for')" = "0" ] [ "$(grep out.txt -ce 'handler_ran')" = "0" ] +[ "$(grep out.txt -ce 'handler1_ran')" = "0" ] ansible-playbook rescue_flush_handlers.yml "$@" + +ANSIBLE_DEBUG=1 ansible-playbook tagged_play.yml --tags task_tag "$@" 2>&1 | tee out.txt +[ "$(grep out.txt -ce 'META: triggered running handlers')" = "1" ] +[ "$(grep out.txt -ce 'handler_ran')" = "0" ] +[ "$(grep out.txt -ce 'handler1_ran')" = "1" ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/handlers/tagged_play.yml new/ansible_core-2.19.4/test/integration/targets/handlers/tagged_play.yml --- old/ansible_core-2.19.3/test/integration/targets/handlers/tagged_play.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/handlers/tagged_play.yml 2025-11-05 00:27:03.000000000 +0100 @@ -2,9 +2,19 @@ gather_facts: false tags: the_whole_play tasks: - - command: echo + - debug: + changed_when: true notify: h + + - debug: + changed_when: true + notify: h1 + tags: task_tag handlers: - name: h debug: msg: handler_ran + + - name: h1 + debug: + msg: handler1_ran diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/include_import/roles/nested_tasks/tasks/bar.yml new/ansible_core-2.19.4/test/integration/targets/include_import/roles/nested_tasks/tasks/bar.yml --- old/ansible_core-2.19.3/test/integration/targets/include_import/roles/nested_tasks/tasks/bar.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/include_import/roles/nested_tasks/tasks/bar.yml 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1 @@ +- import_tasks: does-not-exist.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/include_import/roles/nested_tasks/tasks/foo.yml new/ansible_core-2.19.4/test/integration/targets/include_import/roles/nested_tasks/tasks/foo.yml --- old/ansible_core-2.19.3/test/integration/targets/include_import/roles/nested_tasks/tasks/foo.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/include_import/roles/nested_tasks/tasks/foo.yml 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1 @@ +- include_tasks: bar.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/include_import/roles/nested_tasks/tasks/main.yml new/ansible_core-2.19.4/test/integration/targets/include_import/roles/nested_tasks/tasks/main.yml --- old/ansible_core-2.19.3/test/integration/targets/include_import/roles/nested_tasks/tasks/main.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/include_import/roles/nested_tasks/tasks/main.yml 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1 @@ +- import_tasks: foo.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/include_import/runme.sh new/ansible_core-2.19.4/test/integration/targets/include_import/runme.sh --- old/ansible_core-2.19.3/test/integration/targets/include_import/runme.sh 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/include_import/runme.sh 2025-11-05 00:27:03.000000000 +0100 @@ -155,3 +155,9 @@ test "$(grep -c 'No file specified for ansible.builtin.include_tasks' test_null_include_filename.out)" = 1 test "$(grep -c '.*/include_import/null_filename/tasks.yml:4:3.*' test_null_include_filename.out)" = 1 test "$(grep -c '\- name: invalid include_task definition' test_null_include_filename.out)" = 1 + +# https://github.com/ansible/ansible/issues/69882 +set +e +ansible-playbook test_nested_non_existent_tasks.yml 2>&1 | tee test_nested_non_existent_tasks.out +set -e +test "$(grep -c 'Could not find or access' test_nested_non_existent_tasks.out)" = 3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/include_import/test_nested_non_existent_tasks.yml new/ansible_core-2.19.4/test/integration/targets/include_import/test_nested_non_existent_tasks.yml --- old/ansible_core-2.19.3/test/integration/targets/include_import/test_nested_non_existent_tasks.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/include_import/test_nested_non_existent_tasks.yml 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1,5 @@ +- hosts: localhost + gather_facts: false + tasks: + - include_role: + name: nested_tasks diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/include_import_tasks_nested/tasks/main.yml new/ansible_core-2.19.4/test/integration/targets/include_import_tasks_nested/tasks/main.yml --- old/ansible_core-2.19.3/test/integration/targets/include_import_tasks_nested/tasks/main.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/include_import_tasks_nested/tasks/main.yml 2025-11-05 00:27:03.000000000 +0100 @@ -10,4 +10,9 @@ that: - nested_adjacent_count|int == 2 +- set_fact: + not_available_at_parsing: root + - import_tasks: "{{ role_path }}/tests/main.yml" + become: true + become_user: "{{ not_available_at_parsing }}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/include_import_tasks_nested/tests/tests_relative.yml new/ansible_core-2.19.4/test/integration/targets/include_import_tasks_nested/tests/tests_relative.yml --- old/ansible_core-2.19.3/test/integration/targets/include_import_tasks_nested/tests/tests_relative.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/include_import_tasks_nested/tests/tests_relative.yml 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1,6 @@ +- command: whoami + register: r + +- assert: + that: + - r.stdout == not_available_at_parsing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/lookup_config/tasks/main.yml new/ansible_core-2.19.4/test/integration/targets/lookup_config/tasks/main.yml --- old/ansible_core-2.19.3/test/integration/targets/lookup_config/tasks/main.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/lookup_config/tasks/main.yml 2025-11-05 00:27:03.000000000 +0100 @@ -83,14 +83,26 @@ ignore_errors: yes register: lookup_config_12 +- name: origins + set_fact: + config_origin1: "{{ lookup('config', '_Z_TEST_ENTRY', show_origin=True) }}" + ignore_errors: yes + +- name: var sets it + set_fact: + config_origin2: "{{ lookup('config', '_Z_TEST_ENTRY', show_origin=True) }}" + ignore_errors: yes + vars: + _z_test_entry: yolo + - name: Verify lookup_config assert: that: - '"meow" in lookup("config", "ANSIBLE_COW_ACCEPTLIST")' - lookup_config_1 is failed - - lookup_config_1.msg is contains "Setting 'THIS_DOES_NOT_EXIST' does not exist." + - lookup_config_1.msg is contains "No config definition exists for 'THIS_DOES_NOT_EXIST'" - lookup_config_2 is failed - - lookup_config_2.msg is contains "Setting 'THIS_DOES_NOT_EXIST' does not exist." + - lookup_config_2.msg is contains "No config definition exists for 'THIS_DOES_NOT_EXIST'" - lookup_config_3 is success - 'lookup3|length == 0' - lookup_config_4 is success @@ -100,7 +112,7 @@ - lookup_config_6 is failed - '"Invalid setting identifier" in lookup_config_6.msg' - lookup_config_7 is failed - - '"Invalid setting" in lookup_config_7.msg' + - lookup_config_7.msg is contains "No config definition exists for 'ConfigManager'" - lookup_config_8 is failed - '"Both plugin_type and plugin_name" in lookup_config_8.msg' - lookup_config_9 is failed @@ -114,3 +126,6 @@ - ssh_user_and_port == ['lola', 2022] - "ssh_user_and_port_and_origin == [['lola', 'var: ansible_ssh_user'], [2022, 'var: ansible_ssh_port']]" - yolo_remote == ["yolo"] + - config_origin1[1] == "default" + - config_origin2[0] == 'yolo' + - 'config_origin2[1] == "var: _z_test_entry"' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/signal_propagation/aliases new/ansible_core-2.19.4/test/integration/targets/signal_propagation/aliases --- old/ansible_core-2.19.3/test/integration/targets/signal_propagation/aliases 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/signal_propagation/aliases 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1,3 @@ +shippable/posix/group4 +context/controller +needs/target/test_utils diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/signal_propagation/inventory new/ansible_core-2.19.4/test/integration/targets/signal_propagation/inventory --- old/ansible_core-2.19.3/test/integration/targets/signal_propagation/inventory 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/signal_propagation/inventory 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1,14 @@ +localhost0 +localhost1 +localhost2 +localhost3 +localhost4 +localhost5 +localhost6 +localhost7 +localhost8 +localhost9 + +[all:vars] +ansible_connection=local +ansible_python_interpreter={{ansible_playbook_python}} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/signal_propagation/runme.sh new/ansible_core-2.19.4/test/integration/targets/signal_propagation/runme.sh --- old/ansible_core-2.19.3/test/integration/targets/signal_propagation/runme.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.4/test/integration/targets/signal_propagation/runme.sh 2025-11-05 00:27:03.000000000 +0100 @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -x + +../test_utils/scripts/timeout.py -s SIGINT 3 -- \ + ansible all -i inventory -m debug -a 'msg={{lookup("pipe", "sleep 33")}}' -f 10 +if [[ "$?" != "124" ]]; then + echo "Process was not terminated due to timeout" + exit 1 +fi + +# a short sleep to let processes die +sleep 2 + +sleeps="$(pgrep -alf 'sleep\ 33')" +rc="$?" +if [[ "$rc" == "0" ]]; then + echo "Found lingering processes:" + echo "$sleeps" + exit 1 +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/ssh_agent/tasks/tests.yml new/ansible_core-2.19.4/test/integration/targets/ssh_agent/tasks/tests.yml --- old/ansible_core-2.19.3/test/integration/targets/ssh_agent/tasks/tests.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/ssh_agent/tasks/tests.yml 2025-11-05 00:27:03.000000000 +0100 @@ -29,15 +29,17 @@ vars: pid: '{{ auto.stdout|regex_findall("ssh-agent\[(\d+)\]")|first }}' -- command: ssh-agent -D -s -a '{{ output_dir }}/agent.sock' - async: 30 - poll: 0 +- shell: ssh-agent -D -s -a '{{ output_dir }}/agent.sock' & + register: ssh_agent_result -- command: ansible-playbook -i {{ ansible_inventory_sources|first|quote }} -vvv {{ role_path }}/auto.yml - environment: - ANSIBLE_CALLBACK_RESULT_FORMAT: yaml - ANSIBLE_SSH_AGENT: '{{ output_dir }}/agent.sock' - register: existing +- block: + - command: ansible-playbook -i {{ ansible_inventory_sources|first|quote }} -vvv {{ role_path }}/auto.yml + environment: + ANSIBLE_CALLBACK_RESULT_FORMAT: yaml + ANSIBLE_SSH_AGENT: '{{ output_dir }}/agent.sock' + register: existing + always: + - command: "kill {{ ssh_agent_result.stdout | regex_search('Agent pid ([0-9]+)', '\\1') | first }}" - assert: that: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/test_utils/scripts/timeout.py new/ansible_core-2.19.4/test/integration/targets/test_utils/scripts/timeout.py --- old/ansible_core-2.19.3/test/integration/targets/test_utils/scripts/timeout.py 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/test_utils/scripts/timeout.py 2025-11-05 00:27:03.000000000 +0100 @@ -2,21 +2,32 @@ from __future__ import annotations import argparse +import signal import subprocess import sys + +def signal_type(v: str) -> signal.Signals: + if v.isdecimal(): + return signal.Signals(int(v)) + if not v.startswith('SIG'): + v = f'SIG{v}' + return getattr(signal.Signals, v) + + parser = argparse.ArgumentParser() parser.add_argument('duration', type=int) +parser.add_argument('--signal', '-s', default=signal.SIGTERM, type=signal_type) parser.add_argument('command', nargs='+') args = parser.parse_args() +p: subprocess.Popen | None = None try: - p = subprocess.run( - ' '.join(args.command), - shell=True, - timeout=args.duration, - check=False, - ) + p = subprocess.Popen(args.command) + p.wait(timeout=args.duration) sys.exit(p.returncode) except subprocess.TimeoutExpired: + if p and p.poll() is None: + p.send_signal(args.signal) + p.wait() sys.exit(124) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.3/test/integration/targets/win_exec_wrapper/tasks/main.yml new/ansible_core-2.19.4/test/integration/targets/win_exec_wrapper/tasks/main.yml --- old/ansible_core-2.19.3/test/integration/targets/win_exec_wrapper/tasks/main.yml 2025-10-06 19:22:06.000000000 +0200 +++ new/ansible_core-2.19.4/test/integration/targets/win_exec_wrapper/tasks/main.yml 2025-11-05 00:27:03.000000000 +0100 @@ -268,14 +268,6 @@ <<: *become_vars ansible_remote_tmp: C:\Windows\TEMP\test-dir - - name: assert warning about tmpdir deletion is present - assert: - that: - - temp_deletion_warning.warnings | count == 1 - - >- - temp_deletion_warning.warnings[0] is - regex("(?i).*Failed to cleanup temporary directory 'C:\\\\Windows\\\\TEMP\\\\test-dir\\\\.*' used for compiling C# code\\. Files may still be present after the task is complete\\..*") - always: - name: ensure test user is deleted win_user: ++++++ ansible_core-2.19.3.tar.gz.sha256 -> ansible_core-2.19.4.tar.gz.sha256 ++++++ --- /work/SRC/openSUSE:Factory/ansible-core/ansible_core-2.19.3.tar.gz.sha256 2025-10-07 18:30:04.145769808 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.1980/ansible_core-2.19.4.tar.gz.sha256 2025-11-06 18:13:58.325364076 +0100 @@ -1 +1 @@ -243a69669a007be0794360bc4477f70e0128ce0091dc3af4c5cb81c6a466f573 ansible_core-2.19.3.tar.gz +888db6593f2fd42cd05bdbe546704d9c969dce99e3373a54498f6dbefcfa1917 ansible_core-2.19.4.tar.gz
