_path = os.path.realpath(__file__)
_path = os.path.normpath(os.path.join(os.path.dirname(_path), '..', '..'))
sys.path.insert(0, _path)
-from chromite.buildbot import constants
+from chromite.cbuildbot import constants
from chromite.lib import cros_build_lib
from chromite.lib import osutils
from chromite.lib import retry_util
# messages. It's all observed 'bad' GoB responses so far.
GIT_TRANSIENT_ERRORS = (
# crbug.com/285832
- r'! \[remote rejected\].* -> .* \(error in hook\)',
+ r'! \[remote rejected\].*\(error in hook\)',
# crbug.com/289932
- r'! \[remote rejected\].* -> .* \(failed to lock\)',
+ r'! \[remote rejected\].*\(failed to lock\)',
# crbug.com/307156
- r'! \[remote rejected\].* -> .* \(error in Gerrit backend\)',
+ r'! \[remote rejected\].*\(error in Gerrit backend\)',
# crbug.com/285832
r'remote error: Internal Server Error',
# crbug.com/315421
r'The requested URL returned error: 500 while accessing',
+
+ # crbug.com/388876
+ r'Connection timed out',
)
-GIT_TRANSIENT_ERRORS_RE = re.compile('|'.join(GIT_TRANSIENT_ERRORS))
+GIT_TRANSIENT_ERRORS_RE = re.compile('|'.join(GIT_TRANSIENT_ERRORS),
+ re.IGNORECASE)
DEFAULT_RETRY_INTERVAL = 3
DEFAULT_RETRIES = 5
'https://chromium.googlesource.com/chromiumos/chromite.git', etc.) and a ref
name (e.g., 'refs/heads/master').
"""
+
def __init__(self, remote, ref):
self.remote = remote
self.ref = ref
_HEX_CHARS = frozenset(string.hexdigits)
+
+
def IsSHA1(value, full=True):
"""Returns True if the given value looks like a sha1.
Currently this is identified via refs/tags/ prefixing.
"""
- return value.startswith("refs/tags/")
+ return value.startswith('refs/tags/')
def GetGitRepoRevision(cwd, branch='HEAD'):
return RunGit(cwd, ['rev-parse', branch]).output.strip()
-def DoesCommitExistInRepo(cwd, commit_hash):
- """Determine if commit object exists in a repo.
+def DoesCommitExistInRepo(cwd, commit):
+ """Determine whether a commit (SHA1 or ref) exists in a repo.
Args:
cwd: A directory within the project repo.
- commit_hash: The hash of the commit object to look for.
- """
- return 0 == RunGit(cwd, ['rev-list', '-n1', commit_hash],
- error_code_ok=True).returncode
-
+ commit: The commit to look for. This can be a SHA1 or it can be a ref.
-def DoesLocalBranchExist(repo_dir, branch):
- """Returns True if the local branch exists.
-
- Args:
- repo_dir: Directory of the git repository to check.
- branch: The name of the branch to test for.
+ Returns:
+ True if the commit exists in the repo.
"""
- return os.path.isfile(
- os.path.join(repo_dir, '.git/refs/heads',
- branch.lstrip('/')))
+ try:
+ RunGit(cwd, ['rev-list', '-n1', commit, '--'])
+ except cros_build_lib.RunCommandError as e:
+ if e.result.returncode == 128:
+ return False
+ raise
+ return True
def GetCurrentBranch(cwd):
def StripRefs(ref):
"""Remove leading 'refs/heads', 'refs/remotes/[^/]+/' from a ref name."""
ref = StripRefsHeads(ref, False)
- if ref.startswith("refs/remotes/"):
- return ref.split("/", 3)[-1]
+ if ref.startswith('refs/remotes/'):
+ return ref.split('/', 3)[-1]
return ref
def IsBranchableProject(self):
"""Return whether this project is hosted on ChromeOS git servers."""
- return (self['remote'] in constants.CROS_REMOTES and
+ return (
+ self['remote'] in constants.CROS_REMOTES and
re.match(constants.BRANCHABLE_PROJECTS[self['remote']], self['name']))
def IsPatchable(self):
# - For an unversioned manifest, if the 'revision' is a raw SHA1 hash
# and not a named branch, assume it is a pinned project path and can not
# be patched.
- # - For a versioned manifest (generated via repo -r), repo will sets
- # revision to the actual git sha1 ref, and adds an 'upstream'
+ # - For a versioned manifest (generated via repo -r), repo will set
+ # revision to the actual git sha1 ref, and add an 'upstream'
# field corresponding to branch name in the original manifest. For
# a project with a SHA1 'revision' but no named branch in the
# 'upstream' field, assume it can not be patched.
attrs.setdefault('alias', attrs['name'])
self.remotes[attrs['name']] = attrs
elif name == 'project':
- self.checkouts_by_path[attrs['path']] = attrs
+ self.checkouts_by_path[attrs.get('path', attrs['name'])] = attrs
self.checkouts_by_name.setdefault(attrs['name'], []).append(attrs)
elif name == 'manifest':
self.revision = attrs.get('revision')
elif name == 'include':
if self.manifest_include_dir is None:
raise OSError(
- errno.ENOENT, "No manifest_include_dir given, but an include was "
- "encountered; attrs=%r" % (attrs,))
+ errno.ENOENT, 'No manifest_include_dir given, but an include was '
+ 'encountered; attrs=%r' % (attrs,))
# Include is calculated relative to the manifest that has the include;
# thus set the path temporarily to the dirname of the target.
original_include_dir = self.manifest_include_dir
manifest_include_dir = os.path.dirname(self.manifest_path)
self.manifest_branch = self._GetManifestsBranch(self.root)
self._content_merging = {}
- self.configured_groups = self._GetManifestGroups(self.root)
Manifest.__init__(self, self.manifest_path,
manifest_include_dir=manifest_include_dir)
root = os.path.normpath(os.path.realpath(root))
if not search:
if os.path.normpath(os.path.realpath(path)) != root:
- raise OSError(errno.ENOENT, "Path %s is not a repo root, and search "
- "is disabled." % path)
+ raise OSError(errno.ENOENT, 'Path %s is not a repo root, and search '
+ 'is disabled.' % path)
if manifest_path is None:
manifest_path = os.path.join(root, '.repo', 'manifest.xml')
return root, manifest_path
- def ProjectIsContentMerging(self, project):
- """Returns whether the given project has content merging enabled in git.
-
- Note this functionality should *only* be used against a remote that is
- known to be >=gerrit-2.2; <gerrit-2.2 lacks the required branch holding
- this data thus will trigger a RunCommandError.
-
- Returns:
- True if content merging is enabled.
-
- Raises:
- AssertionError: If no patchable checkout was found or if the patchable
- checkout does not have a pushable project remote.
- RunCommandError: If the branch can't be fetched due to network
- conditions or if this was invoked against a <gerrit-2.2 server,
- or a mirror that has refs/meta/config stripped from it.
- """
- result = self._content_merging.get(project)
- if result is None:
- checkouts = self.FindCheckouts(project, only_patchable=True)
- if len(checkouts) < 1:
- raise AssertionError('No patchable checkout of %s was found' % project)
- for checkout in checkouts:
- checkout.AssertPushable()
- self._content_merging[project] = result = _GitRepoIsContentMerging(
- checkout['local_path'], checkout['push_remote'])
- return result
-
def FindCheckouts(self, project, branch=None, only_patchable=False):
"""Returns the list of checkouts for a given |project|/|branch|.
"""Find the associated checkouts for a given |path|.
The |path| can either be to the root of a project, or within the
- project itself (chromite/buildbot for example). It may be relative
+ project itself (chromite.cbuildbot for example). It may be relative
to the repo root, or an absolute path. If |path| is not within a
checkout, return None.
attrs['local_path'] = os.path.join(self.root, attrs['path'])
@staticmethod
- def _GetManifestGroups(root):
- """Discern which manifest groups were enabled for this checkout."""
- path = os.path.join(root, '.repo', 'manifests.git')
- try:
- result = RunGit(path, ['config', '--get', 'manifest.groups'])
- except cros_build_lib.RunCommandError as e:
- if e.result.returncode == 1:
- # Value wasn't found, which is fine.
- return frozenset(['default'])
- # If exit code 2, multiple values matched (broken checkout). Anything
- # else, git internal error.
- raise
-
- result = result.output.replace(',', ' ').split()
- if not result:
- result = ['default']
- return frozenset(result)
-
- @staticmethod
def _GetManifestsBranch(root):
"""Get the tracking branch of the manifest repository.
current_branch = GetCurrentBranch(path)
if current_branch != 'default':
raise OSError(errno.ENOENT,
- "Manifest repository at %s is checked out to %s. "
+ 'Manifest repository at %s is checked out to %s. '
"It should be checked out to 'default'."
% (root, 'detached HEAD' if current_branch is None
else current_branch))
raise OSError(errno.ENOENT,
"Manifest repository at %s is checked out to 'default', but "
- "the git tracking configuration for that branch is broken; "
- "failing due to that." % (root,))
+ 'the git tracking configuration for that branch is broken; '
+ 'failing due to that.' % (root,))
# pylint: disable=W0221
@classmethod
return obj
-def _GitRepoIsContentMerging(git_repo, remote):
- """Identify if the given git repo has content merging marked.
-
- This is a gerrit >=2.2 bit of functionality; specifically, the content
- merging configuration is stored in a specially crafted branch which
- we access. If the branch is fetchable, we either return True or False.
-
- Args:
- git_repo: The local path to the git repository to inspect.
- remote: The configured remote to use from the given git repository.
-
- Returns:
- True if content merging is enabled, False if not.
-
- Raises:
- RunCommandError: Thrown if fetching fails due to either the namespace
- not existing, or a network error intervening.
- """
- # Need to use the manifest to get upstream gerrit; also, if upstream
- # doesn't provide a refs/meta/config for the repo, this will fail.
- RunGit(git_repo, ['fetch', remote, 'refs/meta/config:refs/meta/config'])
-
- content = RunGit(git_repo, ['show', 'refs/meta/config:project.config'],
- error_code_ok=True)
-
- if content.returncode != 0:
- return False
-
- try:
- result = RunGit(git_repo, ['config', '-f', '/dev/stdin', '--get',
- 'submit.mergeContent'], input=content.output)
- return result.output.strip().lower() == 'true'
- except cros_build_lib.RunCommandError as e:
- # If the field isn't set at all, exit code is 1.
- # Anything else is a bad invocation or an indecipherable state.
- if e.result.returncode != 1:
- raise
-
- return False
-
-
def RunGit(git_repo, cmd, retry=True, **kwargs):
"""RunCommand wrapper for git commands.
elif remote == '.':
if recurse == 0:
raise Exception(
- "While tracing out tracking branches, we recursed too deeply: "
- "bailing at %s" % branch)
+ 'While tracing out tracking branches, we recursed too deeply: '
+ 'bailing at %s' % branch)
return GetTrackingBranchViaGitConfig(
git_repo, StripRefsHeads(rev), for_checkout=for_checkout,
allow_broken_merge_settings=allow_broken_merge_settings,
start with refs/remotes/ (specifically must be a proper remote
target rather than an ambiguous name).
"""
- if not rebase_target.startswith("refs/remotes/"):
+ if not rebase_target.startswith('refs/remotes/'):
raise Exception(
- "Was asked to rebase to a non branch target w/in the push pathways. "
- "This is highly indicative of an internal bug. remote %s, rebase %s"
+ 'Was asked to rebase to a non branch target w/in the push pathways. '
+ 'This is highly indicative of an internal bug. remote %s, rebase %s'
% (remote, rebase_target))
cmd = ['remote', 'update', remote]
# impedence here; cros_mark_as_stable
_, local_ref = GetTrackingBranch(git_repo, branch, for_push=True)
- if not ref.startswith("refs/heads/"):
- raise Exception("Was asked to push to a non branch namespace: %s" % (ref,))
+ if not ref.startswith('refs/heads/'):
+ raise Exception('Was asked to push to a non branch namespace: %s' % (ref,))
push_command = ['push', remote, '%s:%s' % (branch, ref)]
- cros_build_lib.Debug("Trying to push %s to %s:%s", git_repo, branch, ref)
+ cros_build_lib.Debug('Trying to push %s to %s:%s', git_repo, branch, ref)
if dryrun:
push_command.append('--dry-run')
continue
raise
- cros_build_lib.Info("Successfully pushed %s to %s:%s", git_repo, branch, ref)
+ cros_build_lib.Info('Successfully pushed %s to %s:%s', git_repo, branch, ref)
def CleanAndDetachHead(git_repo):
# Not a manifest checkout.
Warning(
"Chromite checkout at %s isn't controlled by repo, nor is it on a "
- "branch (or if it is, the tracking configuration is missing or broken). "
- "Falling back to assuming the chromite checkout is derived from "
+ 'branch (or if it is, the tracking configuration is missing or broken). '
+ 'Falling back to assuming the chromite checkout is derived from '
"'master'; this *may* result in breakage." % cwd)
return 'master'