123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- # Copyright 2013 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- from recipe_engine import recipe_api
- class GerritApi(recipe_api.RecipeApi):
- """Module for interact with Gerrit endpoints"""
- def __init__(self, *args, **kwargs):
- super(GerritApi, self).__init__(*args, **kwargs)
- self._changes_target_branch_cache = {}
- def __call__(self, name, cmd, infra_step=True, **kwargs):
- """Wrapper for easy calling of gerrit_utils steps."""
- assert isinstance(cmd, (list, tuple))
- prefix = 'gerrit '
- env = self.m.context.env
- env.setdefault('PATH', '%(PATH)s')
- env['PATH'] = self.m.path.pathsep.join([
- env['PATH'], str(self.repo_resource())])
- with self.m.context(env=env):
- return self.m.python(prefix + name,
- self.repo_resource('gerrit_client.py'),
- cmd,
- infra_step=infra_step,
- venv=True,
- **kwargs)
- def create_gerrit_branch(self, host, project, branch, commit, **kwargs):
- """Creates a new branch from given project and commit
- Returns:
- The ref of the branch created
- """
- args = [
- 'branch',
- '--host', host,
- '--project', project,
- '--branch', branch,
- '--commit', commit,
- '--json_file', self.m.json.output()
- ]
- step_name = 'create_gerrit_branch (%s %s)' % (project, branch)
- step_result = self(step_name, args, **kwargs)
- ref = step_result.json.output.get('ref')
- return ref
- # TODO(machenbach): Rename to get_revision? And maybe above to
- # create_ref?
- def get_gerrit_branch(self, host, project, branch, **kwargs):
- """Gets a branch from given project and commit
- Returns:
- The revision of the branch
- """
- args = [
- 'branchinfo',
- '--host', host,
- '--project', project,
- '--branch', branch,
- '--json_file', self.m.json.output()
- ]
- step_name = 'get_gerrit_branch (%s %s)' % (project, branch)
- step_result = self(step_name, args, **kwargs)
- revision = step_result.json.output.get('revision')
- return revision
- def get_change_description(self,
- host,
- change,
- patchset,
- timeout=None,
- step_test_data=None):
- """Gets the description for a given CL and patchset.
- Args:
- host: URL of Gerrit host to query.
- change: The change number.
- patchset: The patchset number.
- Returns:
- The description corresponding to given CL and patchset.
- """
- ri = self.get_revision_info(host, change, patchset, timeout, step_test_data)
- return ri['commit']['message']
- def get_revision_info(self,
- host,
- change,
- patchset,
- timeout=None,
- step_test_data=None):
- """
- Returns the info for a given patchset of a given change.
- Args:
- host: Gerrit host to query.
- change: The change number.
- patchset: The patchset number.
- Returns:
- A dict for the target revision as documented here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes
- """
- assert int(change), change
- assert int(patchset), patchset
- step_test_data = step_test_data or (
- lambda: self.test_api.get_one_change_response_data(change_number=change,
- patchset=patchset))
- cls = self.get_changes(host,
- query_params=[('change', str(change))],
- o_params=['ALL_REVISIONS', 'ALL_COMMITS'],
- limit=1,
- timeout=timeout,
- step_test_data=step_test_data)
- cl = cls[0] if len(cls) == 1 else {'revisions': {}}
- for ri in cl['revisions'].values():
- # TODO(tandrii): add support for patchset=='current'.
- if str(ri['_number']) == str(patchset):
- return ri
- raise self.m.step.InfraFailure(
- 'Error querying for CL description: host:%r change:%r; patchset:%r' % (
- host, change, patchset))
- def get_changes(self, host, query_params, start=None, limit=None,
- o_params=None, step_test_data=None, **kwargs):
- """Queries changes for the given host.
- Args:
- * host: URL of Gerrit host to query.
- * query_params: Query parameters as list of (key, value) tuples to form a
- query as documented here:
- https://gerrit-review.googlesource.com/Documentation/user-search.html#search-operators
- * start: How many changes to skip (starting with the most recent).
- * limit: Maximum number of results to return.
- * o_params: A list of additional output specifiers, as documented here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes
- * step_test_data: Optional mock test data for the underlying gerrit client.
- Returns:
- A list of change dicts as documented here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes
- """
- args = [
- 'changes',
- '--host', host,
- '--json_file', self.m.json.output()
- ]
- if start:
- args += ['--start', str(start)]
- if limit:
- args += ['--limit', str(limit)]
- for k, v in query_params:
- args += ['-p', '%s=%s' % (k, v)]
- for v in (o_params or []):
- args += ['-o', v]
- if not step_test_data:
- step_test_data = lambda: self.test_api.get_one_change_response_data()
- return self(
- kwargs.pop('name', 'changes'),
- args,
- step_test_data=step_test_data,
- **kwargs
- ).json.output
- def get_related_changes(self, host, change, revision='current', step_test_data=None):
- """Queries related changes for a given host, change, and revision.
- Args:
- * host: URL of Gerrit host to query.
- * change: The change-id of the change to get related changes for as
- documented here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-id
- * revision: The revision-id of the revision to get related changes for as
- documented here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#revision-id
- This defaults to current, which names the most recent patch set.
- * step_test_data: Optional mock test data for the underlying gerrit client.
- Returns:
- A related changes dictionary as documented here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#related-changes-info
- """
- args = [
- 'relatedchanges',
- '--host',
- host,
- '--change',
- change,
- '--revision',
- revision,
- '--json_file',
- self.m.json.output(),
- ]
- if not step_test_data:
- step_test_data = lambda: self.test_api.get_related_changes_response_data()
- return self('relatedchanges', args,
- step_test_data=step_test_data).json.output
- def abandon_change(self, host, change, message=None, name=None,
- step_test_data=None):
- args = [
- 'abandon',
- '--host', host,
- '--change', int(change),
- '--json_file', self.m.json.output(),
- ]
- if message:
- args.extend(['--message', message])
- if not step_test_data:
- step_test_data = lambda: self.test_api.get_one_change_response_data(
- status='ABANDONED', _number=str(change))
- return self(
- name or 'abandon',
- args,
- step_test_data=step_test_data,
- ).json.output
- def set_change_label(self,
- host,
- change,
- label_name,
- label_value,
- name=None,
- step_test_data=None):
- args = [
- 'setlabel', '--host', host, '--change',
- int(change), '--json_file',
- self.m.json.output(), '-l', label_name, label_value
- ]
- return self(
- name or 'setlabel',
- args,
- step_test_data=step_test_data,
- ).json.output
- def move_changes(self,
- host,
- project,
- from_branch,
- to_branch,
- step_test_data=None):
- args = [
- 'movechanges', '--host', host, '-p',
- 'project=%s' % project, '-p',
- 'branch=%s' % from_branch, '-p', 'status=open', '--destination_branch',
- to_branch, '--json_file',
- self.m.json.output()
- ]
- if not step_test_data:
- step_test_data = lambda: self.test_api.get_one_change_response_data(
- branch=to_branch)
- return self(
- 'move changes',
- args,
- step_test_data=step_test_data,
- ).json.output
- def update_files(self,
- host,
- project,
- branch,
- new_contents_by_file_path,
- commit_msg,
- params=frozenset(['status=NEW']),
- submit=False):
- """Update a set of files by creating and submitting a Gerrit CL.
- Args:
- * host: URL of Gerrit host to name.
- * project: Gerrit project name, e.g. chromium/src.
- * branch: The branch to land the change, e.g. main
- * new_contents_by_file_path: Dict of the new contents with file path as
- the key.
- * commit_msg: Description to add to the CL.
- * params: A list of additional ChangeInput specifiers, with format
- 'key=value'.
- * submit: Should land this CL instantly.
- Returns:
- A ChangeInfo dictionary as documented here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#create-change
- Or if the change is submitted, here:
- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#submit-change
- """
- assert len(new_contents_by_file_path
- ) > 0, 'The dict of file paths should not be empty.'
- command = [
- 'createchange',
- '--host',
- host,
- '--project',
- project,
- '--branch',
- branch,
- '--subject',
- commit_msg,
- '--json_file',
- self.m.json.output(),
- ]
- for p in params:
- command.extend(['-p', p])
- step_result = self('create change at (%s %s)' % (project, branch), command)
- change = int(step_result.json.output.get('_number'))
- step_result.presentation.links['change %d' %
- change] = '%s/#/q/%d' % (host, change)
- with self.m.step.nest('update contents in CL %d' % change):
- for path, content in new_contents_by_file_path.items():
- _file = self.m.path.mkstemp()
- self.m.file.write_raw('store the new content for %s' % path, _file,
- content)
- self('edit file %s' % path, [
- 'changeedit',
- '--host',
- host,
- '--change',
- change,
- '--path',
- path,
- '--file',
- _file,
- ])
- self('publish edit', [
- 'publishchangeedit',
- '--host',
- host,
- '--change',
- change,
- ])
- if submit:
- self('set Bot-Commit+1 for change %d' % change, [
- 'setbotcommit',
- '--host',
- host,
- '--change',
- change,
- ])
- submit_cmd = [
- 'submitchange',
- '--host',
- host,
- '--change',
- change,
- '--json_file',
- self.m.json.output(),
- ]
- step_result = self('submit change %d' % change, submit_cmd)
- return step_result.json.output
|