From 576654ceeafd84fe7a00c4c6650ecf6d73ca99fb Mon Sep 17 00:00:00 2001 From: Rolf Krahl Date: Sat, 16 May 2026 14:16:27 +0200 Subject: [PATCH 1/4] Add a configuration section 'zypper' with one flag: 'auto_agree_with_licenses' --- etc/auto-patch.cfg | 6 ++++++ scripts/auto-patch.py | 3 +++ tests/conftest.py | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/etc/auto-patch.cfg b/etc/auto-patch.cfg index c5a249e..040bc11 100644 --- a/etc/auto-patch.cfg +++ b/etc/auto-patch.cfg @@ -3,6 +3,12 @@ # The configuration values below are the default values commented out # by a leading '!'. +[zypper] +# Configuration for the zypper call. The /auto_agree_with_licenses/ +# flag controls whether or not to add the `--auto-agree-with-licenses` +# flag to the `zypper patch` call. +!auto_agree_with_licenses = no + [mailreport] # A mail report may be sent if patches are available or have been # installed. This is controlled by the /report/ flag, default is on. diff --git a/scripts/auto-patch.py b/scripts/auto-patch.py index c420e27..bb948c4 100644 --- a/scripts/auto-patch.py +++ b/scripts/auto-patch.py @@ -29,6 +29,9 @@ config_files = "/etc/auto-patch.cfg" config_defaults = { + 'zypper': { + 'auto_agree_with_licenses': "no", + }, 'mailreport': { 'report': "on", 'hostname': socket.getfqdn(), diff --git a/tests/conftest.py b/tests/conftest.py index 27630a6..e2f9c0c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -133,7 +133,7 @@ def get_caller(cls, case, config=None): return cls(zypper_results, config) def _create_config(self, config): - d = { 'mailreport': {}, 'retry': {}, 'logging': {} } + d = { 'zypper': {}, 'mailreport': {}, 'retry': {}, 'logging': {} } if config is not None: for k in config.keys(): d[k].update(config[k]) From d7f90b9236a76b0e5c3e73ab7554d6b54cf3a838 Mon Sep 17 00:00:00 2001 From: Rolf Krahl Date: Sat, 16 May 2026 14:30:26 +0200 Subject: [PATCH 2/4] Add the flag --auto-agree-with-licenses to the zypper patch call --- scripts/auto-patch.py | 2 ++ tests/conftest.py | 1 + 2 files changed, 3 insertions(+) diff --git a/scripts/auto-patch.py b/scripts/auto-patch.py index bb948c4..e003189 100644 --- a/scripts/auto-patch.py +++ b/scripts/auto-patch.py @@ -253,6 +253,8 @@ def patch(self, stdout=None): args = ["--quiet", "--non-interactive", "patch", "--skip-interactive"] if self.version >= Version("1.14.69"): args.append("--skip-not-applicable-patches") + if config['zypper'].getboolean('auto_agree_with_licenses'): + args.append("--auto-agree-with-licenses") return self.call(args, stdout=stdout) def ps(self, stdout=None): diff --git a/tests/conftest.py b/tests/conftest.py index e2f9c0c..e9cfab1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ def get_zypper_argument_parser(): parser.add_argument('subcmd', nargs='?') parser.add_argument('--skip-interactive', action='store_true') parser.add_argument('--skip-not-applicable-patches', action='store_true') + parser.add_argument('--auto-agree-with-licenses', action='store_true') return parser zypper_arg_parser = get_zypper_argument_parser() From b6da402e43909ccd45a736db9c4d39177bc730c1 Mon Sep 17 00:00:00 2001 From: Rolf Krahl Date: Sat, 16 May 2026 16:51:13 +0200 Subject: [PATCH 3/4] Add test cases for the auto_agree_with_licenses configuration flag --- tests/test_02_exitcode.py | 13 --------- tests/test_02_licenses.py | 50 +++++++++++++++++++++++++++++++++++ tests/zypper-result-data.json | 35 ++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 tests/test_02_licenses.py diff --git a/tests/test_02_exitcode.py b/tests/test_02_exitcode.py index 499c84f..59813e1 100644 --- a/tests/test_02_exitcode.py +++ b/tests/test_02_exitcode.py @@ -42,16 +42,3 @@ def test_error_scripterr(tmpdir): caller = AutoPatchCaller.get_caller("err_scripterr") caller.run(exitcode=107) caller.check_report(extra_msg="ERROR:") - - -def test_error_license(tmpdir): - """Patch fails due to missing license confirmation. - - A patch for a non-free package requires agreement with the terms - of the license, which doesn't work in non-interactive mode. The - patch command fails with ZypperLibraryError. - """ - with tmpdir.as_cwd(): - caller = AutoPatchCaller.get_caller("no_license_consent") - caller.run(exitcode=4) - caller.check_report(extra_msg="ERROR:") diff --git a/tests/test_02_licenses.py b/tests/test_02_licenses.py new file mode 100644 index 0000000..37355b0 --- /dev/null +++ b/tests/test_02_licenses.py @@ -0,0 +1,50 @@ +"""Special case: a patch requires accepting a third party license. + +A patch for a non-free package requires agreement with the terms of +the license. By default, the zypper patch call fails with +ZypperLibraryError in non-interactive mode. This can either be fixed +by manual intervention by the admin or by setting the +auto_agree_with_licenses configuration flag to yes. +""" + +import pytest +from conftest import AutoPatchCaller + + +def test_license_default(tmpdir): + """Default configuration. + + The patch command fails with ZypperLibraryError. + """ + with tmpdir.as_cwd(): + caller = AutoPatchCaller.get_caller("no_license_consent") + caller.run(exitcode=4) + caller.check_report(extra_msg="ERROR:") + + +@pytest.mark.parametrize("flag", ["0", "no", "false", "off"]) +def test_license_auto_agree_no(tmpdir, flag): + """Explicitely switch off auto_agree_with_licenses. + + This is the default, so we won't get any different behavior. + Try out different representations of the negative value. + """ + with tmpdir.as_cwd(): + cfg = { 'zypper': {'auto_agree_with_licenses': flag} } + caller = AutoPatchCaller.get_caller("no_license_consent", config=cfg) + caller.run(exitcode=4) + caller.check_report(extra_msg="ERROR:") + + +@pytest.mark.parametrize("flag", ["1", "yes", "true", "on"]) +def test_license_auto_agree_yes(tmpdir, flag): + """Explicitely switch on auto_agree_with_licenses. + + In this case, the patch is applied successfully. + Try out different representations of the affirmative value. + """ + with tmpdir.as_cwd(): + cfg = { 'zypper': {'auto_agree_with_licenses': flag} } + caller = AutoPatchCaller.get_caller("auto_license_consent", config=cfg) + caller.run() + caller.check_report() diff --git a/tests/zypper-result-data.json b/tests/zypper-result-data.json index 928dc74..dbdf305 100644 --- a/tests/zypper-result-data.json +++ b/tests/zypper-result-data.json @@ -713,5 +713,40 @@ "--skip-not-applicable-patches" ] } + ], + "auto_license_consent": [ + { + "cmd": null, + "stdout": "zypper 1.14.94", + "capture": false + }, + { + "cmd": "patch-check", + "returncode": 100, + "stdout": "\nCategory | Patches\n------------+--------\nrecommended | 1\n\n1 patch needed (0 security patches)\n" + }, + { + "cmd": "list-patches", + "stdout": "\nRepository | Name | Category | Severity | Interactive | Status | Summary\n-------------------------------------------------------------+-----------------------------+-------------+----------+-------------+--------+-----------------------------------------------------\nUpdate repository with updates from SUSE Linux Enterprise 15 | openSUSE-SLE-15.6-2026-1084 | recommended | moderate | --- | needed | Recommended update for nvidia-open-driver-G06-signed\n\n1 patch needed (0 security patches)\n" + }, + { + "cmd": "patch", + "stdout": "\nThe following 10 packages are going to be upgraded:\n libnvidia-gpucomp nvidia-common-G06 nvidia-compute-G06 nvidia-compute-utils-G06 nvidia-gl-G06 nvidia-modprobe nvidia-open-driver-G06-signed-kmp-meta nvidia-persistenced nvidia-userspace-meta-G06 nvidia-video-G06\n\nThe following NEW package is going to be installed:\n nvidia-open-driver-G06-signed-kmp-default-580.126.18_k6.4.0_150600.23.87-150600.3.85.1\n\nThe following NEW patch is going to be installed:\n openSUSE-SLE-15.6-2026-1084\n\nThe following package is going to be REMOVED:\n nvidia-open-driver-G06-signed-kmp-default-580.126.09_k6.4.0_150600.23.81-150600.3.80.1\n\n10 packages to upgrade, 1 new, 1 to remove.\n\nPackage download size: 294.3 MiB\n\nPackage install size change:\n | 997.5 MiB required by packages that will be installed\n 12.9 KiB | - 997.5 MiB released by packages that will be removed\n\nBackend: classic_rpmtrans\nContinue? [y/n/v/...? shows all options] (y): y\n", + "args": [ + "--quiet", + "--non-interactive", + "patch", + "--skip-interactive", + "--skip-not-applicable-patches", + "--auto-agree-with-licenses" + ] + }, + { + "cmd": "patch-check", + "stdout": "\n0 patches needed (0 security patches)\n" + }, + { + "cmd": "ps" + } ] } From cebb38dedfc389e45039355c0e66abf6724a87b9 Mon Sep 17 00:00:00 2001 From: Rolf Krahl Date: Sat, 16 May 2026 17:12:15 +0200 Subject: [PATCH 4/4] Update changelog --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6da21d5..b2fd02b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,11 @@ Changelog 1.3.0 (not yet released) ~~~~~~~~~~~~~~~~~~~~~~~~ +New features +------------ + ++ `#25`_, `#30`_: add a `auto_agree_with_licenses` configuration flag. + Behavioral change ----------------- @@ -19,10 +24,12 @@ Internal + `#26`_, `#27`_, `#29`_: review test suite. .. _#24: https://github.com/RKrahl/auto-patch/issues/24 +.. _#25: https://github.com/RKrahl/auto-patch/issues/25 .. _#26: https://github.com/RKrahl/auto-patch/pull/26 .. _#27: https://github.com/RKrahl/auto-patch/pull/27 .. _#28: https://github.com/RKrahl/auto-patch/pull/28 .. _#29: https://github.com/RKrahl/auto-patch/pull/29 +.. _#30: https://github.com/RKrahl/auto-patch/pull/30 .. _changes-1_2_0: