From 9eed4238d820b24fb4e106fc0f389c75bf894ce2 Mon Sep 17 00:00:00 2001 From: LaMont Jones Date: Fri, 2 Apr 2021 16:29:49 +0000 Subject: [PATCH] gerrit: add createchange changeedit publishchangeedit Add calls to support creating changes, as well as editing and publishing them in gerrit_client and gerrit_util. Bug=b:182613582 Change-Id: I0514cf08dce63ab29d99d4485d96fa124006326a Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2800811 Auto-Submit: LaMont Jones Reviewed-by: Dirk Pranke Reviewed-by: Josip Sokcevic Commit-Queue: LaMont Jones --- gerrit_client.py | 58 +++++++++++++++++++++++++++++++++++++ gerrit_util.py | 42 +++++++++++++++++++++++++++ tests/gerrit_client_test.py | 34 ++++++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/gerrit_client.py b/gerrit_client.py index de397a4640..8c51a77a9d 100755 --- a/gerrit_client.py +++ b/gerrit_client.py @@ -153,6 +153,64 @@ def CMDchanges(parser, args): write_result(result, opt) +@subcommand.usage('[args ...]') +def CMDcreatechange(parser, args): + parser.add_option('-s', '--subject', help='subject for change') + parser.add_option('-b', + '--branch', + default='main', + help='target branch for change') + parser.add_option( + '-p', + '--param', + dest='params', + action='append', + help='repeatable field value parameter, format: -p key=value') + + (opt, args) = parser.parse_args(args) + for p in opt.params: + assert '=' in p, '--param is key=value, not "%s"' % p + + result = gerrit_util.CreateChange( + urlparse.urlparse(opt.host).netloc, + opt.project, + branch=opt.branch, + subject=opt.subject, + params=list(tuple(p.split('=', 1)) for p in opt.params), + ) + logging.info(result) + write_result(result, opt) + + +@subcommand.usage('[args ...]') +def CMDchangeedit(parser, args): + parser.add_option('-c', '--change', type=int, help='change number') + parser.add_option('--path', help='path for file') + parser.add_option('--file', help='file to place at |path|') + + (opt, args) = parser.parse_args(args) + + with open(opt.file) as f: + data = f.read() + result = gerrit_util.ChangeEdit( + urlparse.urlparse(opt.host).netloc, opt.change, opt.path, data) + logging.info(result) + write_result(result, opt) + + +@subcommand.usage('[args ...]') +def CMDpublishchangeedit(parser, args): + parser.add_option('-c', '--change', type=int, help='change number') + parser.add_option('--notify', help='whether to notify') + + (opt, args) = parser.parse_args(args) + + result = gerrit_util.PublishChangeEdit( + urlparse.urlparse(opt.host).netloc, opt.change, opt.notify) + logging.info(result) + write_result(result, opt) + + @subcommand.usage('') def CMDabandon(parser, args): parser.add_option('-c', '--change', type=int, help='change number') diff --git a/gerrit_util.py b/gerrit_util.py index 1b3dc735c2..4d817c36cc 100644 --- a/gerrit_util.py +++ b/gerrit_util.py @@ -737,6 +737,25 @@ def SubmitChange(host, change, wait_for_merge=True): return ReadHttpJsonResponse(conn) +def PublishChangeEdit(host, change, notify=True): + """Publish a Gerrit change edit.""" + path = 'changes/%s/edit:publish' % change + body = {'notify': 'ALL' if notify else 'NONE'} + conn = CreateHttpConn(host, path, reqtype='POST', body=body) + return ReadHttpJsonResponse(conn, accept_statuses=(204, )) + + +def ChangeEdit(host, change, path, data): + """Puts content of a file into a change edit.""" + path = 'changes/%s/edit/%s' % (change, urllib.parse.quote(path, '')) + body = { + 'binary_content': + 'data:text/plain;base64,%s' % base64.b64encode(data.encode('utf-8')) + } + conn = CreateHttpConn(host, path, reqtype='PUT', body=body) + return ReadHttpJsonResponse(conn, accept_statuses=(204, 409)) + + def HasPendingChangeEdit(host, change): conn = CreateHttpConn(host, 'changes/%s/edit' % change) try: @@ -930,6 +949,29 @@ def ResetReviewLabels(host, change, label, value='0', message=None, 'a new patchset was uploaded.' % change) +def CreateChange(host, project, branch='main', subject='', params=()): + """ + Creates a new change. + + Args: + params: A list of additional ChangeInput specifiers, as documented here: + (e.g. ('is_private', 'true') to mark the change private. + https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-input + + Returns: + ChangeInfo for the new change. + """ + path = 'changes/' + body = {'project': project, 'branch': branch, 'subject': subject} + body.update({k: v for k, v in params}) + for key in 'project', 'branch', 'subject': + if not body[key]: + raise GerritError(200, '%s is required' % key.title()) + + conn = CreateHttpConn(host, path, reqtype='POST', body=body) + return ReadHttpJsonResponse(conn, accept_statuses=[201]) + + def CreateGerritBranch(host, project, branch, commit): """Creates a new branch from given project and commit diff --git a/tests/gerrit_client_test.py b/tests/gerrit_client_test.py index 90b0a89634..d1a94440c8 100755 --- a/tests/gerrit_client_test.py +++ b/tests/gerrit_client_test.py @@ -13,9 +13,11 @@ import unittest if sys.version_info.major == 2: from StringIO import StringIO import mock + BUILTIN_OPEN = '__builtin__.open' else: from io import StringIO from unittest import mock + BUILTIN_OPEN = 'builtins.open' sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -54,6 +56,38 @@ class TestGerritClient(unittest.TestCase): start=20, o_params=['op1', 'op2']) + @mock.patch('gerrit_util.CreateChange', return_value={}) + def test_createchange(self, util_mock): + gerrit_client.main([ + 'createchange', '--host', 'https://example.org/foo', '--project', + 'project', '--branch', 'main', '--subject', 'subject', '-p', + 'work_in_progress=true' + ]) + util_mock.assert_called_once_with('example.org', + 'project', + branch='main', + subject='subject', + params=[('work_in_progress', 'true')]) + + @mock.patch(BUILTIN_OPEN, mock.mock_open()) + @mock.patch('gerrit_util.ChangeEdit', return_value='') + def test_changeedit(self, util_mock): + open().read.return_value = 'test_data' + gerrit_client.main([ + 'changeedit', '--host', 'https://example.org/foo', '--change', '1', + '--path', 'path/to/file', '--file', '/my/foo' + ]) + util_mock.assert_called_once_with('example.org', 1, 'path/to/file', + 'test_data') + + @mock.patch('gerrit_util.PublishChangeEdit', return_value='') + def test_publishchangeedit(self, util_mock): + gerrit_client.main([ + 'publishchangeedit', '--host', 'https://example.org/foo', '--change', + '1', '--notify', 'yes' + ]) + util_mock.assert_called_once_with('example.org', 1, 'yes') + @mock.patch('gerrit_util.AbandonChange', return_value='') def test_abandon(self, util_mock): gerrit_client.main([