mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-01-02 16:14:25 +00:00
Raise repo exit errors in place of sys.exit
Bug: b/293344017 Change-Id: I92d81c78eba8ff31b5252415f4c9a515a6c76411 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/381774 Tested-by: Jason Chang <jasonnc@google.com> Reviewed-by: Joanna Wang <jojwang@google.com> Commit-Queue: Jason Chang <jasonnc@google.com>
This commit is contained in:
parent
b8a7b4a629
commit
f9aacd4087
@ -16,11 +16,11 @@ import multiprocessing
|
|||||||
import os
|
import os
|
||||||
import optparse
|
import optparse
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
|
|
||||||
from event_log import EventLog
|
from event_log import EventLog
|
||||||
from error import NoSuchProjectError
|
from error import NoSuchProjectError
|
||||||
from error import InvalidProjectGroupsError
|
from error import InvalidProjectGroupsError
|
||||||
|
from error import RepoExitError
|
||||||
import progress
|
import progress
|
||||||
|
|
||||||
|
|
||||||
@ -42,6 +42,10 @@ WORKER_BATCH_SIZE = 32
|
|||||||
DEFAULT_LOCAL_JOBS = min(os.cpu_count(), 8)
|
DEFAULT_LOCAL_JOBS = min(os.cpu_count(), 8)
|
||||||
|
|
||||||
|
|
||||||
|
class UsageError(RepoExitError):
|
||||||
|
"""Exception thrown with invalid command usage."""
|
||||||
|
|
||||||
|
|
||||||
class Command(object):
|
class Command(object):
|
||||||
"""Base class for any command line action in repo."""
|
"""Base class for any command line action in repo."""
|
||||||
|
|
||||||
@ -215,7 +219,7 @@ class Command(object):
|
|||||||
def Usage(self):
|
def Usage(self):
|
||||||
"""Display usage and terminate."""
|
"""Display usage and terminate."""
|
||||||
self.OptionParser.print_usage()
|
self.OptionParser.print_usage()
|
||||||
sys.exit(1)
|
raise UsageError()
|
||||||
|
|
||||||
def CommonValidateOptions(self, opt, args):
|
def CommonValidateOptions(self, opt, args):
|
||||||
"""Validate common options."""
|
"""Validate common options."""
|
||||||
|
9
fetch.py
9
fetch.py
@ -18,6 +18,11 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
from error import RepoExitError
|
||||||
|
|
||||||
|
|
||||||
|
class FetchFileError(RepoExitError):
|
||||||
|
"""Exit error when fetch_file fails."""
|
||||||
|
|
||||||
|
|
||||||
def fetch_file(url, verbose=False):
|
def fetch_file(url, verbose=False):
|
||||||
@ -29,6 +34,7 @@ def fetch_file(url, verbose=False):
|
|||||||
scheme = urlparse(url).scheme
|
scheme = urlparse(url).scheme
|
||||||
if scheme == "gs":
|
if scheme == "gs":
|
||||||
cmd = ["gsutil", "cat", url]
|
cmd = ["gsutil", "cat", url]
|
||||||
|
errors = []
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
|
||||||
@ -41,9 +47,10 @@ def fetch_file(url, verbose=False):
|
|||||||
)
|
)
|
||||||
return result.stdout
|
return result.stdout
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
|
errors.append(e)
|
||||||
print(
|
print(
|
||||||
'fatal: error running "gsutil": %s' % e.stderr, file=sys.stderr
|
'fatal: error running "gsutil": %s' % e.stderr, file=sys.stderr
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise FetchFileError(aggregate_errors=errors)
|
||||||
with urlopen(url) as f:
|
with urlopen(url) as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
@ -19,6 +19,7 @@ import subprocess
|
|||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from error import GitError
|
from error import GitError
|
||||||
|
from error import RepoExitError
|
||||||
from git_refs import HEAD
|
from git_refs import HEAD
|
||||||
import platform_utils
|
import platform_utils
|
||||||
from repo_trace import REPO_TRACE, IsTrace, Trace
|
from repo_trace import REPO_TRACE, IsTrace, Trace
|
||||||
@ -44,6 +45,7 @@ DEFAULT_GIT_FAIL_MESSAGE = "git command failure"
|
|||||||
# Common line length limit
|
# Common line length limit
|
||||||
GIT_ERROR_STDOUT_LINES = 1
|
GIT_ERROR_STDOUT_LINES = 1
|
||||||
GIT_ERROR_STDERR_LINES = 1
|
GIT_ERROR_STDERR_LINES = 1
|
||||||
|
INVALID_GIT_EXIT_CODE = 126
|
||||||
|
|
||||||
|
|
||||||
class _GitCall(object):
|
class _GitCall(object):
|
||||||
@ -51,8 +53,9 @@ class _GitCall(object):
|
|||||||
def version_tuple(self):
|
def version_tuple(self):
|
||||||
ret = Wrapper().ParseGitVersion()
|
ret = Wrapper().ParseGitVersion()
|
||||||
if ret is None:
|
if ret is None:
|
||||||
print("fatal: unable to detect git version", file=sys.stderr)
|
msg = "fatal: unable to detect git version"
|
||||||
sys.exit(1)
|
print(msg, file=sys.stderr)
|
||||||
|
raise GitRequireError(msg)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
@ -167,10 +170,9 @@ def git_require(min_version, fail=False, msg=""):
|
|||||||
need = ".".join(map(str, min_version))
|
need = ".".join(map(str, min_version))
|
||||||
if msg:
|
if msg:
|
||||||
msg = " for " + msg
|
msg = " for " + msg
|
||||||
print(
|
error_msg = "fatal: git %s or later required%s" % (need, msg)
|
||||||
"fatal: git %s or later required%s" % (need, msg), file=sys.stderr
|
print(error_msg, file=sys.stderr)
|
||||||
)
|
raise GitRequireError(error_msg)
|
||||||
sys.exit(1)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -403,6 +405,13 @@ class GitCommand(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GitRequireError(RepoExitError):
|
||||||
|
"""Error raised when git version is unavailable or invalid."""
|
||||||
|
|
||||||
|
def __init__(self, message, exit_code: int = INVALID_GIT_EXIT_CODE):
|
||||||
|
super().__init__(message, exit_code=exit_code)
|
||||||
|
|
||||||
|
|
||||||
class GitCommandError(GitError):
|
class GitCommandError(GitError):
|
||||||
"""
|
"""
|
||||||
Error raised from a failed git command.
|
Error raised from a failed git command.
|
||||||
|
22
project.py
22
project.py
@ -2003,8 +2003,8 @@ class Project(object):
|
|||||||
name: The name of the branch to abandon.
|
name: The name of the branch to abandon.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if the abandon succeeded; False if it didn't; None if the
|
True if the abandon succeeded; Raises GitCommandError if it didn't;
|
||||||
branch didn't exist.
|
None if the branch didn't exist.
|
||||||
"""
|
"""
|
||||||
rev = R_HEADS + name
|
rev = R_HEADS + name
|
||||||
all_refs = self.bare_ref.all
|
all_refs = self.bare_ref.all
|
||||||
@ -2025,16 +2025,14 @@ class Project(object):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._Checkout(revid, quiet=True)
|
self._Checkout(revid, quiet=True)
|
||||||
|
GitCommand(
|
||||||
return (
|
self,
|
||||||
GitCommand(
|
["branch", "-D", name],
|
||||||
self,
|
capture_stdout=True,
|
||||||
["branch", "-D", name],
|
capture_stderr=True,
|
||||||
capture_stdout=True,
|
verify_command=True,
|
||||||
capture_stderr=True,
|
).Wait()
|
||||||
).Wait()
|
return True
|
||||||
== 0
|
|
||||||
)
|
|
||||||
|
|
||||||
def PruneHeads(self):
|
def PruneHeads(self):
|
||||||
"""Prune any topic branches already merged into upstream."""
|
"""Prune any topic branches already merged into upstream."""
|
||||||
|
@ -20,6 +20,11 @@ import sys
|
|||||||
from command import Command, DEFAULT_LOCAL_JOBS
|
from command import Command, DEFAULT_LOCAL_JOBS
|
||||||
from git_command import git
|
from git_command import git
|
||||||
from progress import Progress
|
from progress import Progress
|
||||||
|
from error import RepoError, RepoExitError
|
||||||
|
|
||||||
|
|
||||||
|
class AbandonError(RepoExitError):
|
||||||
|
"""Exit error when abandon command fails."""
|
||||||
|
|
||||||
|
|
||||||
class Abandon(Command):
|
class Abandon(Command):
|
||||||
@ -68,28 +73,37 @@ It is equivalent to "git branch -D <branchname>".
|
|||||||
branches = nb
|
branches = nb
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
|
errors = []
|
||||||
for name in branches:
|
for name in branches:
|
||||||
status = project.AbandonBranch(name)
|
status = None
|
||||||
|
try:
|
||||||
|
status = project.AbandonBranch(name)
|
||||||
|
except RepoError as e:
|
||||||
|
status = False
|
||||||
|
errors.append(e)
|
||||||
if status is not None:
|
if status is not None:
|
||||||
ret[name] = status
|
ret[name] = status
|
||||||
return (ret, project)
|
|
||||||
|
return (ret, project, errors)
|
||||||
|
|
||||||
def Execute(self, opt, args):
|
def Execute(self, opt, args):
|
||||||
nb = args[0].split()
|
nb = args[0].split()
|
||||||
err = defaultdict(list)
|
err = defaultdict(list)
|
||||||
success = defaultdict(list)
|
success = defaultdict(list)
|
||||||
|
aggregate_errors = []
|
||||||
all_projects = self.GetProjects(
|
all_projects = self.GetProjects(
|
||||||
args[1:], all_manifests=not opt.this_manifest_only
|
args[1:], all_manifests=not opt.this_manifest_only
|
||||||
)
|
)
|
||||||
_RelPath = lambda p: p.RelPath(local=opt.this_manifest_only)
|
_RelPath = lambda p: p.RelPath(local=opt.this_manifest_only)
|
||||||
|
|
||||||
def _ProcessResults(_pool, pm, states):
|
def _ProcessResults(_pool, pm, states):
|
||||||
for results, project in states:
|
for results, project, errors in states:
|
||||||
for branch, status in results.items():
|
for branch, status in results.items():
|
||||||
if status:
|
if status:
|
||||||
success[branch].append(project)
|
success[branch].append(project)
|
||||||
else:
|
else:
|
||||||
err[branch].append(project)
|
err[branch].append(project)
|
||||||
|
aggregate_errors.extend(errors)
|
||||||
pm.update(msg="")
|
pm.update(msg="")
|
||||||
|
|
||||||
self.ExecuteInParallel(
|
self.ExecuteInParallel(
|
||||||
@ -116,13 +130,13 @@ It is equivalent to "git branch -D <branchname>".
|
|||||||
" " * len(err_msg) + " | %s" % _RelPath(proj),
|
" " * len(err_msg) + " | %s" % _RelPath(proj),
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise AbandonError(aggregate_errors=aggregate_errors)
|
||||||
elif not success:
|
elif not success:
|
||||||
print(
|
print(
|
||||||
"error: no project has local branch(es) : %s" % nb,
|
"error: no project has local branch(es) : %s" % nb,
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
raise AbandonError(aggregate_errors=aggregate_errors)
|
||||||
else:
|
else:
|
||||||
# Everything below here is displaying status.
|
# Everything below here is displaying status.
|
||||||
if opt.quiet:
|
if opt.quiet:
|
||||||
|
@ -19,6 +19,8 @@ from color import Coloring
|
|||||||
from command import InteractiveCommand, MirrorSafeCommand
|
from command import InteractiveCommand, MirrorSafeCommand
|
||||||
from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD
|
from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD
|
||||||
from wrapper import Wrapper
|
from wrapper import Wrapper
|
||||||
|
from error import UpdateManifestError
|
||||||
|
from error import RepoUnhandledExceptionError
|
||||||
|
|
||||||
_REPO_ALLOW_SHALLOW = os.environ.get("REPO_ALLOW_SHALLOW")
|
_REPO_ALLOW_SHALLOW = os.environ.get("REPO_ALLOW_SHALLOW")
|
||||||
|
|
||||||
@ -156,7 +158,10 @@ to update the working directory files.
|
|||||||
git_event_log=self.git_event_log,
|
git_event_log=self.git_event_log,
|
||||||
manifest_name=opt.manifest_name,
|
manifest_name=opt.manifest_name,
|
||||||
):
|
):
|
||||||
sys.exit(1)
|
manifest_name = opt.manifest_name
|
||||||
|
raise UpdateManifestError(
|
||||||
|
f"Unable to sync manifest {manifest_name}"
|
||||||
|
)
|
||||||
|
|
||||||
def _Prompt(self, prompt, value):
|
def _Prompt(self, prompt, value):
|
||||||
print("%-10s [%s]: " % (prompt, value), end="", flush=True)
|
print("%-10s [%s]: " % (prompt, value), end="", flush=True)
|
||||||
@ -346,14 +351,15 @@ to update the working directory files.
|
|||||||
repo_verify=opt.repo_verify,
|
repo_verify=opt.repo_verify,
|
||||||
quiet=opt.quiet,
|
quiet=opt.quiet,
|
||||||
)
|
)
|
||||||
except wrapper.CloneFailure:
|
except wrapper.CloneFailure as e:
|
||||||
err_msg = "fatal: double check your --repo-rev setting."
|
err_msg = "fatal: double check your --repo-rev setting."
|
||||||
print(
|
print(
|
||||||
err_msg,
|
err_msg,
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
self.git_event_log.ErrorEvent(err_msg)
|
self.git_event_log.ErrorEvent(err_msg)
|
||||||
sys.exit(1)
|
raise RepoUnhandledExceptionError(e)
|
||||||
|
|
||||||
branch = rp.GetBranch("default")
|
branch = rp.GetBranch("default")
|
||||||
branch.merge = remote_ref
|
branch.merge = remote_ref
|
||||||
rp.work_git.reset("--hard", rev)
|
rp.work_git.reset("--hard", rev)
|
||||||
|
@ -21,7 +21,7 @@ from typing import List
|
|||||||
|
|
||||||
from command import DEFAULT_LOCAL_JOBS, InteractiveCommand
|
from command import DEFAULT_LOCAL_JOBS, InteractiveCommand
|
||||||
from editor import Editor
|
from editor import Editor
|
||||||
from error import UploadError
|
from error import UploadError, RepoExitError
|
||||||
from git_command import GitCommand
|
from git_command import GitCommand
|
||||||
from git_refs import R_HEADS
|
from git_refs import R_HEADS
|
||||||
from hooks import RepoHook
|
from hooks import RepoHook
|
||||||
@ -31,6 +31,10 @@ from project import ReviewableBranch
|
|||||||
_DEFAULT_UNUSUAL_COMMIT_THRESHOLD = 5
|
_DEFAULT_UNUSUAL_COMMIT_THRESHOLD = 5
|
||||||
|
|
||||||
|
|
||||||
|
class UploadExitError(RepoExitError):
|
||||||
|
"""Indicates that there is an upload command error requiring a sys exit."""
|
||||||
|
|
||||||
|
|
||||||
def _VerifyPendingCommits(branches: List[ReviewableBranch]) -> bool:
|
def _VerifyPendingCommits(branches: List[ReviewableBranch]) -> bool:
|
||||||
"""Perform basic safety checks on the given set of branches.
|
"""Perform basic safety checks on the given set of branches.
|
||||||
|
|
||||||
@ -86,7 +90,7 @@ def _VerifyPendingCommits(branches: List[ReviewableBranch]) -> bool:
|
|||||||
def _die(fmt, *args):
|
def _die(fmt, *args):
|
||||||
msg = fmt % args
|
msg = fmt % args
|
||||||
print("error: %s" % msg, file=sys.stderr)
|
print("error: %s" % msg, file=sys.stderr)
|
||||||
sys.exit(1)
|
raise UploadExitError(msg)
|
||||||
|
|
||||||
|
|
||||||
def _SplitEmails(values):
|
def _SplitEmails(values):
|
||||||
@ -697,7 +701,7 @@ Gerrit Code Review: https://www.gerritcodereview.com/
|
|||||||
)
|
)
|
||||||
|
|
||||||
if have_errors:
|
if have_errors:
|
||||||
sys.exit(1)
|
raise branch.error
|
||||||
|
|
||||||
def _GetMergeBranch(self, project, local_branch=None):
|
def _GetMergeBranch(self, project, local_branch=None):
|
||||||
if local_branch is None:
|
if local_branch is None:
|
||||||
|
@ -21,12 +21,16 @@ import unittest
|
|||||||
import error
|
import error
|
||||||
import project
|
import project
|
||||||
import git_command
|
import git_command
|
||||||
|
import fetch
|
||||||
|
import command
|
||||||
from subcmds import all_modules
|
from subcmds import all_modules
|
||||||
|
|
||||||
imports = all_modules + [
|
imports = all_modules + [
|
||||||
error,
|
error,
|
||||||
project,
|
project,
|
||||||
git_command,
|
git_command,
|
||||||
|
fetch,
|
||||||
|
command,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -204,12 +204,12 @@ class GitRequireTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_older_fatal(self):
|
def test_older_fatal(self):
|
||||||
"""Test fatal require calls with old versions."""
|
"""Test fatal require calls with old versions."""
|
||||||
with self.assertRaises(SystemExit) as e:
|
with self.assertRaises(git_command.GitRequireError) as e:
|
||||||
git_command.git_require((2,), fail=True)
|
git_command.git_require((2,), fail=True)
|
||||||
self.assertNotEqual(0, e.code)
|
self.assertNotEqual(0, e.code)
|
||||||
|
|
||||||
def test_older_fatal_msg(self):
|
def test_older_fatal_msg(self):
|
||||||
"""Test fatal require calls with old versions and message."""
|
"""Test fatal require calls with old versions and message."""
|
||||||
with self.assertRaises(SystemExit) as e:
|
with self.assertRaises(git_command.GitRequireError) as e:
|
||||||
git_command.git_require((2,), fail=True, msg="so sad")
|
git_command.git_require((2,), fail=True, msg="so sad")
|
||||||
self.assertNotEqual(0, e.code)
|
self.assertNotEqual(0, e.code)
|
||||||
|
Loading…
Reference in New Issue
Block a user