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-05-19 17:50:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible-core (Old) and /work/SRC/openSUSE:Factory/.ansible-core.new.1966 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible-core" Tue May 19 17:50:23 2026 rev:56 rq:1353955 version:2.20.6 Changes: -------- --- /work/SRC/openSUSE:Factory/ansible-core/ansible-core.changes 2026-04-21 12:47:41.133554471 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.1966/ansible-core.changes 2026-05-19 17:50:59.398174693 +0200 @@ -1,0 +2,22 @@ +Tue May 19 07:08:20 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 2.20.6: + https://github.com/ansible/ansible/blob/v2.20.6/changelogs/CHANGELOG-v2.20.rst + * Security Fixes + - psrp - Do not log raw stdout/stderr on verbosity 5 when task + has no_log: true set + - winrm - Do not log raw stdout/stderr on verbosity 5 when task + has no_log: true set + * Bugfixes + - ansible-test remote alias - Alias values for --controller and + --target are properly resolved for remote. Previously, remote + alias values (e.g. fedora/latest) resolved to the correct + name only for the legacy --remote arg, failing with an + unknown image error for the newer args. + - git - use the branch configured in .gitmodules or the remote + HEAD instead of hardcoding master when track_submodules=yes + (#77691). + - module_utils/basic.py - Fix AnsibleModule.run_command() to + handle None return from non-blocking pipe reads (#86920). + +------------------------------------------------------------------- Old: ---- ansible_core-2.20.5.tar.gz ansible_core-2.20.5.tar.gz.sha256 New: ---- ansible_core-2.20.6.tar.gz ansible_core-2.20.6.tar.gz.sha256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible-core.spec ++++++ --- /var/tmp/diff_new_pack.rYp0al/_old 2026-05-19 17:51:01.474260475 +0200 +++ /var/tmp/diff_new_pack.rYp0al/_new 2026-05-19 17:51:01.490261136 +0200 @@ -43,7 +43,7 @@ %endif Name: ansible-core -Version: 2.20.5 +Version: 2.20.6 Release: 0 Summary: Radically simple IT automation License: GPL-3.0-or-later ++++++ ansible_core-2.20.5.tar.gz -> ansible_core-2.20.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/PKG-INFO new/ansible_core-2.20.6/PKG-INFO --- old/ansible_core-2.20.5/PKG-INFO 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/PKG-INFO 2026-05-18 21:15:21.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.20.5 +Version: 2.20.6 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.5/ansible_core.egg-info/PKG-INFO new/ansible_core-2.20.6/ansible_core.egg-info/PKG-INFO --- old/ansible_core-2.20.5/ansible_core.egg-info/PKG-INFO 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/ansible_core.egg-info/PKG-INFO 2026-05-18 21:15:21.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.20.5 +Version: 2.20.6 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.5/ansible_core.egg-info/SOURCES.txt new/ansible_core-2.20.6/ansible_core.egg-info/SOURCES.txt --- old/ansible_core-2.20.5/ansible_core.egg-info/SOURCES.txt 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/ansible_core.egg-info/SOURCES.txt 2026-05-18 21:15:21.000000000 +0200 @@ -2290,6 +2290,7 @@ test/integration/targets/git/tasks/single-branch.yml test/integration/targets/git/tasks/specific-revision.yml test/integration/targets/git/tasks/submodules.yml +test/integration/targets/git/tasks/track-submodules-branch.yml test/integration/targets/git/vars/main.yml test/integration/targets/group/aliases test/integration/targets/group/files/get_free_gid.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/changelogs/CHANGELOG-v2.20.rst new/ansible_core-2.20.6/changelogs/CHANGELOG-v2.20.rst --- old/ansible_core-2.20.5/changelogs/CHANGELOG-v2.20.rst 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/changelogs/CHANGELOG-v2.20.rst 2026-05-18 21:15:21.000000000 +0200 @@ -4,6 +4,28 @@ .. contents:: Topics +v2.20.6 +======= + +Release Summary +--------------- + +| Release Date: 2026-05-18 +| `Porting Guide <https://docs.ansible.com/ansible-core/2.20/porting_guides/porting_guide_core_2.20.html>`__ + +Security Fixes +-------------- + +- psrp - Do not log raw stdout/stderr on verbosity 5 when task has ``no_log: true`` set +- winrm - Do not log raw stdout/stderr on verbosity 5 when task has ``no_log: true`` set + +Bugfixes +-------- + +- ansible-test remote alias - Alias values for ``--controller`` and ``--target`` are properly resolved for ``remote``. Previously, remote alias values (e.g. ``fedora/latest``) resolved to the correct name only for the legacy ``--remote`` arg, failing with an unknown image error for the newer args. +- git - use the branch configured in ``.gitmodules`` or the remote HEAD instead of hardcoding ``master`` when ``track_submodules=yes`` (https://github.com/ansible/ansible/issues/77691). +- module_utils/basic.py - Fix ``AnsibleModule.run_command()`` to handle ``None`` return from non-blocking pipe reads (https://github.com/ansible/ansible/issues/86920). + v2.20.5 ======= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/changelogs/changelog.yaml new/ansible_core-2.20.6/changelogs/changelog.yaml --- old/ansible_core-2.20.5/changelogs/changelog.yaml 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/changelogs/changelog.yaml 2026-05-18 21:15:21.000000000 +0200 @@ -619,3 +619,43 @@ - clearlinux-gentoo-parsing.yml - start-at-task-fact-gathering.yml release_date: '2026-04-13' + 2.20.6: + changes: + release_summary: '| Release Date: 2026-05-18 + + | `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.6_summary.yaml + release_date: '2026-05-18' + 2.20.6rc1: + changes: + bugfixes: + - ansible-test remote alias - Alias values for ``--controller`` and ``--target`` + are properly resolved for ``remote``. Previously, remote alias values (e.g. + ``fedora/latest``) resolved to the correct name only for the legacy ``--remote`` + arg, failing with an unknown image error for the newer args. + - git - use the branch configured in ``.gitmodules`` or the remote HEAD instead + of hardcoding ``master`` when ``track_submodules=yes`` (https://github.com/ansible/ansible/issues/77691). + - module_utils/basic.py - Fix ``AnsibleModule.run_command()`` to handle ``None`` + return from non-blocking pipe reads (https://github.com/ansible/ansible/issues/86920). + release_summary: '| Release Date: 2026-05-11 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.20/porting_guides/porting_guide_core_2.20.html>`__ + + ' + security_fixes: + - 'psrp - Do not log raw stdout/stderr on verbosity 5 when task has ``no_log: + true`` set' + - 'winrm - Do not log raw stdout/stderr on verbosity 5 when task has ``no_log: + true`` set' + codename: Good Times Bad Times + fragments: + - 2.20.6rc1_summary.yaml + - 77691-git-track-submodules-branch.yml + - 86920-fix-run-command-none-read.yml + - core_ci_remote_alias.yml + - winrm-psrp-nolog.yml + release_date: '2026-05-11' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/lib/ansible/module_utils/ansible_release.py new/ansible_core-2.20.6/lib/ansible/module_utils/ansible_release.py --- old/ansible_core-2.20.5/lib/ansible/module_utils/ansible_release.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/lib/ansible/module_utils/ansible_release.py 2026-05-18 21:15:21.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.20.5' +__version__ = '2.20.6' __author__ = 'Ansible, Inc.' __codename__ = "Good Times Bad Times" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/lib/ansible/module_utils/basic.py new/ansible_core-2.20.6/lib/ansible/module_utils/basic.py --- old/ansible_core-2.20.5/lib/ansible/module_utils/basic.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/lib/ansible/module_utils/basic.py 2026-05-18 21:15:21.000000000 +0200 @@ -2093,7 +2093,13 @@ stdout_changed = False for key, event in events: b_chunk = key.fileobj.read(32768) - if not b_chunk and b_chunk is not None: + if b_chunk is None: + # Non-blocking read returned None (no data currently available). + # This can happen with certain file-like objects or in edge cases. + # Skip this chunk and try again on next select iteration. + continue + if not b_chunk: + # Empty bytes received, EOF reached selector.unregister(key.fileobj) elif key.fileobj == cmd.stdout: stdout += b_chunk diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/lib/ansible/modules/git.py new/ansible_core-2.20.6/lib/ansible/modules/git.py --- old/ansible_core-2.20.5/lib/ansible/modules/git.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/lib/ansible/modules/git.py 2026-05-18 21:15:21.000000000 +0200 @@ -168,9 +168,11 @@ track_submodules: description: - - If V(true), submodules will track the latest commit on their - master branch (or other branch specified in C(.gitmodules)). If - V(false), submodules will be kept at the revision specified by the + - If V(true), submodules will track the latest commit on the branch specified in C(.gitmodules). + If no branch is specified in C(.gitmodules), it will use the remote HEAD. + - Currently, the value of remote is defaulted to C(origin). + Specifying the remote for the submodules is not supported. + - If V(false), submodules will be kept at the revision specified by the main project. This is equivalent to specifying the C(--remote) flag to git submodule update. type: bool @@ -566,6 +568,28 @@ return submodules +def get_submodule_branch(git_path, module, dest, submodule): + """Get the configured branch for a submodule from .gitmodules. + + Falls back to the remote HEAD if no branch is configured. + """ + cmd = [git_path, 'config', '-f', '.gitmodules', + f'submodule.{submodule}.branch'] + rc, out, _err = module.run_command(cmd, cwd=dest) + if rc == 0 and out.strip(): + return out.strip() + + # No branch configured in .gitmodules, use remote HEAD + submodule_path = os.path.join(dest, submodule) + cmd = [git_path, 'symbolic-ref', '--short', 'refs/remotes/origin/HEAD'] + rc, out, _err = module.run_command(cmd, cwd=submodule_path) + if rc == 0 and out.strip(): + # Returns e.g. "origin/main", strip the remote prefix + return out.strip().split('/', 1)[-1] + + return 'HEAD' + + def clone(git_path, module, repo, dest, remote, depth, version, bare, reference, refspec, git_version_used, verify_commit, separate_git_dir, result, gpg_allowlist, single_branch): """ makes a new git repo if it does not already exist """ @@ -959,10 +983,19 @@ module.fail_json(msg="Failed to fetch submodules: %s" % out + err) if track_submodules: - # Compare against submodule HEAD - # FIXME: determine this from .gitmodules - version = 'master' - after = get_submodule_versions(git_path, module, dest, '%s/%s' % (remote, version)) + # Compare each submodule against its configured remote branch + after = {} + for submodule in begin: + branch = get_submodule_branch(git_path, module, dest, submodule) + version_ref = f'{remote}/{branch}' if branch != 'HEAD' else 'HEAD' + submodule_path = os.path.join(dest, submodule) + cmd = [git_path, 'rev-parse', version_ref] + (rc, out, err) = module.run_command(cmd, cwd=submodule_path) + if rc != 0: + module.fail_json( + msg='Unable to determine hash of submodule %s at %s' % (submodule, version_ref), + stdout=out, stderr=err, rc=rc) + after[submodule] = out.strip() if begin != after: changed = True else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/lib/ansible/plugins/connection/psrp.py new/ansible_core-2.20.6/lib/ansible/plugins/connection/psrp.py --- old/ansible_core-2.20.5/lib/ansible/plugins/connection/psrp.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/lib/ansible/plugins/connection/psrp.py 2026-05-18 21:15:21.000000000 +0200 @@ -767,9 +767,14 @@ stderr_list += self.host.ui.stderr stderr = "".join([to_text(o) for o in stderr_list]) + log_stdout = stdout + log_stderr = stderr + if self._play_context.no_log: + log_stdout = log_stderr = '<censored due to no log>' + display.vvvvv("PSRP RC: %d" % rc, host=self._psrp_host) - display.vvvvv("PSRP STDOUT: %s" % stdout, host=self._psrp_host) - display.vvvvv("PSRP STDERR: %s" % stderr, host=self._psrp_host) + display.vvvvv(f"PSRP STDOUT: {log_stdout}", host=self._psrp_host) + display.vvvvv(f"PSRP STDERR: {log_stderr}", host=self._psrp_host) # reset the host back output back to defaults, needed if running # multiple pipelines on the same RunspacePool diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/lib/ansible/plugins/connection/winrm.py new/ansible_core-2.20.6/lib/ansible/plugins/connection/winrm.py --- old/ansible_core-2.20.5/lib/ansible/plugins/connection/winrm.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/lib/ansible/plugins/connection/winrm.py 2026-05-18 21:15:21.000000000 +0200 @@ -625,11 +625,16 @@ stdout = to_text(b_stdout) stderr = to_text(b_stderr) + log_stdout = stdout + log_stderr = stderr + if self._play_context.no_log: + log_stdout = log_stderr = '<censored due to no log>' + if from_exec: - display.vvvvv('WINRM RESULT <Response code %d, out %r, err %r>' % (rc, stdout, stderr), host=self._winrm_host) + display.vvvvv(f'WINRM RESULT <Response code {rc}, out {log_stdout!r}, err {log_stderr!r}>', host=self._winrm_host) display.vvvvvv('WINRM RC %d' % rc, host=self._winrm_host) - display.vvvvvv('WINRM STDOUT %s' % stdout, host=self._winrm_host) - display.vvvvvv('WINRM STDERR %s' % stderr, host=self._winrm_host) + display.vvvvvv(f'WINRM STDOUT {log_stdout}', host=self._winrm_host) + display.vvvvvv(f'WINRM STDERR {log_stderr}', host=self._winrm_host) # This is done after logging so we can still see the raw stderr for # debugging purposes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/lib/ansible/release.py new/ansible_core-2.20.6/lib/ansible/release.py --- old/ansible_core-2.20.5/lib/ansible/release.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/lib/ansible/release.py 2026-05-18 21:15:21.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.20.5' +__version__ = '2.20.6' __author__ = 'Ansible, Inc.' __codename__ = "Good Times Bad Times" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/test/integration/targets/git/tasks/main.yml new/ansible_core-2.20.6/test/integration/targets/git/tasks/main.yml --- old/ansible_core-2.20.5/test/integration/targets/git/tasks/main.yml 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/test/integration/targets/git/tasks/main.yml 2026-05-18 21:15:21.000000000 +0200 @@ -32,6 +32,7 @@ - import_tasks: no-destination.yml - import_tasks: specific-revision.yml - import_tasks: submodules.yml + - import_tasks: track-submodules-branch.yml - import_tasks: change-repo-url.yml - import_tasks: depth.yml - import_tasks: single-branch.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/test/integration/targets/git/tasks/track-submodules-branch.yml new/ansible_core-2.20.6/test/integration/targets/git/tasks/track-submodules-branch.yml --- old/ansible_core-2.20.5/test/integration/targets/git/tasks/track-submodules-branch.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.20.6/test/integration/targets/git/tasks/track-submodules-branch.yml 2026-05-18 21:15:21.000000000 +0200 @@ -0,0 +1,272 @@ +# +# Tests for track_submodules with non-master default branches +# +# Verifies that get_submodule_branch() correctly reads the branch from +# .gitmodules and falls back to remote HEAD, instead of hardcoding 'master'. +# + +# --- Setup --- + +- name: TRACK-SUBMODULES-BRANCH | allow local file transport for submodule clones + shell: git config --global protocol.file.allow always + +- name: TRACK-SUBMODULES-BRANCH | create local repo dirs + file: + path: "{{ item }}" + state: directory + loop: + - "{{ repo_dir }}/track_sub_remote" + - "{{ repo_dir }}/track_sub_parent" + +- name: TRACK-SUBMODULES-BRANCH | create a submodule repo with 'main' as default branch + shell: | + set -eEu + git init + echo "initial" > file1.txt + git add file1.txt + git commit -m "initial commit on main" + args: + chdir: "{{ repo_dir }}/track_sub_remote" + +- name: TRACK-SUBMODULES-BRANCH | create parent repo with submodule pointing to 'main' + shell: | + set -eEu + git init + echo "parent" > README.md + git add README.md + git commit -m "parent initial" + + git submodule add "{{ repo_dir }}/track_sub_remote" sub1 + git commit -m "add submodule sub1" + args: + chdir: "{{ repo_dir }}/track_sub_parent" + +- name: TRACK-SUBMODULES-BRANCH | set explicit branch in .gitmodules + shell: | + set -eEu + git config -f .gitmodules submodule.sub1.branch main + git add .gitmodules + git commit -m "set submodule branch to main" + args: + chdir: "{{ repo_dir }}/track_sub_parent" + +# --- Test 1: track_submodules detects updates on configured branch (main) --- + +- name: TRACK-SUBMODULES-BRANCH | clean checkout dir + file: + state: absent + path: "{{ checkout_dir }}" + +- name: TRACK-SUBMODULES-BRANCH | clone parent with recursive submodules + git: + repo: "{{ repo_dir }}/track_sub_parent" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + +- name: TRACK-SUBMODULES-BRANCH | record initial state (should not be changed) + git: + repo: "{{ repo_dir }}/track_sub_parent" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + register: track_initial + +- name: TRACK-SUBMODULES-BRANCH | assert no change on first re-run + assert: + that: + - not track_initial.changed + +- name: TRACK-SUBMODULES-BRANCH | push a new commit to the submodule remote + shell: | + set -eEu + echo "update" > file2.txt + git add file2.txt + git commit -m "second commit on main" + args: + chdir: "{{ repo_dir }}/track_sub_remote" + +- name: TRACK-SUBMODULES-BRANCH | re-run with track_submodules to detect remote update + git: + repo: "{{ repo_dir }}/track_sub_parent" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + register: track_after_update + +- name: TRACK-SUBMODULES-BRANCH | assert change detected after submodule remote was updated + assert: + that: + - track_after_update.changed + +# --- Test 2: track_submodules works with remote HEAD fallback (no branch in .gitmodules) --- + +- name: TRACK-SUBMODULES-BRANCH | create dirs for fallback test + file: + path: "{{ item }}" + state: directory + loop: + - "{{ repo_dir }}/track_sub_remote_fb" + - "{{ repo_dir }}/track_sub_parent_fb" + +- name: TRACK-SUBMODULES-BRANCH | create submodule repo for fallback test + shell: | + set -eEu + git init + echo "initial" > file1.txt + git add file1.txt + git commit -m "initial commit" + args: + chdir: "{{ repo_dir }}/track_sub_remote_fb" + +- name: TRACK-SUBMODULES-BRANCH | create parent repo without explicit branch in .gitmodules + shell: | + set -eEu + git init + echo "parent" > README.md + git add README.md + git commit -m "parent initial" + + git submodule add "{{ repo_dir }}/track_sub_remote_fb" sub_fb + git commit -m "add submodule sub_fb (no explicit branch)" + args: + chdir: "{{ repo_dir }}/track_sub_parent_fb" + +- name: TRACK-SUBMODULES-BRANCH | clean checkout dir for fallback test + file: + state: absent + path: "{{ checkout_dir }}" + +- name: TRACK-SUBMODULES-BRANCH | clone parent (fallback test) + git: + repo: "{{ repo_dir }}/track_sub_parent_fb" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + +- name: TRACK-SUBMODULES-BRANCH | push a new commit to fallback submodule remote + shell: | + set -eEu + echo "new content" > file2.txt + git add file2.txt + git commit -m "second commit" + args: + chdir: "{{ repo_dir }}/track_sub_remote_fb" + +- name: TRACK-SUBMODULES-BRANCH | re-run track_submodules with fallback branch detection + git: + repo: "{{ repo_dir }}/track_sub_parent_fb" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + register: track_fallback_update + +- name: TRACK-SUBMODULES-BRANCH | assert change detected with fallback branch + assert: + that: + - track_fallback_update.changed + +# --- Test 3: two submodules with different default branches --- + +- name: TRACK-SUBMODULES-BRANCH | create dirs for multi-branch test + file: + path: "{{ item }}" + state: directory + loop: + - "{{ repo_dir }}/track_sub_remote_master" + - "{{ repo_dir }}/track_sub_remote_main" + - "{{ repo_dir }}/track_sub_parent_multi" + +- name: TRACK-SUBMODULES-BRANCH | create submodule repo with 'develop' branch + shell: | + set -eEu + git init + echo "initial" > file1.txt + git add file1.txt + git commit -m "initial on main" + git checkout -b develop + echo "dev" > file2.txt + git add file2.txt + git commit -m "initial on develop" + args: + chdir: "{{ repo_dir }}/track_sub_remote_master" + +- name: TRACK-SUBMODULES-BRANCH | create submodule repo using 'main' + shell: | + set -eEu + git init + echo "initial" > file1.txt + git add file1.txt + git commit -m "initial on main" + args: + chdir: "{{ repo_dir }}/track_sub_remote_main" + +- name: TRACK-SUBMODULES-BRANCH | create parent with two submodules on different branches + shell: | + set -eEu + git init + echo "parent" > README.md + git add README.md + git commit -m "parent initial" + + git submodule add -b develop "{{ repo_dir }}/track_sub_remote_master" sub_develop + git submodule add "{{ repo_dir }}/track_sub_remote_main" sub_main + git config -f .gitmodules submodule.sub_main.branch main + git add .gitmodules + git commit -m "add two submodules with different branches" + args: + chdir: "{{ repo_dir }}/track_sub_parent_multi" + +- name: TRACK-SUBMODULES-BRANCH | clean checkout dir for multi-branch test + file: + state: absent + path: "{{ checkout_dir }}" + +- name: TRACK-SUBMODULES-BRANCH | clone parent with two submodules + git: + repo: "{{ repo_dir }}/track_sub_parent_multi" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + +- name: TRACK-SUBMODULES-BRANCH | re-run should show no changes + git: + repo: "{{ repo_dir }}/track_sub_parent_multi" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + register: track_multi_initial + +- name: TRACK-SUBMODULES-BRANCH | assert no change on re-run with two submodules + assert: + that: + - not track_multi_initial.changed + +- name: TRACK-SUBMODULES-BRANCH | push update to only the 'main' submodule + shell: | + set -eEu + echo "update" > file2.txt + git add file2.txt + git commit -m "second commit on main" + args: + chdir: "{{ repo_dir }}/track_sub_remote_main" + +- name: TRACK-SUBMODULES-BRANCH | re-run should detect change in sub_main only + git: + repo: "{{ repo_dir }}/track_sub_parent_multi" + dest: "{{ checkout_dir }}" + recursive: yes + track_submodules: yes + register: track_multi_update + +- name: TRACK-SUBMODULES-BRANCH | assert change detected with mixed-branch submodules + assert: + that: + - track_multi_update.changed + +# --- Cleanup --- + +- name: TRACK-SUBMODULES-BRANCH | clean checkout dir + file: + state: absent + path: "{{ checkout_dir }}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/test/lib/ansible_test/_internal/host_configs.py new/ansible_core-2.20.6/test/lib/ansible_test/_internal/host_configs.py --- old/ansible_core-2.20.5/test/lib/ansible_test/_internal/host_configs.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/test/lib/ansible_test/_internal/host_configs.py 2026-05-18 21:15:21.000000000 +0200 @@ -384,6 +384,7 @@ super().apply_defaults(context, defaults) self.become = self.become or defaults.become + self.name = defaults.name @property def have_root(self) -> bool: @@ -413,6 +414,7 @@ super().apply_defaults(context, defaults) self.connection = self.connection or defaults.connection + self.name = defaults.name @dataclasses.dataclass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.20.5/test/units/module_utils/basic/test_run_command.py new/ansible_core-2.20.6/test/units/module_utils/basic/test_run_command.py --- old/ansible_core-2.20.5/test/units/module_utils/basic/test_run_command.py 2026-04-21 02:44:49.000000000 +0200 +++ new/ansible_core-2.20.6/test/units/module_utils/basic/test_run_command.py 2026-05-18 21:15:21.000000000 +0200 @@ -255,3 +255,81 @@ assert subprocess_mock.Popen.call_args[1]['pass_fds'] == (101, 42) assert subprocess_mock.Popen.call_args[1]['close_fds'] is True + + +class TestRunCommandNoneRead: + """ + Test handling of read() returning None from non-blocking pipes. + + This tests the fix for issue #86920 where read() can return None + in certain edge cases with non-blocking I/O, which would cause + TypeError when trying to concatenate None to bytes. + """ + + class NoneReturningBytesIO(SpecialBytesIO): + """ + BytesIO that returns None on first read, then actual data. + + This simulates edge cases where non-blocking read() returns None + to indicate "no data available right now" rather than empty bytes. + """ + + def __init__(self, *args, **kwargs): + # Pop 'data' before calling super().__init__() since BytesIO doesn't accept it + self.data = kwargs.pop('data', b'test output') + self.read_count = 0 + super(TestRunCommandNoneRead.NoneReturningBytesIO, self).__init__(*args, **kwargs) + + def read(self, size=-1): + self.read_count += 1 + if self.read_count == 1: + # First read returns None (no data available) + return None + elif self.read_count == 2: + # Second read returns actual data + return self.data + else: + # Subsequent reads return empty bytes (EOF) + return b'' + + @pytest.mark.parametrize('stdin', [{}], indirect=['stdin']) + def test_none_from_stdout_read(self, mocker, rc_am): + """Test that None returned from stdout.read() doesn't cause TypeError.""" + rc_am._subprocess._output = { + mocker.sentinel.stdout: + self.NoneReturningBytesIO(fh=mocker.sentinel.stdout, data=b'command output'), + mocker.sentinel.stderr: + SpecialBytesIO(b'', fh=mocker.sentinel.stderr) + } + (rc, stdout, stderr) = rc_am.run_command('/bin/test') + assert rc == 0 + assert stdout == 'command output' + assert stderr == '' + + @pytest.mark.parametrize('stdin', [{}], indirect=['stdin']) + def test_none_from_stderr_read(self, mocker, rc_am): + """Test that None returned from stderr.read() doesn't cause TypeError.""" + rc_am._subprocess._output = { + mocker.sentinel.stdout: + SpecialBytesIO(b'', fh=mocker.sentinel.stdout), + mocker.sentinel.stderr: + self.NoneReturningBytesIO(fh=mocker.sentinel.stderr, data=b'error output') + } + (rc, stdout, stderr) = rc_am.run_command('/bin/test') + assert rc == 0 + assert stdout == '' + assert stderr == 'error output' + + @pytest.mark.parametrize('stdin', [{}], indirect=['stdin']) + def test_none_from_both_pipes(self, mocker, rc_am): + """Test that None returned from both pipes doesn't cause TypeError.""" + rc_am._subprocess._output = { + mocker.sentinel.stdout: + self.NoneReturningBytesIO(fh=mocker.sentinel.stdout, data=b'stdout data'), + mocker.sentinel.stderr: + self.NoneReturningBytesIO(fh=mocker.sentinel.stderr, data=b'stderr data') + } + (rc, stdout, stderr) = rc_am.run_command('/bin/test') + assert rc == 0 + assert stdout == 'stdout data' + assert stderr == 'stderr data' ++++++ ansible_core-2.20.5.tar.gz.sha256 -> ansible_core-2.20.6.tar.gz.sha256 ++++++ --- /work/SRC/openSUSE:Factory/ansible-core/ansible_core-2.20.5.tar.gz.sha256 2026-04-21 12:47:41.229558453 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core.new.1966/ansible_core-2.20.6.tar.gz.sha256 2026-05-19 17:50:59.602183122 +0200 @@ -1 +1 @@ -82e3049d95e6e02e5d20d4a5a8e10533a55e0cc52e878e4cf77166c45410f16f ansible_core-2.20.5.tar.gz +3066c430e8cba46777bf736ebcd085c90b0d7664c3fbd8c5b85227f8579cdcbf ansible_core-2.20.6.tar.gz
