summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAravind Vasudevan <aravindvasudev@google.com>2023-09-14 22:54:04 +0000
committerLUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-09-18 20:06:30 +0000
commitb8fd19215f59f7f8dbe69528aefca700a2190ecd (patch)
tree6f6ac455d16b0e58fd6cf16475d11ba7407883ca
parent7a1f1f70f0587795e2b6979adf7eac389037de57 (diff)
downloadgit-repo-b8fd19215f59f7f8dbe69528aefca700a2190ecd.tar.gz
main: Use repo logger
Bug: b/292704435 Change-Id: Ica02e4c00994a2f64083bb36e8f4ee8aa45d76bd Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/386454 Reviewed-by: Jason Chang <jasonnc@google.com> Commit-Queue: Aravind Vasudevan <aravindvasudev@google.com> Tested-by: Aravind Vasudevan <aravindvasudev@google.com>
-rwxr-xr-xmain.py120
-rw-r--r--repo_logging.py25
-rw-r--r--tests/test_repo_logging.py66
3 files changed, 104 insertions, 107 deletions
diff --git a/main.py b/main.py
index 1c7f0af6..bafa64df 100755
--- a/main.py
+++ b/main.py
@@ -32,6 +32,8 @@ import textwrap
32import time 32import time
33import urllib.request 33import urllib.request
34 34
35from repo_logging import RepoLogger
36
35 37
36try: 38try:
37 import kerberos 39 import kerberos
@@ -69,6 +71,9 @@ from wrapper import Wrapper
69from wrapper import WrapperPath 71from wrapper import WrapperPath
70 72
71 73
74logger = RepoLogger(__file__)
75
76
72# NB: These do not need to be kept in sync with the repo launcher script. 77# NB: These do not need to be kept in sync with the repo launcher script.
73# These may be much newer as it allows the repo launcher to roll between 78# These may be much newer as it allows the repo launcher to roll between
74# different repo releases while source versions might require a newer python. 79# different repo releases while source versions might require a newer python.
@@ -82,25 +87,25 @@ MIN_PYTHON_VERSION_SOFT = (3, 6)
82MIN_PYTHON_VERSION_HARD = (3, 6) 87MIN_PYTHON_VERSION_HARD = (3, 6)
83 88
84if sys.version_info.major < 3: 89if sys.version_info.major < 3:
85 print( 90 logger.error(
86 "repo: error: Python 2 is no longer supported; " 91 "repo: error: Python 2 is no longer supported; "
87 "Please upgrade to Python {}.{}+.".format(*MIN_PYTHON_VERSION_SOFT), 92 "Please upgrade to Python %d.%d+.",
88 file=sys.stderr, 93 *MIN_PYTHON_VERSION_SOFT,
89 ) 94 )
90 sys.exit(1) 95 sys.exit(1)
91else: 96else:
92 if sys.version_info < MIN_PYTHON_VERSION_HARD: 97 if sys.version_info < MIN_PYTHON_VERSION_HARD:
93 print( 98 logger.error(
94 "repo: error: Python 3 version is too old; " 99 "repo: error: Python 3 version is too old; "
95 "Please upgrade to Python {}.{}+.".format(*MIN_PYTHON_VERSION_SOFT), 100 "Please upgrade to Python %d.%d+.",
96 file=sys.stderr, 101 *MIN_PYTHON_VERSION_SOFT,
97 ) 102 )
98 sys.exit(1) 103 sys.exit(1)
99 elif sys.version_info < MIN_PYTHON_VERSION_SOFT: 104 elif sys.version_info < MIN_PYTHON_VERSION_SOFT:
100 print( 105 logger.error(
101 "repo: warning: your Python 3 version is no longer supported; " 106 "repo: warning: your Python 3 version is no longer supported; "
102 "Please upgrade to Python {}.{}+.".format(*MIN_PYTHON_VERSION_SOFT), 107 "Please upgrade to Python %d.%d+.",
103 file=sys.stderr, 108 *MIN_PYTHON_VERSION_SOFT,
104 ) 109 )
105 110
106KEYBOARD_INTERRUPT_EXIT = 128 + signal.SIGINT 111KEYBOARD_INTERRUPT_EXIT = 128 + signal.SIGINT
@@ -309,7 +314,7 @@ class _Repo(object):
309 ) 314 )
310 315
311 if Wrapper().gitc_parse_clientdir(os.getcwd()): 316 if Wrapper().gitc_parse_clientdir(os.getcwd()):
312 print("GITC is not supported.", file=sys.stderr) 317 logger.error("GITC is not supported.")
313 raise GitcUnsupportedError() 318 raise GitcUnsupportedError()
314 319
315 try: 320 try:
@@ -322,32 +327,24 @@ class _Repo(object):
322 git_event_log=git_trace2_event_log, 327 git_event_log=git_trace2_event_log,
323 ) 328 )
324 except KeyError: 329 except KeyError:
325 print( 330 logger.error(
326 "repo: '%s' is not a repo command. See 'repo help'." % name, 331 "repo: '%s' is not a repo command. See 'repo help'.", name
327 file=sys.stderr,
328 ) 332 )
329 return 1 333 return 1
330 334
331 Editor.globalConfig = cmd.client.globalConfig 335 Editor.globalConfig = cmd.client.globalConfig
332 336
333 if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: 337 if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror:
334 print( 338 logger.error("fatal: '%s' requires a working directory", name)
335 "fatal: '%s' requires a working directory" % name,
336 file=sys.stderr,
337 )
338 return 1 339 return 1
339 340
340 try: 341 try:
341 copts, cargs = cmd.OptionParser.parse_args(argv) 342 copts, cargs = cmd.OptionParser.parse_args(argv)
342 copts = cmd.ReadEnvironmentOptions(copts) 343 copts = cmd.ReadEnvironmentOptions(copts)
343 except NoManifestException as e: 344 except NoManifestException as e:
344 print( 345 logger.error("error: in `%s`: %s", " ".join([name] + argv), e)
345 "error: in `%s`: %s" % (" ".join([name] + argv), str(e)), 346 logger.error(
346 file=sys.stderr, 347 "error: manifest missing or unreadable -- please run init"
347 )
348 print(
349 "error: manifest missing or unreadable -- please run init",
350 file=sys.stderr,
351 ) 348 )
352 return 1 349 return 1
353 350
@@ -453,34 +450,28 @@ class _Repo(object):
453 ManifestInvalidRevisionError, 450 ManifestInvalidRevisionError,
454 NoManifestException, 451 NoManifestException,
455 ) as e: 452 ) as e:
456 print( 453 logger.error("error: in `%s`: %s", " ".join([name] + argv), e)
457 "error: in `%s`: %s" % (" ".join([name] + argv), str(e)),
458 file=sys.stderr,
459 )
460 if isinstance(e, NoManifestException): 454 if isinstance(e, NoManifestException):
461 print( 455 logger.error(
462 "error: manifest missing or unreadable -- please run init", 456 "error: manifest missing or unreadable -- please run init"
463 file=sys.stderr,
464 ) 457 )
465 result = e.exit_code 458 result = e.exit_code
466 except NoSuchProjectError as e: 459 except NoSuchProjectError as e:
467 if e.name: 460 if e.name:
468 print("error: project %s not found" % e.name, file=sys.stderr) 461 logger.error("error: project %s not found", e.name)
469 else: 462 else:
470 print("error: no project in current directory", file=sys.stderr) 463 logger.error("error: no project in current directory")
471 result = e.exit_code 464 result = e.exit_code
472 except InvalidProjectGroupsError as e: 465 except InvalidProjectGroupsError as e:
473 if e.name: 466 if e.name:
474 print( 467 logger.error(
475 "error: project group must be enabled for project %s" 468 "error: project group must be enabled for project %s",
476 % e.name, 469 e.name,
477 file=sys.stderr,
478 ) 470 )
479 else: 471 else:
480 print( 472 logger.error(
481 "error: project group must be enabled for the project in " 473 "error: project group must be enabled for the project in "
482 "the current directory", 474 "the current directory"
483 file=sys.stderr,
484 ) 475 )
485 result = e.exit_code 476 result = e.exit_code
486 except SystemExit as e: 477 except SystemExit as e:
@@ -547,7 +538,7 @@ def _CheckWrapperVersion(ver_str, repo_path):
547 repo_path = "~/bin/repo" 538 repo_path = "~/bin/repo"
548 539
549 if not ver_str: 540 if not ver_str:
550 print("no --wrapper-version argument", file=sys.stderr) 541 logger.error("no --wrapper-version argument")
551 sys.exit(1) 542 sys.exit(1)
552 543
553 # Pull out the version of the repo launcher we know about to compare. 544 # Pull out the version of the repo launcher we know about to compare.
@@ -556,7 +547,7 @@ def _CheckWrapperVersion(ver_str, repo_path):
556 547
557 exp_str = ".".join(map(str, exp)) 548 exp_str = ".".join(map(str, exp))
558 if ver < MIN_REPO_VERSION: 549 if ver < MIN_REPO_VERSION:
559 print( 550 logger.error(
560 """ 551 """
561repo: error: 552repo: error:
562!!! Your version of repo %s is too old. 553!!! Your version of repo %s is too old.
@@ -565,42 +556,42 @@ repo: error:
565!!! You must upgrade before you can continue: 556!!! You must upgrade before you can continue:
566 557
567 cp %s %s 558 cp %s %s
568""" 559""",
569 % (ver_str, min_str, exp_str, WrapperPath(), repo_path), 560 ver_str,
570 file=sys.stderr, 561 min_str,
562 exp_str,
563 WrapperPath(),
564 repo_path,
571 ) 565 )
572 sys.exit(1) 566 sys.exit(1)
573 567
574 if exp > ver: 568 if exp > ver:
575 print( 569 logger.warn("\n... A new version of repo (%s) is available.", exp_str)
576 "\n... A new version of repo (%s) is available." % (exp_str,),
577 file=sys.stderr,
578 )
579 if os.access(repo_path, os.W_OK): 570 if os.access(repo_path, os.W_OK):
580 print( 571 logger.warn(
581 """\ 572 """\
582... You should upgrade soon: 573... You should upgrade soon:
583 cp %s %s 574 cp %s %s
584""" 575""",
585 % (WrapperPath(), repo_path), 576 WrapperPath(),
586 file=sys.stderr, 577 repo_path,
587 ) 578 )
588 else: 579 else:
589 print( 580 logger.warn(
590 """\ 581 """\
591... New version is available at: %s 582... New version is available at: %s
592... The launcher is run from: %s 583... The launcher is run from: %s
593!!! The launcher is not writable. Please talk to your sysadmin or distro 584!!! The launcher is not writable. Please talk to your sysadmin or distro
594!!! to get an update installed. 585!!! to get an update installed.
595""" 586""",
596 % (WrapperPath(), repo_path), 587 WrapperPath(),
597 file=sys.stderr, 588 repo_path,
598 ) 589 )
599 590
600 591
601def _CheckRepoDir(repo_dir): 592def _CheckRepoDir(repo_dir):
602 if not repo_dir: 593 if not repo_dir:
603 print("no --repo-dir argument", file=sys.stderr) 594 logger.error("no --repo-dir argument")
604 sys.exit(1) 595 sys.exit(1)
605 596
606 597
@@ -861,18 +852,7 @@ def _Main(argv):
861 result = repo._Run(name, gopts, argv) or 0 852 result = repo._Run(name, gopts, argv) or 0
862 except RepoExitError as e: 853 except RepoExitError as e:
863 if not isinstance(e, SilentRepoExitError): 854 if not isinstance(e, SilentRepoExitError):
864 exception_name = type(e).__name__ 855 logger.log_aggregated_errors(e)
865 print("fatal: %s" % e, file=sys.stderr)
866 if e.aggregate_errors:
867 print(f"{exception_name} Aggregate Errors")
868 for err in e.aggregate_errors[:MAX_PRINT_ERRORS]:
869 print(err)
870 if (
871 e.aggregate_errors
872 and len(e.aggregate_errors) > MAX_PRINT_ERRORS
873 ):
874 diff = len(e.aggregate_errors) - MAX_PRINT_ERRORS
875 print(f"+{diff} additional errors ...")
876 result = e.exit_code 856 result = e.exit_code
877 except KeyboardInterrupt: 857 except KeyboardInterrupt:
878 print("aborted by user", file=sys.stderr) 858 print("aborted by user", file=sys.stderr)
diff --git a/repo_logging.py b/repo_logging.py
index 7d050555..58625351 100644
--- a/repo_logging.py
+++ b/repo_logging.py
@@ -15,12 +15,13 @@
15"""Logic for printing user-friendly logs in repo.""" 15"""Logic for printing user-friendly logs in repo."""
16 16
17import logging 17import logging
18from typing import List
19 18
20from color import Coloring 19from color import Coloring
20from error import RepoExitError
21 21
22 22
23SEPARATOR = "=" * 80 23SEPARATOR = "=" * 80
24MAX_PRINT_ERRORS = 5
24 25
25 26
26class _ConfigMock: 27class _ConfigMock:
@@ -70,8 +71,22 @@ class RepoLogger(logging.Logger):
70 handler.setFormatter(_LogColoringFormatter(config)) 71 handler.setFormatter(_LogColoringFormatter(config))
71 self.addHandler(handler) 72 self.addHandler(handler)
72 73
73 def log_aggregated_errors(self, errors: List[Exception]): 74 def log_aggregated_errors(self, err: RepoExitError):
74 """Print all aggregated logs.""" 75 """Print all aggregated logs."""
75 super().error(SEPARATOR) 76 self.error(SEPARATOR)
76 super().error("Repo command failed due to following errors:") 77
77 super().error("\n".join(str(e) for e in errors)) 78 if not err.aggregate_errors:
79 self.error("Repo command failed: %s", type(err).__name__)
80 return
81
82 self.error(
83 "Repo command failed due to the following `%s` errors:",
84 type(err).__name__,
85 )
86 self.error(
87 "\n".join(str(e) for e in err.aggregate_errors[:MAX_PRINT_ERRORS])
88 )
89
90 diff = len(err.aggregate_errors) - MAX_PRINT_ERRORS
91 if diff:
92 self.error("+%d additional errors...", diff)
diff --git a/tests/test_repo_logging.py b/tests/test_repo_logging.py
index 52f251a7..0f6a3355 100644
--- a/tests/test_repo_logging.py
+++ b/tests/test_repo_logging.py
@@ -16,47 +16,49 @@
16import unittest 16import unittest
17from unittest import mock 17from unittest import mock
18 18
19from error import RepoExitError
19from repo_logging import RepoLogger 20from repo_logging import RepoLogger
20 21
21 22
22class TestRepoLogger(unittest.TestCase): 23class TestRepoLogger(unittest.TestCase):
23 def test_log_aggregated_errors_logs_aggregated_errors(self): 24 @mock.patch.object(RepoLogger, "error")
24 """Test if log_aggregated_errors outputs aggregated errors.""" 25 def test_log_aggregated_errors_logs_aggregated_errors(self, mock_error):
26 """Test if log_aggregated_errors logs a list of aggregated errors."""
25 logger = RepoLogger(__name__) 27 logger = RepoLogger(__name__)
26 result = []
27
28 def mock_handler(log):
29 nonlocal result
30 result.append(log.getMessage())
31
32 mock_out = mock.MagicMock()
33 mock_out.level = 0
34 mock_out.handle = mock_handler
35 logger.addHandler(mock_out)
36
37 logger.error("Never gonna give you up")
38 logger.error("Never gonna let you down")
39 logger.error("Never gonna run around and desert you")
40 logger.log_aggregated_errors( 28 logger.log_aggregated_errors(
29 RepoExitError(
30 aggregate_errors=[
31 Exception("foo"),
32 Exception("bar"),
33 Exception("baz"),
34 Exception("hello"),
35 Exception("world"),
36 Exception("test"),
37 ]
38 )
39 )
40
41 mock_error.assert_has_calls(
41 [ 42 [
42 "Never gonna give you up", 43 mock.call("=" * 80),
43 "Never gonna let you down", 44 mock.call(
44 "Never gonna run around and desert you", 45 "Repo command failed due to the following `%s` errors:",
46 "RepoExitError",
47 ),
48 mock.call("foo\nbar\nbaz\nhello\nworld"),
49 mock.call("+%d additional errors...", 1),
45 ] 50 ]
46 ) 51 )
47 52
48 self.assertEqual( 53 @mock.patch.object(RepoLogger, "error")
49 result, 54 def test_log_aggregated_errors_logs_single_error(self, mock_error):
55 """Test if log_aggregated_errors logs empty aggregated_errors."""
56 logger = RepoLogger(__name__)
57 logger.log_aggregated_errors(RepoExitError())
58
59 mock_error.assert_has_calls(
50 [ 60 [
51 "Never gonna give you up", 61 mock.call("=" * 80),
52 "Never gonna let you down", 62 mock.call("Repo command failed: %s", "RepoExitError"),
53 "Never gonna run around and desert you", 63 ]
54 "=" * 80,
55 "Repo command failed due to following errors:",
56 (
57 "Never gonna give you up\n"
58 "Never gonna let you down\n"
59 "Never gonna run around and desert you"
60 ),
61 ],
62 ) 64 )