diff options
| author | Jason Chang <jasonnc@google.com> | 2023-08-08 14:12:53 -0700 |
|---|---|---|
| committer | LUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-08-10 23:46:31 +0000 |
| commit | 1a3612fe6d347e458a53d7a9e920a91ea502e6ba (patch) | |
| tree | 02b1a61f1d97e32201ea5fa309bf1f1b6050e929 /subcmds | |
| parent | f0aeb220def22edfac9838288ad251f86da782c1 (diff) | |
| download | git-repo-1a3612fe6d347e458a53d7a9e920a91ea502e6ba.tar.gz | |
Raise RepoExitError in place of sys.exit
Bug: b/293344017
Change-Id: Icae4932b00e4068cba502a5ab4a0274fd7854d9d
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/382214
Reviewed-by: Gavin Mak <gavinmak@google.com>
Tested-by: Jason Chang <jasonnc@google.com>
Reviewed-by: Aravind Vasudevan <aravindvasudev@google.com>
Commit-Queue: Jason Chang <jasonnc@google.com>
Diffstat (limited to 'subcmds')
| -rw-r--r-- | subcmds/checkout.py | 50 | ||||
| -rw-r--r-- | subcmds/cherry_pick.py | 76 | ||||
| -rw-r--r-- | subcmds/download.py | 23 | ||||
| -rw-r--r-- | subcmds/grep.py | 84 | ||||
| -rw-r--r-- | subcmds/help.py | 7 | ||||
| -rw-r--r-- | subcmds/selfupdate.py | 10 | ||||
| -rw-r--r-- | subcmds/start.py | 38 |
7 files changed, 201 insertions, 87 deletions
diff --git a/subcmds/checkout.py b/subcmds/checkout.py index 6448518f..033fd349 100644 --- a/subcmds/checkout.py +++ b/subcmds/checkout.py | |||
| @@ -15,8 +15,26 @@ | |||
| 15 | import functools | 15 | import functools |
| 16 | import sys | 16 | import sys |
| 17 | 17 | ||
| 18 | from typing import NamedTuple | ||
| 18 | from command import Command, DEFAULT_LOCAL_JOBS | 19 | from command import Command, DEFAULT_LOCAL_JOBS |
| 19 | from progress import Progress | 20 | from progress import Progress |
| 21 | from project import Project | ||
| 22 | from error import GitError, RepoExitError | ||
| 23 | |||
| 24 | |||
| 25 | class CheckoutBranchResult(NamedTuple): | ||
| 26 | # Whether the Project is on the branch (i.e. branch exists and no errors) | ||
| 27 | result: bool | ||
| 28 | project: Project | ||
| 29 | error: Exception | ||
| 30 | |||
| 31 | |||
| 32 | class CheckoutCommandError(RepoExitError): | ||
| 33 | """Exception thrown when checkout command fails.""" | ||
| 34 | |||
| 35 | |||
| 36 | class MissingBranchError(RepoExitError): | ||
| 37 | """Exception thrown when no project has specified branch.""" | ||
| 20 | 38 | ||
| 21 | 39 | ||
| 22 | class Checkout(Command): | 40 | class Checkout(Command): |
| @@ -41,23 +59,30 @@ The command is equivalent to: | |||
| 41 | 59 | ||
| 42 | def _ExecuteOne(self, nb, project): | 60 | def _ExecuteOne(self, nb, project): |
| 43 | """Checkout one project.""" | 61 | """Checkout one project.""" |
| 44 | return (project.CheckoutBranch(nb), project) | 62 | error = None |
| 63 | result = None | ||
| 64 | try: | ||
| 65 | result = project.CheckoutBranch(nb) | ||
| 66 | except GitError as e: | ||
| 67 | error = e | ||
| 68 | return CheckoutBranchResult(result, project, error) | ||
| 45 | 69 | ||
| 46 | def Execute(self, opt, args): | 70 | def Execute(self, opt, args): |
| 47 | nb = args[0] | 71 | nb = args[0] |
| 48 | err = [] | 72 | err = [] |
| 73 | err_projects = [] | ||
| 49 | success = [] | 74 | success = [] |
| 50 | all_projects = self.GetProjects( | 75 | all_projects = self.GetProjects( |
| 51 | args[1:], all_manifests=not opt.this_manifest_only | 76 | args[1:], all_manifests=not opt.this_manifest_only |
| 52 | ) | 77 | ) |
| 53 | 78 | ||
| 54 | def _ProcessResults(_pool, pm, results): | 79 | def _ProcessResults(_pool, pm, results): |
| 55 | for status, project in results: | 80 | for result in results: |
| 56 | if status is not None: | 81 | if result.error is not None: |
| 57 | if status: | 82 | err.append(result.error) |
| 58 | success.append(project) | 83 | err_projects.append(result.project) |
| 59 | else: | 84 | elif result.result: |
| 60 | err.append(project) | 85 | success.append(result.project) |
| 61 | pm.update(msg="") | 86 | pm.update(msg="") |
| 62 | 87 | ||
| 63 | self.ExecuteInParallel( | 88 | self.ExecuteInParallel( |
| @@ -70,13 +95,14 @@ The command is equivalent to: | |||
| 70 | ), | 95 | ), |
| 71 | ) | 96 | ) |
| 72 | 97 | ||
| 73 | if err: | 98 | if err_projects: |
| 74 | for p in err: | 99 | for p in err_projects: |
| 75 | print( | 100 | print( |
| 76 | "error: %s/: cannot checkout %s" % (p.relpath, nb), | 101 | "error: %s/: cannot checkout %s" % (p.relpath, nb), |
| 77 | file=sys.stderr, | 102 | file=sys.stderr, |
| 78 | ) | 103 | ) |
| 79 | sys.exit(1) | 104 | raise CheckoutCommandError(aggregate_errors=err) |
| 80 | elif not success: | 105 | elif not success: |
| 81 | print("error: no project has branch %s" % nb, file=sys.stderr) | 106 | msg = f"error: no project has branch {nb}" |
| 82 | sys.exit(1) | 107 | print(msg, file=sys.stderr) |
| 108 | raise MissingBranchError(msg) | ||
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py index 4cfb8c88..7a4dd09e 100644 --- a/subcmds/cherry_pick.py +++ b/subcmds/cherry_pick.py | |||
| @@ -16,6 +16,7 @@ import re | |||
| 16 | import sys | 16 | import sys |
| 17 | from command import Command | 17 | from command import Command |
| 18 | from git_command import GitCommand | 18 | from git_command import GitCommand |
| 19 | from error import GitError | ||
| 19 | 20 | ||
| 20 | CHANGE_ID_RE = re.compile(r"^\s*Change-Id: I([0-9a-f]{40})\s*$") | 21 | CHANGE_ID_RE = re.compile(r"^\s*Change-Id: I([0-9a-f]{40})\s*$") |
| 21 | 22 | ||
| @@ -44,18 +45,31 @@ change id will be added. | |||
| 44 | ["rev-parse", "--verify", reference], | 45 | ["rev-parse", "--verify", reference], |
| 45 | capture_stdout=True, | 46 | capture_stdout=True, |
| 46 | capture_stderr=True, | 47 | capture_stderr=True, |
| 48 | verify_command=True, | ||
| 47 | ) | 49 | ) |
| 48 | if p.Wait() != 0: | 50 | try: |
| 51 | p.Wait() | ||
| 52 | except GitError: | ||
| 49 | print(p.stderr, file=sys.stderr) | 53 | print(p.stderr, file=sys.stderr) |
| 50 | sys.exit(1) | 54 | raise |
| 55 | |||
| 51 | sha1 = p.stdout.strip() | 56 | sha1 = p.stdout.strip() |
| 52 | 57 | ||
| 53 | p = GitCommand(None, ["cat-file", "commit", sha1], capture_stdout=True) | 58 | p = GitCommand( |
| 54 | if p.Wait() != 0: | 59 | None, |
| 60 | ["cat-file", "commit", sha1], | ||
| 61 | capture_stdout=True, | ||
| 62 | verify_command=True, | ||
| 63 | ) | ||
| 64 | |||
| 65 | try: | ||
| 66 | p.Wait() | ||
| 67 | except GitError: | ||
| 55 | print( | 68 | print( |
| 56 | "error: Failed to retrieve old commit message", file=sys.stderr | 69 | "error: Failed to retrieve old commit message", file=sys.stderr |
| 57 | ) | 70 | ) |
| 58 | sys.exit(1) | 71 | raise |
| 72 | |||
| 59 | old_msg = self._StripHeader(p.stdout) | 73 | old_msg = self._StripHeader(p.stdout) |
| 60 | 74 | ||
| 61 | p = GitCommand( | 75 | p = GitCommand( |
| @@ -63,37 +77,47 @@ change id will be added. | |||
| 63 | ["cherry-pick", sha1], | 77 | ["cherry-pick", sha1], |
| 64 | capture_stdout=True, | 78 | capture_stdout=True, |
| 65 | capture_stderr=True, | 79 | capture_stderr=True, |
| 80 | verify_command=True, | ||
| 66 | ) | 81 | ) |
| 67 | status = p.Wait() | 82 | |
| 83 | try: | ||
| 84 | p.Wait() | ||
| 85 | except GitError as e: | ||
| 86 | print(str(e)) | ||
| 87 | print( | ||
| 88 | "NOTE: When committing (please see above) and editing the " | ||
| 89 | "commit message, please remove the old Change-Id-line and " | ||
| 90 | "add:" | ||
| 91 | ) | ||
| 92 | print(self._GetReference(sha1), file=sys.stderr) | ||
| 93 | print(file=sys.stderr) | ||
| 94 | raise | ||
| 68 | 95 | ||
| 69 | if p.stdout: | 96 | if p.stdout: |
| 70 | print(p.stdout.strip(), file=sys.stdout) | 97 | print(p.stdout.strip(), file=sys.stdout) |
| 71 | if p.stderr: | 98 | if p.stderr: |
| 72 | print(p.stderr.strip(), file=sys.stderr) | 99 | print(p.stderr.strip(), file=sys.stderr) |
| 73 | 100 | ||
| 74 | if status == 0: | 101 | # The cherry-pick was applied correctly. We just need to edit |
| 75 | # The cherry-pick was applied correctly. We just need to edit the | 102 | # the commit message. |
| 76 | # commit message. | 103 | new_msg = self._Reformat(old_msg, sha1) |
| 77 | new_msg = self._Reformat(old_msg, sha1) | ||
| 78 | |||
| 79 | p = GitCommand( | ||
| 80 | None, | ||
| 81 | ["commit", "--amend", "-F", "-"], | ||
| 82 | input=new_msg, | ||
| 83 | capture_stdout=True, | ||
| 84 | capture_stderr=True, | ||
| 85 | ) | ||
| 86 | if p.Wait() != 0: | ||
| 87 | print("error: Failed to update commit message", file=sys.stderr) | ||
| 88 | sys.exit(1) | ||
| 89 | 104 | ||
| 90 | else: | 105 | p = GitCommand( |
| 106 | None, | ||
| 107 | ["commit", "--amend", "-F", "-"], | ||
| 108 | input=new_msg, | ||
| 109 | capture_stdout=True, | ||
| 110 | capture_stderr=True, | ||
| 111 | verify_command=True, | ||
| 112 | ) | ||
| 113 | try: | ||
| 114 | p.Wait() | ||
| 115 | except GitError: | ||
| 91 | print( | 116 | print( |
| 92 | "NOTE: When committing (please see above) and editing the " | 117 | "error: Failed to update commit message", |
| 93 | "commit message, please remove the old Change-Id-line and add:" | 118 | file=sys.stderr, |
| 94 | ) | 119 | ) |
| 95 | print(self._GetReference(sha1), file=sys.stderr) | 120 | raise |
| 96 | print(file=sys.stderr) | ||
| 97 | 121 | ||
| 98 | def _IsChangeId(self, line): | 122 | def _IsChangeId(self, line): |
| 99 | return CHANGE_ID_RE.match(line) | 123 | return CHANGE_ID_RE.match(line) |
diff --git a/subcmds/download.py b/subcmds/download.py index 475c0bc2..18e555be 100644 --- a/subcmds/download.py +++ b/subcmds/download.py | |||
| @@ -16,11 +16,15 @@ import re | |||
| 16 | import sys | 16 | import sys |
| 17 | 17 | ||
| 18 | from command import Command | 18 | from command import Command |
| 19 | from error import GitError, NoSuchProjectError | 19 | from error import GitError, NoSuchProjectError, RepoExitError |
| 20 | 20 | ||
| 21 | CHANGE_RE = re.compile(r"^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$") | 21 | CHANGE_RE = re.compile(r"^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$") |
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | class DownloadCommandError(RepoExitError): | ||
| 25 | """Error raised when download command fails.""" | ||
| 26 | |||
| 27 | |||
| 24 | class Download(Command): | 28 | class Download(Command): |
| 25 | COMMON = True | 29 | COMMON = True |
| 26 | helpSummary = "Download and checkout a change" | 30 | helpSummary = "Download and checkout a change" |
| @@ -137,15 +141,16 @@ If no project is specified try to use current directory as a project. | |||
| 137 | ) | 141 | ) |
| 138 | 142 | ||
| 139 | def Execute(self, opt, args): | 143 | def Execute(self, opt, args): |
| 144 | try: | ||
| 145 | self._ExecuteHelper(opt, args) | ||
| 146 | except Exception as e: | ||
| 147 | if isinstance(e, RepoExitError): | ||
| 148 | raise e | ||
| 149 | raise DownloadCommandError(aggregate_errors=[e]) | ||
| 150 | |||
| 151 | def _ExecuteHelper(self, opt, args): | ||
| 140 | for project, change_id, ps_id in self._ParseChangeIds(opt, args): | 152 | for project, change_id, ps_id in self._ParseChangeIds(opt, args): |
| 141 | dl = project.DownloadPatchSet(change_id, ps_id) | 153 | dl = project.DownloadPatchSet(change_id, ps_id) |
| 142 | if not dl: | ||
| 143 | print( | ||
| 144 | "[%s] change %d/%d not found" | ||
| 145 | % (project.name, change_id, ps_id), | ||
| 146 | file=sys.stderr, | ||
| 147 | ) | ||
| 148 | sys.exit(1) | ||
| 149 | 154 | ||
| 150 | if not opt.revert and not dl.commits: | 155 | if not opt.revert and not dl.commits: |
| 151 | print( | 156 | print( |
| @@ -201,4 +206,4 @@ If no project is specified try to use current directory as a project. | |||
| 201 | % (project.name, mode, dl.commit), | 206 | % (project.name, mode, dl.commit), |
| 202 | file=sys.stderr, | 207 | file=sys.stderr, |
| 203 | ) | 208 | ) |
| 204 | sys.exit(1) | 209 | raise |
diff --git a/subcmds/grep.py b/subcmds/grep.py index 5cd33763..9ebd776c 100644 --- a/subcmds/grep.py +++ b/subcmds/grep.py | |||
| @@ -17,8 +17,10 @@ import sys | |||
| 17 | 17 | ||
| 18 | from color import Coloring | 18 | from color import Coloring |
| 19 | from command import DEFAULT_LOCAL_JOBS, PagedCommand | 19 | from command import DEFAULT_LOCAL_JOBS, PagedCommand |
| 20 | from error import GitError | 20 | from error import GitError, InvalidArgumentsError, SilentRepoExitError |
| 21 | from git_command import GitCommand | 21 | from git_command import GitCommand |
| 22 | from typing import NamedTuple | ||
| 23 | from project import Project | ||
| 22 | 24 | ||
| 23 | 25 | ||
| 24 | class GrepColoring(Coloring): | 26 | class GrepColoring(Coloring): |
| @@ -28,6 +30,22 @@ class GrepColoring(Coloring): | |||
| 28 | self.fail = self.printer("fail", fg="red") | 30 | self.fail = self.printer("fail", fg="red") |
| 29 | 31 | ||
| 30 | 32 | ||
| 33 | class ExecuteOneResult(NamedTuple): | ||
| 34 | """Result from an execute instance.""" | ||
| 35 | |||
| 36 | project: Project | ||
| 37 | rc: int | ||
| 38 | stdout: str | ||
| 39 | stderr: str | ||
| 40 | error: GitError | ||
| 41 | |||
| 42 | |||
| 43 | class GrepCommandError(SilentRepoExitError): | ||
| 44 | """Grep command failure. Since Grep command | ||
| 45 | output already outputs errors ensure that | ||
| 46 | aggregate errors exit silently.""" | ||
| 47 | |||
| 48 | |||
| 31 | class Grep(PagedCommand): | 49 | class Grep(PagedCommand): |
| 32 | COMMON = True | 50 | COMMON = True |
| 33 | helpSummary = "Print lines matching a pattern" | 51 | helpSummary = "Print lines matching a pattern" |
| @@ -246,11 +264,18 @@ contain a line that matches both expressions: | |||
| 246 | bare=False, | 264 | bare=False, |
| 247 | capture_stdout=True, | 265 | capture_stdout=True, |
| 248 | capture_stderr=True, | 266 | capture_stderr=True, |
| 267 | verify_command=True, | ||
| 249 | ) | 268 | ) |
| 250 | except GitError as e: | 269 | except GitError as e: |
| 251 | return (project, -1, None, str(e)) | 270 | return ExecuteOneResult(project, -1, None, str(e), e) |
| 252 | 271 | ||
| 253 | return (project, p.Wait(), p.stdout, p.stderr) | 272 | try: |
| 273 | error = None | ||
| 274 | rc = p.Wait() | ||
| 275 | except GitError as e: | ||
| 276 | rc = 1 | ||
| 277 | error = e | ||
| 278 | return ExecuteOneResult(project, rc, p.stdout, p.stderr, error) | ||
| 254 | 279 | ||
| 255 | @staticmethod | 280 | @staticmethod |
| 256 | def _ProcessResults(full_name, have_rev, opt, _pool, out, results): | 281 | def _ProcessResults(full_name, have_rev, opt, _pool, out, results): |
| @@ -258,31 +283,40 @@ contain a line that matches both expressions: | |||
| 258 | bad_rev = False | 283 | bad_rev = False |
| 259 | have_match = False | 284 | have_match = False |
| 260 | _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) | 285 | _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) |
| 286 | errors = [] | ||
| 261 | 287 | ||
| 262 | for project, rc, stdout, stderr in results: | 288 | for result in results: |
| 263 | if rc < 0: | 289 | if result.rc < 0: |
| 264 | git_failed = True | 290 | git_failed = True |
| 265 | out.project("--- project %s ---" % _RelPath(project)) | 291 | out.project("--- project %s ---" % _RelPath(result.project)) |
| 266 | out.nl() | 292 | out.nl() |
| 267 | out.fail("%s", stderr) | 293 | out.fail("%s", result.stderr) |
| 268 | out.nl() | 294 | out.nl() |
| 295 | errors.append(result.error) | ||
| 269 | continue | 296 | continue |
| 270 | 297 | ||
| 271 | if rc: | 298 | if result.rc: |
| 272 | # no results | 299 | # no results |
| 273 | if stderr: | 300 | if result.stderr: |
| 274 | if have_rev and "fatal: ambiguous argument" in stderr: | 301 | if ( |
| 302 | have_rev | ||
| 303 | and "fatal: ambiguous argument" in result.stderr | ||
| 304 | ): | ||
| 275 | bad_rev = True | 305 | bad_rev = True |
| 276 | else: | 306 | else: |
| 277 | out.project("--- project %s ---" % _RelPath(project)) | 307 | out.project( |
| 308 | "--- project %s ---" % _RelPath(result.project) | ||
| 309 | ) | ||
| 278 | out.nl() | 310 | out.nl() |
| 279 | out.fail("%s", stderr.strip()) | 311 | out.fail("%s", result.stderr.strip()) |
| 280 | out.nl() | 312 | out.nl() |
| 313 | if result.error is not None: | ||
| 314 | errors.append(result.error) | ||
| 281 | continue | 315 | continue |
| 282 | have_match = True | 316 | have_match = True |
| 283 | 317 | ||
| 284 | # We cut the last element, to avoid a blank line. | 318 | # We cut the last element, to avoid a blank line. |
| 285 | r = stdout.split("\n") | 319 | r = result.stdout.split("\n") |
| 286 | r = r[0:-1] | 320 | r = r[0:-1] |
| 287 | 321 | ||
| 288 | if have_rev and full_name: | 322 | if have_rev and full_name: |
| @@ -290,13 +324,13 @@ contain a line that matches both expressions: | |||
| 290 | rev, line = line.split(":", 1) | 324 | rev, line = line.split(":", 1) |
| 291 | out.write("%s", rev) | 325 | out.write("%s", rev) |
| 292 | out.write(":") | 326 | out.write(":") |
| 293 | out.project(_RelPath(project)) | 327 | out.project(_RelPath(result.project)) |
| 294 | out.write("/") | 328 | out.write("/") |
| 295 | out.write("%s", line) | 329 | out.write("%s", line) |
| 296 | out.nl() | 330 | out.nl() |
| 297 | elif full_name: | 331 | elif full_name: |
| 298 | for line in r: | 332 | for line in r: |
| 299 | out.project(_RelPath(project)) | 333 | out.project(_RelPath(result.project)) |
| 300 | out.write("/") | 334 | out.write("/") |
| 301 | out.write("%s", line) | 335 | out.write("%s", line) |
| 302 | out.nl() | 336 | out.nl() |
| @@ -304,7 +338,7 @@ contain a line that matches both expressions: | |||
| 304 | for line in r: | 338 | for line in r: |
| 305 | print(line) | 339 | print(line) |
| 306 | 340 | ||
| 307 | return (git_failed, bad_rev, have_match) | 341 | return (git_failed, bad_rev, have_match, errors) |
| 308 | 342 | ||
| 309 | def Execute(self, opt, args): | 343 | def Execute(self, opt, args): |
| 310 | out = GrepColoring(self.manifest.manifestProject.config) | 344 | out = GrepColoring(self.manifest.manifestProject.config) |
| @@ -333,16 +367,14 @@ contain a line that matches both expressions: | |||
| 333 | have_rev = False | 367 | have_rev = False |
| 334 | if opt.revision: | 368 | if opt.revision: |
| 335 | if "--cached" in cmd_argv: | 369 | if "--cached" in cmd_argv: |
| 336 | print( | 370 | msg = "fatal: cannot combine --cached and --revision" |
| 337 | "fatal: cannot combine --cached and --revision", | 371 | print(msg, file=sys.stderr) |
| 338 | file=sys.stderr, | 372 | raise InvalidArgumentsError(msg) |
| 339 | ) | ||
| 340 | sys.exit(1) | ||
| 341 | have_rev = True | 373 | have_rev = True |
| 342 | cmd_argv.extend(opt.revision) | 374 | cmd_argv.extend(opt.revision) |
| 343 | cmd_argv.append("--") | 375 | cmd_argv.append("--") |
| 344 | 376 | ||
| 345 | git_failed, bad_rev, have_match = self.ExecuteInParallel( | 377 | git_failed, bad_rev, have_match, errors = self.ExecuteInParallel( |
| 346 | opt.jobs, | 378 | opt.jobs, |
| 347 | functools.partial(self._ExecuteOne, cmd_argv), | 379 | functools.partial(self._ExecuteOne, cmd_argv), |
| 348 | projects, | 380 | projects, |
| @@ -354,12 +386,12 @@ contain a line that matches both expressions: | |||
| 354 | ) | 386 | ) |
| 355 | 387 | ||
| 356 | if git_failed: | 388 | if git_failed: |
| 357 | sys.exit(1) | 389 | raise GrepCommandError( |
| 390 | "error: git failures", aggregate_errors=errors | ||
| 391 | ) | ||
| 358 | elif have_match: | 392 | elif have_match: |
| 359 | sys.exit(0) | 393 | sys.exit(0) |
| 360 | elif have_rev and bad_rev: | 394 | elif have_rev and bad_rev: |
| 361 | for r in opt.revision: | 395 | for r in opt.revision: |
| 362 | print("error: can't search revision %s" % r, file=sys.stderr) | 396 | print("error: can't search revision %s" % r, file=sys.stderr) |
| 363 | sys.exit(1) | 397 | raise GrepCommandError(aggregate_errors=errors) |
| 364 | else: | ||
| 365 | sys.exit(1) | ||
diff --git a/subcmds/help.py b/subcmds/help.py index 50a48047..593bf676 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
| @@ -26,6 +26,11 @@ from command import ( | |||
| 26 | ) | 26 | ) |
| 27 | import gitc_utils | 27 | import gitc_utils |
| 28 | from wrapper import Wrapper | 28 | from wrapper import Wrapper |
| 29 | from error import RepoExitError | ||
| 30 | |||
| 31 | |||
| 32 | class InvalidHelpCommand(RepoExitError): | ||
| 33 | """Invalid command passed into help.""" | ||
| 29 | 34 | ||
| 30 | 35 | ||
| 31 | class Help(PagedCommand, MirrorSafeCommand): | 36 | class Help(PagedCommand, MirrorSafeCommand): |
| @@ -202,7 +207,7 @@ Displays detailed usage information about a command. | |||
| 202 | print( | 207 | print( |
| 203 | "repo: '%s' is not a repo command." % name, file=sys.stderr | 208 | "repo: '%s' is not a repo command." % name, file=sys.stderr |
| 204 | ) | 209 | ) |
| 205 | sys.exit(1) | 210 | raise InvalidHelpCommand(name) |
| 206 | 211 | ||
| 207 | self._PrintCommandHelp(cmd) | 212 | self._PrintCommandHelp(cmd) |
| 208 | 213 | ||
diff --git a/subcmds/selfupdate.py b/subcmds/selfupdate.py index d5d0a838..00376b66 100644 --- a/subcmds/selfupdate.py +++ b/subcmds/selfupdate.py | |||
| @@ -18,6 +18,11 @@ import sys | |||
| 18 | from command import Command, MirrorSafeCommand | 18 | from command import Command, MirrorSafeCommand |
| 19 | from subcmds.sync import _PostRepoUpgrade | 19 | from subcmds.sync import _PostRepoUpgrade |
| 20 | from subcmds.sync import _PostRepoFetch | 20 | from subcmds.sync import _PostRepoFetch |
| 21 | from error import RepoExitError | ||
| 22 | |||
| 23 | |||
| 24 | class SelfupdateError(RepoExitError): | ||
| 25 | """Exit error for failed selfupdate command.""" | ||
| 21 | 26 | ||
| 22 | 27 | ||
| 23 | class Selfupdate(Command, MirrorSafeCommand): | 28 | class Selfupdate(Command, MirrorSafeCommand): |
| @@ -58,9 +63,10 @@ need to be performed by an end-user. | |||
| 58 | _PostRepoUpgrade(self.manifest) | 63 | _PostRepoUpgrade(self.manifest) |
| 59 | 64 | ||
| 60 | else: | 65 | else: |
| 61 | if not rp.Sync_NetworkHalf().success: | 66 | result = rp.Sync_NetworkHalf() |
| 67 | if result.error: | ||
| 62 | print("error: can't update repo", file=sys.stderr) | 68 | print("error: can't update repo", file=sys.stderr) |
| 63 | sys.exit(1) | 69 | raise SelfupdateError(aggregate_errors=[result.error]) |
| 64 | 70 | ||
| 65 | rp.bare_git.gc("--auto") | 71 | rp.bare_git.gc("--auto") |
| 66 | _PostRepoFetch(rp, repo_verify=opt.repo_verify, verbose=True) | 72 | _PostRepoFetch(rp, repo_verify=opt.repo_verify, verbose=True) |
diff --git a/subcmds/start.py b/subcmds/start.py index f6355126..67ac7df9 100644 --- a/subcmds/start.py +++ b/subcmds/start.py | |||
| @@ -21,7 +21,18 @@ from git_config import IsImmutable | |||
| 21 | from git_command import git | 21 | from git_command import git |
| 22 | import gitc_utils | 22 | import gitc_utils |
| 23 | from progress import Progress | 23 | from progress import Progress |
| 24 | from project import SyncBuffer | 24 | from project import SyncBuffer, Project |
| 25 | from typing import NamedTuple | ||
| 26 | from error import RepoExitError | ||
| 27 | |||
| 28 | |||
| 29 | class ExecuteOneResult(NamedTuple): | ||
| 30 | project: Project | ||
| 31 | error: Exception | ||
| 32 | |||
| 33 | |||
| 34 | class StartError(RepoExitError): | ||
| 35 | """Exit error for failed start command.""" | ||
| 25 | 36 | ||
| 26 | 37 | ||
| 27 | class Start(Command): | 38 | class Start(Command): |
| @@ -73,6 +84,7 @@ revision specified in the manifest. | |||
| 73 | # a change, then we can't push back to it. Substitute with | 84 | # a change, then we can't push back to it. Substitute with |
| 74 | # dest_branch, if defined; or with manifest default revision instead. | 85 | # dest_branch, if defined; or with manifest default revision instead. |
| 75 | branch_merge = "" | 86 | branch_merge = "" |
| 87 | error = None | ||
| 76 | if IsImmutable(project.revisionExpr): | 88 | if IsImmutable(project.revisionExpr): |
| 77 | if project.dest_branch: | 89 | if project.dest_branch: |
| 78 | branch_merge = project.dest_branch | 90 | branch_merge = project.dest_branch |
| @@ -80,7 +92,7 @@ revision specified in the manifest. | |||
| 80 | branch_merge = self.manifest.default.revisionExpr | 92 | branch_merge = self.manifest.default.revisionExpr |
| 81 | 93 | ||
| 82 | try: | 94 | try: |
| 83 | ret = project.StartBranch( | 95 | project.StartBranch( |
| 84 | nb, branch_merge=branch_merge, revision=revision | 96 | nb, branch_merge=branch_merge, revision=revision |
| 85 | ) | 97 | ) |
| 86 | except Exception as e: | 98 | except Exception as e: |
| @@ -88,11 +100,12 @@ revision specified in the manifest. | |||
| 88 | "error: unable to checkout %s: %s" % (project.name, e), | 100 | "error: unable to checkout %s: %s" % (project.name, e), |
| 89 | file=sys.stderr, | 101 | file=sys.stderr, |
| 90 | ) | 102 | ) |
| 91 | ret = False | 103 | error = e |
| 92 | return (ret, project) | 104 | return ExecuteOneResult(project, error) |
| 93 | 105 | ||
| 94 | def Execute(self, opt, args): | 106 | def Execute(self, opt, args): |
| 95 | nb = args[0] | 107 | nb = args[0] |
| 108 | err_projects = [] | ||
| 96 | err = [] | 109 | err = [] |
| 97 | projects = [] | 110 | projects = [] |
| 98 | if not opt.all: | 111 | if not opt.all: |
| @@ -146,9 +159,10 @@ revision specified in the manifest. | |||
| 146 | pm.end() | 159 | pm.end() |
| 147 | 160 | ||
| 148 | def _ProcessResults(_pool, pm, results): | 161 | def _ProcessResults(_pool, pm, results): |
| 149 | for result, project in results: | 162 | for result in results: |
| 150 | if not result: | 163 | if result.error: |
| 151 | err.append(project) | 164 | err_projects.append(result.project) |
| 165 | err.append(result.error) | ||
| 152 | pm.update(msg="") | 166 | pm.update(msg="") |
| 153 | 167 | ||
| 154 | self.ExecuteInParallel( | 168 | self.ExecuteInParallel( |
| @@ -161,13 +175,15 @@ revision specified in the manifest. | |||
| 161 | ), | 175 | ), |
| 162 | ) | 176 | ) |
| 163 | 177 | ||
| 164 | if err: | 178 | if err_projects: |
| 165 | for p in err: | 179 | for p in err_projects: |
| 166 | print( | 180 | print( |
| 167 | "error: %s/: cannot start %s" | 181 | "error: %s/: cannot start %s" |
| 168 | % (p.RelPath(local=opt.this_manifest_only), nb), | 182 | % (p.RelPath(local=opt.this_manifest_only), nb), |
| 169 | file=sys.stderr, | 183 | file=sys.stderr, |
| 170 | ) | 184 | ) |
| 171 | msg_fmt = "cannot start %d project(s)" | 185 | msg_fmt = "cannot start %d project(s)" |
| 172 | self.git_event_log.ErrorEvent(msg_fmt % (len(err)), msg_fmt) | 186 | self.git_event_log.ErrorEvent( |
| 173 | sys.exit(1) | 187 | msg_fmt % (len(err_projects)), msg_fmt |
| 188 | ) | ||
| 189 | raise StartError(aggregate_errors=err) | ||
