diff --git a/CHANGES.rst b/CHANGES.rst index 462e681..f64b4bc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,11 +13,15 @@ Misc Bug fixes and minor changes --------------------------- ++ `#15`_, `#18`_: Send a report also in the case of certain errors + from zypper. + `#16`_, `#17`_: Review of the test framework. .. _#14: https://github.com/RKrahl/auto-patch/pull/14 +.. _#15: https://github.com/RKrahl/auto-patch/issues/15 .. _#16: https://github.com/RKrahl/auto-patch/issues/16 .. _#17: https://github.com/RKrahl/auto-patch/pull/17 +.. _#18: https://github.com/RKrahl/auto-patch/pull/18 1.1.0 (2022-10-03) diff --git a/scripts/auto-patch.py b/scripts/auto-patch.py index f48b576..48c0266 100644 --- a/scripts/auto-patch.py +++ b/scripts/auto-patch.py @@ -70,7 +70,8 @@ def logging_add_report(cfg, stream): root = logging.getLogger() report_hdlr = logging.StreamHandler(stream=stream) report_hdlr.setLevel(cfg.get('report_level')) - report_hdlr.setFormatter(logging.Formatter(fmt="\n%(message)s")) + fmt = "\n%(levelname)s: %(message)s" + report_hdlr.setFormatter(logging.Formatter(fmt=fmt)) root.addHandler(report_hdlr) try: yield None @@ -292,31 +293,40 @@ def patch(stdout=None): err.Message += (". Giving up after %d tries." % try_count) raise err +def make_report(logfile): + logfile.seek(0) + report = logfile.read() + log.debug(report) + if config['mailreport'].getboolean('report'): + msg = EmailMessage() + msg.set_content(report) + msg['From'] = config['mailreport'].get('mailfrom') + msg['To'] = config['mailreport'].get('mailto') + msg['Subject'] = config['mailreport'].get('subject') + mailhost = config['mailreport'].get('mailhost') + with smtplib.SMTP(mailhost) as smtp: + smtp.send_message(msg) + def main(): setup_logging(config['logging']) with tempfile.TemporaryFile(mode='w+t') as tmpf: + exit_code = 0 with logging_add_report(config['logging'], tmpf): - have_patches = patch(stdout=tmpf) - if have_patches: - tmpf.seek(0) - report = tmpf.read() - log.debug(report) - if config['mailreport'].getboolean('report'): - msg = EmailMessage() - msg.set_content(report) - msg['From'] = config['mailreport'].get('mailfrom') - msg['To'] = config['mailreport'].get('mailto') - msg['Subject'] = config['mailreport'].get('subject') - mailhost = config['mailreport'].get('mailhost') - with smtplib.SMTP(mailhost) as smtp: - smtp.send_message(msg) + try: + have_patches = patch(stdout=tmpf) + except (ZypperCommitError, ZypperRPMScriptfailed, + ZypperSignal) as err: + log.error(err) + exit_code = err.ExitCode + if exit_code or have_patches: + make_report(tmpf) + return exit_code if __name__ == "__main__": try: - main() + exit_code = main() except (ZypperPrivilegesError, ZypperNoReposError, ZypperLockedError, - ZypperCommitError, ZypperSignal, ZypperReposSkipped, - ZypperRPMScriptfailed) as err: + ZypperReposSkipped) as err: log.error(err) sys.exit(err.ExitCode) except ZypperExitException as err: @@ -327,3 +337,4 @@ def main(): log.critical("Internal error %s: %s", type(err).__name__, err, exc_info=err) sys.exit(-1) + sys.exit(exit_code) diff --git a/tests/conftest.py b/tests/conftest.py index 2590cdb..fed0cc4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -122,7 +122,7 @@ def run(self, exitcode=0): p.join() assert p.exitcode == exitcode - def check_report(self): + def check_report(self, extra_msg=None): with open("report.pickle", "rb") as f: host = pickle.load(f) msg = pickle.load(f) @@ -132,4 +132,7 @@ def check_report(self): idx = body.find(res.stdout, idx) assert idx >= 0 idx += len(res.stdout) + if extra_msg: + idx = body.find(extra_msg, idx) + assert idx >= 0 return host, msg diff --git a/tests/test_02_exitcode.py b/tests/test_02_exitcode.py index e7fc4a5..23ccb15 100644 --- a/tests/test_02_exitcode.py +++ b/tests/test_02_exitcode.py @@ -34,3 +34,15 @@ def test_error_permission(tmpdir): # assert that no mail report has been sent: with pytest.raises(FileNotFoundError): caller.check_report() + + +def test_error_scripterr(tmpdir): + """A %post() scriptlet from one of the packages failed. + + This may happen, though rarely. The auto-patch should report the + error, but still deliver a report. + """ + with tmpdir.as_cwd(): + caller = AutoPatchCaller.get_caller("err_scripterr") + caller.run(exitcode=107) + caller.check_report(extra_msg="ERROR:") diff --git a/tests/zypper-result-data.json b/tests/zypper-result-data.json index 1394606..a4ec2ae 100644 --- a/tests/zypper-result-data.json +++ b/tests/zypper-result-data.json @@ -435,5 +435,21 @@ "returncode": 5, "stderr": "Root privileges are required to run this command.\n" } + ], + "err_scripterr": [ + { + "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 | Since | Summary\n-------------------------------------------------------------+-----------------------------+-------------+----------+-------------+--------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\nUpdate repository with updates from SUSE Linux Enterprise 15 | openSUSE-SLE-15.6-2025-2574 | recommended | moderate | --- | needed | 2025-08-08 | Recommended update for python3-PyNaCl, python3-atomicwrites, python3-cryptography, python3-cryptography-vectors, python3-more-itertools, python3-paramiko, python3-pip, python3-pyOpenSSL, python3-pytest, python3-setuptools\n\n1 patch needed (0 security patches)\n\n" + }, + { + "cmd": "patch", + "returncode": 107, + "stdout": "\nThe following 2 packages are going to be upgraded:\n python3-pip python3-pytest\n\nThe following NEW patch is going to be installed:\n openSUSE-SLE-15.6-2025-2574\n\n2 packages to upgrade.\n\nPackage download size: 2.2 MiB\n\nPackage install size change:\n | 10.2 MiB required by packages that will be installed\n -1.8 MiB | - 12.0 MiB released by packages that will be removed\n\nBackend: classic_rpmtrans\nContinue? [y/n/v/...? shows all options] (y): y\n\n" + } ] }