Repository: borgbackup/borg

Issue:
cmdline option to switch off "terminating with X status"
This is currently failing 1 test when borg.exe is available via PATH as this line appears in output.
The test is counting output lines and get 1 line more than expected (this is a bit stupid test, but this is not topic of this issue).

For some usages, we do not want that output (e.g. if borg is directly invoked from a script, the rc can be checked directly and is maybe not needed in the log). If the invoking script does no rc checking though and the backup is monitored only by looking at / analyzing the log, we want that line in the log.

So, we need something like --show-rc option.



Discussion:

Resolving patch:
diff --git a/borg/archiver.py b/borg/archiver.py
--- a/borg/archiver.py
+++ b/borg/archiver.py
@@ -571,6 +571,8 @@ def build_parser(self, args=None, prog=None):
         common_parser = argparse.ArgumentParser(add_help=False, prog=prog)
         common_parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False,
                                    help='verbose output')
+        common_parser.add_argument('--show-rc', dest='show_rc', action='store_true', default=False,
+                                   help='show/log the return code (rc)')
         common_parser.add_argument('--no-files-cache', dest='cache_files', action='store_false',
                                    help='do not load/update the file metadata cache used to detect unchanged files')
         common_parser.add_argument('--umask', dest='umask', type=lambda s: int(s, 8), default=RemoteRepository.umask, metavar='M',
@@ -985,7 +987,21 @@ def build_parser(self, args=None, prog=None):
                                help='additional help on TOPIC')
         return parser
 
-    def run(self, args=None):
+    def parse_args(self, args=None):
+        # We can't use argparse for "serve" since we don't want it to show up in "Available commands"
+        if args:
+            args = self.preprocess_args(args)
+        parser = self.build_parser(args)
+        args = parser.parse_args(args or ['-h'])
+        update_excludes(args)
+        return args
+
+    def run(self, args):
+        os.umask(args.umask)  # early, before opening files
+        self.verbose = args.verbose
+        RemoteRepository.remote_path = args.remote_path
+        RemoteRepository.umask = args.umask
+        setup_logging()
         check_extension_modules()
         keys_dir = get_keys_dir()
         if not os.path.exists(keys_dir):
@@ -1002,19 +1018,6 @@ def run(self, args=None):
                     # For information about cache directory tags, see:
                     #       http://www.brynosaurus.com/cachedir/
                     """).lstrip())
-
-        # We can't use argparse for "serve" since we don't want it to show up in "Available commands"
-        if args:
-            args = self.preprocess_args(args)
-        parser = self.build_parser(args)
-
-        args = parser.parse_args(args or ['-h'])
-        self.verbose = args.verbose
-        setup_logging()
-        os.umask(args.umask)
-        RemoteRepository.remote_path = args.remote_path
-        RemoteRepository.umask = args.umask
-        update_excludes(args)
         if is_slow_msgpack():
             logger.warning("Using a pure-python msgpack! This will result in lower performance.")
         return args.func(args)
@@ -1060,9 +1063,10 @@ def main():  # pragma: no cover
     sys.stderr = io.TextIOWrapper(sys.stderr.buffer, sys.stderr.encoding, 'replace', line_buffering=True)
     setup_signal_handlers()
     archiver = Archiver()
+    msg = None
+    args = archiver.parse_args(sys.argv[1:])
     try:
-        msg = None
-        exit_code = archiver.run(sys.argv[1:])
+        exit_code = archiver.run(args)
     except Error as e:
         msg = e.get_message()
         if e.traceback:
@@ -1079,16 +1083,17 @@ def main():  # pragma: no cover
         exit_code = EXIT_ERROR
     if msg:
         logger.error(msg)
-    exit_msg = 'terminating with %s status, rc %d'
-    if exit_code == EXIT_SUCCESS:
-        logger.info(exit_msg % ('success', exit_code))
-    elif exit_code == EXIT_WARNING:
-        logger.warning(exit_msg % ('warning', exit_code))
-    elif exit_code == EXIT_ERROR:
-        logger.error(exit_msg % ('error', exit_code))
-    else:
-        # if you see 666 in output, it usually means exit_code was None
-        logger.error(exit_msg % ('abnormal', exit_code or 666))
+    if args.show_rc:
+        exit_msg = 'terminating with %s status, rc %d'
+        if exit_code == EXIT_SUCCESS:
+            logger.info(exit_msg % ('success', exit_code))
+        elif exit_code == EXIT_WARNING:
+            logger.warning(exit_msg % ('warning', exit_code))
+        elif exit_code == EXIT_ERROR:
+            logger.error(exit_msg % ('error', exit_code))
+        else:
+            # if you see 666 in output, it usually means exit_code was None
+            logger.error(exit_msg % ('abnormal', exit_code or 666))
     sys.exit(exit_code)
 
 
diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py
--- a/borg/testsuite/archiver.py
+++ b/borg/testsuite/archiver.py
@@ -93,7 +93,8 @@ def exec_cmd(*args, archiver=None, fork=False, exe=None, **kw):
             sys.stdout = sys.stderr = output = StringIO()
             if archiver is None:
                 archiver = Archiver()
-            ret = archiver.run(list(args))
+            args = archiver.parse_args(list(args))
+            ret = archiver.run(args)
             return ret, output.getvalue()
         finally:
             sys.stdin, sys.stdout, sys.stderr = stdin, stdout, stderr


Test patch: