blob: 3dd9fd25acf62b3482294ba6ee1f9569d791d37e [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Ben Komalo08a3f682010-07-15 16:03:02 -070018import copy
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import re
20import sys
21
22from command import InteractiveCommand
23from editor import Editor
Doug Anderson37282b42011-03-04 11:54:18 -080024from error import HookError, UploadError
Conley Owens3bfd7212013-09-30 15:54:38 -070025from git_command import GitCommand
Sean McAllister682f0b62020-05-18 09:15:51 -060026from git_refs import R_HEADS
Doug Anderson37282b42011-03-04 11:54:18 -080027from project import RepoHook
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028
David Pursehouse59bbb582013-05-17 10:49:33 +090029from pyversion import is_python3
30if not is_python3():
David Pursehousea46bf7d2020-02-15 12:45:53 +090031 input = raw_input # noqa: F821
Anthony Kingd792f792014-05-05 22:01:07 +010032else:
33 unicode = str
Chirayu Desai217ea7d2013-03-01 19:14:38 +053034
Dan Morrillf0a9a1a2010-05-05 08:18:35 -070035UNUSUAL_COMMIT_THRESHOLD = 5
Dan Morrill879a9a52010-05-04 16:56:07 -070036
David Pursehouse819827a2020-02-12 15:20:19 +090037
Dan Morrill879a9a52010-05-04 16:56:07 -070038def _ConfirmManyUploads(multiple_branches=False):
39 if multiple_branches:
David Pursehouse2f9e7e42013-03-05 17:26:46 +090040 print('ATTENTION: One or more branches has an unusually high number '
Sarah Owenscecd1d82012-11-01 22:59:27 -070041 'of commits.')
Dan Morrill879a9a52010-05-04 16:56:07 -070042 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -070043 print('ATTENTION: You are uploading an unusually high number of commits.')
David Pursehouse2f9e7e42013-03-05 17:26:46 +090044 print('YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across '
Sarah Owenscecd1d82012-11-01 22:59:27 -070045 'branches?)')
Chirayu Desai217ea7d2013-03-01 19:14:38 +053046 answer = input("If you are sure you intend to do this, type 'yes': ").strip()
Dan Morrill879a9a52010-05-04 16:56:07 -070047 return answer == "yes"
48
David Pursehouse819827a2020-02-12 15:20:19 +090049
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070050def _die(fmt, *args):
51 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070052 print('error: %s' % msg, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070053 sys.exit(1)
54
David Pursehouse819827a2020-02-12 15:20:19 +090055
Joe Onorato2896a792008-11-17 16:56:36 -050056def _SplitEmails(values):
57 result = []
David Pursehouse8a68ff92012-09-24 12:15:13 +090058 for value in values:
59 result.extend([s.strip() for s in value.split(',')])
Joe Onorato2896a792008-11-17 16:56:36 -050060 return result
61
David Pursehouse819827a2020-02-12 15:20:19 +090062
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070063class Upload(InteractiveCommand):
64 common = True
65 helpSummary = "Upload changes for code review"
David Pursehouse8f62fb72012-11-14 12:09:38 +090066 helpUsage = """
Ficus Kirkpatricka0de6e82010-10-22 13:06:47 -070067%prog [--re --cc] [<project>]...
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068"""
69 helpDescription = """
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -070070The '%prog' command is used to send changes to the Gerrit Code
71Review system. It searches for topic branches in local projects
72that have not yet been published for review. If multiple topic
73branches are found, '%prog' opens an editor to allow the user to
74select which branches to upload.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070075
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -070076'%prog' searches for uploadable changes in all projects listed at
77the command line. Projects can be specified either by name, or by
78a relative or absolute path to the project's local directory. If no
79projects are specified, '%prog' will search for uploadable changes
80in all projects listed in the manifest.
Joe Onorato2896a792008-11-17 16:56:36 -050081
82If the --reviewers or --cc options are passed, those emails are
83added to the respective list of users, and emails are sent to any
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -070084new users. Users passed as --reviewers must already be registered
Joe Onorato2896a792008-11-17 16:56:36 -050085with the code review system, or the upload will fail.
Shawn O. Pearcea6df7d22008-12-12 08:04:07 -080086
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040087# Configuration
Shawn O. Pearcea608fb02009-04-17 12:11:24 -070088
89review.URL.autoupload:
90
Mike Frysingere9311272011-08-11 15:46:43 -040091To disable the "Upload ... (y/N)?" prompt, you can set a per-project
Shawn O. Pearcea608fb02009-04-17 12:11:24 -070092or global Git configuration option. If review.URL.autoupload is set
93to "true" then repo will assume you always answer "y" at the prompt,
94and will not prompt you further. If it is set to "false" then repo
95will assume you always answer "n", and will abort.
96
bijia093fdb62013-11-28 09:19:22 +080097review.URL.autoreviewer:
98
99To automatically append a user or mailing list to reviews, you can set
100a per-project or global Git option to do so.
101
Ben Komalo08a3f682010-07-15 16:03:02 -0700102review.URL.autocopy:
103
104To automatically copy a user or mailing list to all uploaded reviews,
105you can set a per-project or global Git option to do so. Specifically,
106review.URL.autocopy can be set to a comma separated list of reviewers
107who you always want copied on all uploads with a non-empty --re
108argument.
109
Shawn O. Pearce3575b8f2010-07-15 17:00:14 -0700110review.URL.username:
111
112Override the username used to connect to Gerrit Code Review.
113By default the local part of the email address is used.
114
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700115The URL must match the review URL listed in the manifest XML file,
116or in the .git/config within the project. For example:
117
118 [remote "origin"]
119 url = git://git.example.com/project.git
120 review = http://review.example.com/
121
122 [review "http://review.example.com/"]
123 autoupload = true
Ben Komalo08a3f682010-07-15 16:03:02 -0700124 autocopy = johndoe@company.com,my-team-alias@company.com
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700125
Anthony Russellod666e932012-06-01 00:48:22 -0400126review.URL.uploadtopic:
127
128To add a topic branch whenever uploading a commit, you can set a
129per-project or global Git option to do so. If review.URL.uploadtopic
130is set to "true" then repo will assume you always want the equivalent
131of the -t option to the repo command. If unset or set to "false" then
132repo will make use of only the command line option.
133
Mike Frysinger84685ba2020-02-19 02:22:22 -0500134review.URL.uploadhashtags:
135
136To add hashtags whenever uploading a commit, you can set a per-project
137or global Git option to do so. The value of review.URL.uploadhashtags
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500138will be used as comma delimited hashtags like the --hashtag option.
139
140review.URL.uploadlabels:
141
142To add labels whenever uploading a commit, you can set a per-project
143or global Git option to do so. The value of review.URL.uploadlabels
144will be used as comma delimited labels like the --label option.
Mike Frysinger84685ba2020-02-19 02:22:22 -0500145
Mike Frysingerf725e542020-03-14 17:39:03 -0400146review.URL.uploadnotify:
147
148Control e-mail notifications when uploading.
149https://gerrit-review.googlesource.com/Documentation/user-upload.html#notify
150
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400151# References
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -0700152
Mike Frysinger3b24e7b2018-10-10 00:57:44 -0400153Gerrit Code Review: https://www.gerritcodereview.com/
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -0700154
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700155"""
156
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800157 def _Options(self, p):
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700158 p.add_option('-t',
159 dest='auto_topic', action='store_true',
160 help='Send local branch name to Gerrit Code Review')
Mike Frysinger84685ba2020-02-19 02:22:22 -0500161 p.add_option('--hashtag', '--ht',
162 dest='hashtags', action='append', default=[],
163 help='Add hashtags (comma delimited) to the review.')
Mike Frysinger7ff80af2020-02-19 03:00:26 -0500164 p.add_option('--hashtag-branch', '--htb',
165 action='store_true',
166 help='Add local branch name as a hashtag.')
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500167 p.add_option('-l', '--label',
168 dest='labels', action='append', default=[],
169 help='Add a label when uploading.')
Joe Onorato2896a792008-11-17 16:56:36 -0500170 p.add_option('--re', '--reviewers',
David Pursehouse08671042020-02-12 13:52:31 +0900171 type='string', action='append', dest='reviewers',
Joe Onorato2896a792008-11-17 16:56:36 -0500172 help='Request reviews from these people.')
173 p.add_option('--cc',
David Pursehouse08671042020-02-12 13:52:31 +0900174 type='string', action='append', dest='cc',
Joe Onorato2896a792008-11-17 16:56:36 -0500175 help='Also send email to these email addresses.')
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700176 p.add_option('--br',
David Pursehouse08671042020-02-12 13:52:31 +0900177 type='string', action='store', dest='branch',
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700178 help='Branch to upload.')
Daniel Sandlere9d6b612012-04-06 10:39:32 -0400179 p.add_option('--cbr', '--current-branch',
180 dest='current_branch', action='store_true',
181 help='Upload current git branch.')
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700182 p.add_option('--ne', '--no-emails',
183 action='store_false', dest='notify', default=True,
184 help='If specified, do not send emails on upload.')
Changcheng Xiao87984c62017-08-02 16:55:03 +0200185 p.add_option('-p', '--private',
186 action='store_true', dest='private', default=False,
187 help='If specified, upload as a private change.')
188 p.add_option('-w', '--wip',
189 action='store_true', dest='wip', default=False,
190 help='If specified, upload as a work-in-progress change.')
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800191 p.add_option('-o', '--push-option',
192 type='string', action='append', dest='push_options',
193 default=[],
194 help='Additional push options to transmit')
Bryan Jacobsf609f912013-05-06 13:36:24 -0400195 p.add_option('-D', '--destination', '--dest',
196 type='string', action='store', dest='dest_branch',
197 metavar='BRANCH',
198 help='Submit for review on this target branch.')
Mike Frysinger819cc812020-02-19 02:27:22 -0500199 p.add_option('-n', '--dry-run',
200 dest='dryrun', default=False, action='store_true',
201 help='Do everything except actually upload the CL.')
Mike Frysinger02aa8892020-02-19 02:32:52 -0500202 p.add_option('-y', '--yes',
203 default=False, action='store_true',
204 help='Answer yes to all safe prompts.')
Mike Frysinger21c15752020-02-11 05:17:16 -0500205 p.add_option('--no-cert-checks',
206 dest='validate_certs', action='store_false', default=True,
207 help='Disable verifying ssl certs (unsafe).')
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800208
Doug Anderson37282b42011-03-04 11:54:18 -0800209 # Options relating to upload hook. Note that verify and no-verify are NOT
210 # opposites of each other, which is why they store to different locations.
211 # We are using them to match 'git commit' syntax.
212 #
213 # Combinations:
214 # - no-verify=False, verify=False (DEFAULT):
215 # If stdout is a tty, can prompt about running upload hooks if needed.
216 # If user denies running hooks, the upload is cancelled. If stdout is
217 # not a tty and we would need to prompt about upload hooks, upload is
218 # cancelled.
219 # - no-verify=False, verify=True:
220 # Always run upload hooks with no prompt.
221 # - no-verify=True, verify=False:
222 # Never run upload hooks, but upload anyway (AKA bypass hooks).
223 # - no-verify=True, verify=True:
224 # Invalid
Mike Frysinger21c15752020-02-11 05:17:16 -0500225 g = p.add_option_group('Upload hooks')
226 g.add_option('--no-verify',
Doug Anderson37282b42011-03-04 11:54:18 -0800227 dest='bypass_hooks', action='store_true',
228 help='Do not run the upload hook.')
Mike Frysinger21c15752020-02-11 05:17:16 -0500229 g.add_option('--verify',
Doug Anderson37282b42011-03-04 11:54:18 -0800230 dest='allow_all_hooks', action='store_true',
231 help='Run the upload hook without prompting.')
Mike Frysinger21c15752020-02-11 05:17:16 -0500232 g.add_option('--ignore-hooks',
233 dest='ignore_hooks', action='store_true',
234 help='Do not abort uploading if upload hooks fail.')
Doug Anderson37282b42011-03-04 11:54:18 -0800235
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700236 def _SingleBranch(self, opt, branch, people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237 project = branch.project
238 name = branch.name
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700239 remote = project.GetBranch(name).remote
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700241 key = 'review.%s.autoupload' % remote.review
242 answer = project.config.GetBoolean(key)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700243
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700244 if answer is False:
245 _die("upload blocked by %s = false" % key)
246
247 if answer is None:
Shawn O. Pearce66bdd462009-04-17 18:47:22 -0700248 date = branch.date
David Pursehouse8a68ff92012-09-24 12:15:13 +0900249 commit_list = branch.commits
Shawn O. Pearce66bdd462009-04-17 18:47:22 -0700250
Chirayu Desai610d3c42013-06-24 14:02:12 +0530251 destination = opt.dest_branch or project.dest_branch or project.revisionExpr
Nicolas Cornub54343d2017-07-10 10:31:24 +0200252 print('Upload project %s/ to remote branch %s%s:' %
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -0500253 (project.relpath, destination, ' (private)' if opt.private else ''))
Sarah Owenscecd1d82012-11-01 22:59:27 -0700254 print(' branch %s (%2d commit%s, %s):' % (
David Pursehouseabdf7502020-02-12 14:58:39 +0900255 name,
256 len(commit_list),
257 len(commit_list) != 1 and 's' or '',
258 date))
David Pursehouse8a68ff92012-09-24 12:15:13 +0900259 for commit in commit_list:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700260 print(' %s' % commit)
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700261
Mike Frysingerab85fe72019-07-04 17:35:11 -0400262 print('to %s (y/N)? ' % remote.review, end='')
263 # TODO: When we require Python 3, use flush=True w/print above.
264 sys.stdout.flush()
Mike Frysinger02aa8892020-02-19 02:32:52 -0500265 if opt.yes:
266 print('<--yes>')
267 answer = True
268 else:
269 answer = sys.stdin.readline().strip().lower()
270 answer = answer in ('y', 'yes', '1', 'true', 't')
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700271
272 if answer:
Dan Morrill879a9a52010-05-04 16:56:07 -0700273 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
274 answer = _ConfirmManyUploads()
275
276 if answer:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700277 self._UploadAndReport(opt, [branch], people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700278 else:
279 _die("upload aborted by user")
280
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700281 def _MultipleBranches(self, opt, pending, people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700282 projects = {}
283 branches = {}
284
285 script = []
286 script.append('# Uncomment the branches to upload:')
287 for project, avail in pending:
288 script.append('#')
289 script.append('# project %s/:' % project.relpath)
290
291 b = {}
292 for branch in avail:
Bryan Jacobs710d4b02013-05-31 15:28:05 -0400293 if branch is None:
294 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700295 name = branch.name
296 date = branch.date
David Pursehouse8a68ff92012-09-24 12:15:13 +0900297 commit_list = branch.commits
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700298
299 if b:
300 script.append('#')
Bryan Jacobs691a7592013-05-31 15:45:28 -0400301 destination = opt.dest_branch or project.dest_branch or project.revisionExpr
Christer Fletcher6a1f7372011-04-28 14:13:14 +0200302 script.append('# branch %s (%2d commit%s, %s) to remote branch %s:' % (
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700303 name,
David Pursehouse8a68ff92012-09-24 12:15:13 +0900304 len(commit_list),
305 len(commit_list) != 1 and 's' or '',
Christer Fletcher6a1f7372011-04-28 14:13:14 +0200306 date,
Bryan Jacobs691a7592013-05-31 15:45:28 -0400307 destination))
David Pursehouse8a68ff92012-09-24 12:15:13 +0900308 for commit in commit_list:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700309 script.append('# %s' % commit)
310 b[name] = branch
311
312 projects[project.relpath] = project
313 branches[project.name] = b
314 script.append('')
315
316 script = Editor.EditString("\n".join(script)).split("\n")
317
318 project_re = re.compile(r'^#?\s*project\s*([^\s]+)/:$')
319 branch_re = re.compile(r'^\s*branch\s*([^\s(]+)\s*\(.*')
320
321 project = None
322 todo = []
323
324 for line in script:
325 m = project_re.match(line)
326 if m:
327 name = m.group(1)
328 project = projects.get(name)
329 if not project:
330 _die('project %s not available for upload', name)
331 continue
332
333 m = branch_re.match(line)
334 if m:
335 name = m.group(1)
336 if not project:
337 _die('project for branch %s not in script', name)
338 branch = branches[project.name].get(name)
339 if not branch:
340 _die('branch %s not in %s', name, project.relpath)
341 todo.append(branch)
342 if not todo:
343 _die("nothing uncommented for upload")
Dan Morrill879a9a52010-05-04 16:56:07 -0700344
345 many_commits = False
346 for branch in todo:
347 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
348 many_commits = True
349 break
350 if many_commits:
351 if not _ConfirmManyUploads(multiple_branches=True):
352 _die("upload aborted by user")
353
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700354 self._UploadAndReport(opt, todo, people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700355
bijia093fdb62013-11-28 09:19:22 +0800356 def _AppendAutoList(self, branch, people):
Ben Komalo08a3f682010-07-15 16:03:02 -0700357 """
bijia093fdb62013-11-28 09:19:22 +0800358 Appends the list of reviewers in the git project's config.
Ben Komalo08a3f682010-07-15 16:03:02 -0700359 Appends the list of users in the CC list in the git project's config if a
360 non-empty reviewer list was found.
361 """
Ben Komalo08a3f682010-07-15 16:03:02 -0700362 name = branch.name
363 project = branch.project
bijia093fdb62013-11-28 09:19:22 +0800364
365 key = 'review.%s.autoreviewer' % project.GetBranch(name).remote.review
366 raw_list = project.config.GetString(key)
David Pursehouse8f78a832020-02-12 11:20:36 +0900367 if raw_list is not None:
bijia093fdb62013-11-28 09:19:22 +0800368 people[0].extend([entry.strip() for entry in raw_list.split(',')])
369
Ben Komalo08a3f682010-07-15 16:03:02 -0700370 key = 'review.%s.autocopy' % project.GetBranch(name).remote.review
371 raw_list = project.config.GetString(key)
David Pursehouse8f78a832020-02-12 11:20:36 +0900372 if raw_list is not None and len(people[0]) > 0:
Ben Komalo08a3f682010-07-15 16:03:02 -0700373 people[1].extend([entry.strip() for entry in raw_list.split(',')])
374
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700375 def _FindGerritChange(self, branch):
376 last_pub = branch.project.WasPublished(branch.name)
377 if last_pub is None:
378 return ""
379
380 refs = branch.GetPublishedRefs()
381 try:
382 # refs/changes/XYZ/N --> XYZ
383 return refs.get(last_pub).split('/')[-2]
David Pursehouse1d947b32012-10-25 12:23:11 +0900384 except (AttributeError, IndexError):
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700385 return ""
386
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700387 def _UploadAndReport(self, opt, todo, original_people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700388 have_errors = False
389 for branch in todo:
390 try:
Ben Komalo08a3f682010-07-15 16:03:02 -0700391 people = copy.deepcopy(original_people)
bijia093fdb62013-11-28 09:19:22 +0800392 self._AppendAutoList(branch, people)
Ben Komalo08a3f682010-07-15 16:03:02 -0700393
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500394 # Check if there are local changes that may have been forgotten
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700395 changes = branch.project.UncommitedFiles()
396 if changes:
David Pursehousec1b86a22012-11-14 11:36:51 +0900397 key = 'review.%s.autoupload' % branch.project.remote.review
398 answer = branch.project.config.GetBoolean(key)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500399
David Pursehousec1b86a22012-11-14 11:36:51 +0900400 # if they want to auto upload, let's not ask because it could be automated
401 if answer is None:
Mike Frysingerab85fe72019-07-04 17:35:11 -0400402 print()
403 print('Uncommitted changes in %s (did you forget to amend?):'
404 % branch.project.name)
405 print('\n'.join(changes))
406 print('Continue uploading? (y/N) ', end='')
407 # TODO: When we require Python 3, use flush=True w/print above.
408 sys.stdout.flush()
Mike Frysinger02aa8892020-02-19 02:32:52 -0500409 if opt.yes:
410 print('<--yes>')
411 a = 'yes'
412 else:
413 a = sys.stdin.readline().strip().lower()
David Pursehousec1b86a22012-11-14 11:36:51 +0900414 if a not in ('y', 'yes', 't', 'true', 'on'):
415 print("skipping upload", file=sys.stderr)
416 branch.uploaded = False
417 branch.error = 'User aborted'
418 continue
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500419
Anthony Russellod666e932012-06-01 00:48:22 -0400420 # Check if topic branches should be sent to the server during upload
421 if opt.auto_topic is not True:
David Pursehousec1b86a22012-11-14 11:36:51 +0900422 key = 'review.%s.uploadtopic' % branch.project.remote.review
423 opt.auto_topic = branch.project.config.GetBoolean(key)
Anthony Russellod666e932012-06-01 00:48:22 -0400424
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500425 def _ExpandCommaList(value):
426 """Split |value| up into comma delimited entries."""
Mike Frysinger84685ba2020-02-19 02:22:22 -0500427 if not value:
428 return
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500429 for ret in value.split(','):
430 ret = ret.strip()
431 if ret:
432 yield ret
433
434 # Check if hashtags should be included.
Mike Frysinger84685ba2020-02-19 02:22:22 -0500435 key = 'review.%s.uploadhashtags' % branch.project.remote.review
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500436 hashtags = set(_ExpandCommaList(branch.project.config.GetString(key)))
Mike Frysinger84685ba2020-02-19 02:22:22 -0500437 for tag in opt.hashtags:
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500438 hashtags.update(_ExpandCommaList(tag))
Mike Frysinger7ff80af2020-02-19 03:00:26 -0500439 if opt.hashtag_branch:
440 hashtags.add(branch.name)
Mike Frysinger84685ba2020-02-19 02:22:22 -0500441
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500442 # Check if labels should be included.
443 key = 'review.%s.uploadlabels' % branch.project.remote.review
444 labels = set(_ExpandCommaList(branch.project.config.GetString(key)))
445 for label in opt.labels:
446 labels.update(_ExpandCommaList(label))
447 # Basic sanity check on label syntax.
448 for label in labels:
449 if not re.match(r'^.+[+-][0-9]+$', label):
450 print('repo: error: invalid label syntax "%s": labels use forms '
451 'like CodeReview+1 or Verified-1' % (label,), file=sys.stderr)
452 sys.exit(1)
453
Mike Frysingerf725e542020-03-14 17:39:03 -0400454 # Handle e-mail notifications.
455 if opt.notify is False:
456 notify = 'NONE'
457 else:
458 key = 'review.%s.uploadnotify' % branch.project.remote.review
459 notify = branch.project.config.GetString(key)
460
Colin Cross59b31cb2013-10-08 23:10:52 -0700461 destination = opt.dest_branch or branch.project.dest_branch
Conley Owens3bfd7212013-09-30 15:54:38 -0700462
463 # Make sure our local branch is not setup to track a different remote branch
464 merge_branch = self._GetMergeBranch(branch.project)
Conley Owensfbd3f2a2013-10-15 12:59:00 -0700465 if destination:
Sean McAllister682f0b62020-05-18 09:15:51 -0600466 full_dest = destination
467 if not full_dest.startswith(R_HEADS):
468 full_dest = R_HEADS + full_dest
469
Conley Owensfbd3f2a2013-10-15 12:59:00 -0700470 if not opt.dest_branch and merge_branch and merge_branch != full_dest:
471 print('merge branch %s does not match destination branch %s'
472 % (merge_branch, full_dest))
473 print('skipping upload.')
474 print('Please use `--destination %s` if this is intentional'
475 % destination)
476 branch.uploaded = False
477 continue
Conley Owens3bfd7212013-09-30 15:54:38 -0700478
Changcheng Xiao87984c62017-08-02 16:55:03 +0200479 branch.UploadForReview(people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500480 dryrun=opt.dryrun,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200481 auto_topic=opt.auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500482 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500483 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200484 private=opt.private,
Mike Frysingerf725e542020-03-14 17:39:03 -0400485 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200486 wip=opt.wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200487 dest_branch=destination,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800488 validate_certs=opt.validate_certs,
489 push_options=opt.push_options)
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200490
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700491 branch.uploaded = True
Sarah Owensa5be53f2012-09-09 15:37:57 -0700492 except UploadError as e:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700493 branch.error = e
494 branch.uploaded = False
495 have_errors = True
496
Sarah Owenscecd1d82012-11-01 22:59:27 -0700497 print(file=sys.stderr)
498 print('----------------------------------------------------------------------', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700499
500 if have_errors:
501 for branch in todo:
502 if not branch.uploaded:
Shawn O. Pearcef00e0ce2009-08-22 18:39:49 -0700503 if len(str(branch.error)) <= 30:
504 fmt = ' (%s)'
505 else:
506 fmt = '\n (%s)'
Sarah Owenscecd1d82012-11-01 22:59:27 -0700507 print(('[FAILED] %-15s %-15s' + fmt) % (
David Pursehouseabdf7502020-02-12 14:58:39 +0900508 branch.project.relpath + '/',
509 branch.name,
510 str(branch.error)),
511 file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700512 print()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700513
514 for branch in todo:
David Pursehousec1b86a22012-11-14 11:36:51 +0900515 if branch.uploaded:
516 print('[OK ] %-15s %s' % (
David Pursehouseabdf7502020-02-12 14:58:39 +0900517 branch.project.relpath + '/',
518 branch.name),
519 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700520
521 if have_errors:
522 sys.exit(1)
523
Conley Owens3bfd7212013-09-30 15:54:38 -0700524 def _GetMergeBranch(self, project):
525 p = GitCommand(project,
526 ['rev-parse', '--abbrev-ref', 'HEAD'],
David Pursehousee5913ae2020-02-12 13:56:59 +0900527 capture_stdout=True,
528 capture_stderr=True)
Conley Owens3bfd7212013-09-30 15:54:38 -0700529 p.Wait()
530 local_branch = p.stdout.strip()
531 p = GitCommand(project,
532 ['config', '--get', 'branch.%s.merge' % local_branch],
David Pursehousee5913ae2020-02-12 13:56:59 +0900533 capture_stdout=True,
534 capture_stderr=True)
Conley Owens3bfd7212013-09-30 15:54:38 -0700535 p.Wait()
536 merge_branch = p.stdout.strip()
537 return merge_branch
538
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700539 def Execute(self, opt, args):
540 project_list = self.GetProjects(args)
541 pending = []
Joe Onorato2896a792008-11-17 16:56:36 -0500542 reviewers = []
543 cc = []
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700544 branch = None
545
546 if opt.branch:
547 branch = opt.branch
Joe Onorato2896a792008-11-17 16:56:36 -0500548
Doug Anderson37282b42011-03-04 11:54:18 -0800549 for project in project_list:
Daniel Sandlere9d6b612012-04-06 10:39:32 -0400550 if opt.current_branch:
551 cbr = project.CurrentBranch
Warren Turkal011d4f42013-11-27 16:20:57 -0800552 up_branch = project.GetUploadableBranch(cbr)
553 if up_branch:
554 avail = [up_branch]
555 else:
556 avail = None
557 print('ERROR: Current branch (%s) not uploadable. '
558 'You may be able to type '
559 '"git branch --set-upstream-to m/master" to fix '
560 'your branch.' % str(cbr),
561 file=sys.stderr)
Daniel Sandlere9d6b612012-04-06 10:39:32 -0400562 else:
563 avail = project.GetUploadableBranches(branch)
Doug Anderson37282b42011-03-04 11:54:18 -0800564 if avail:
565 pending.append((project, avail))
566
Mike Frysinger163a3be2016-04-04 17:31:32 -0400567 if not pending:
Mike Frysinger7a753b82020-02-19 00:14:32 -0500568 if branch is None:
569 print('repo: error: no branches ready for upload', file=sys.stderr)
570 else:
571 print('repo: error: no branches named "%s" ready for upload' %
572 (branch,), file=sys.stderr)
573 return 1
Mike Frysinger163a3be2016-04-04 17:31:32 -0400574
575 if not opt.bypass_hooks:
Doug Anderson37282b42011-03-04 11:54:18 -0800576 hook = RepoHook('pre-upload', self.manifest.repo_hooks_project,
Mike Frysinger40252c22016-08-15 21:23:44 -0400577 self.manifest.topdir,
578 self.manifest.manifestProject.GetRemote('origin').url,
579 abort_if_user_denies=True)
David Pursehouse3bcd3052017-07-10 22:42:22 +0900580 pending_proj_names = [project.name for (project, available) in pending]
581 pending_worktrees = [project.worktree for (project, available) in pending]
Mike Frysinger21c15752020-02-11 05:17:16 -0500582 passed = True
Doug Anderson37282b42011-03-04 11:54:18 -0800583 try:
David James8d201162013-10-11 17:03:19 -0700584 hook.Run(opt.allow_all_hooks, project_list=pending_proj_names,
585 worktree_list=pending_worktrees)
Mike Frysinger21c15752020-02-11 05:17:16 -0500586 except SystemExit:
587 passed = False
588 if not opt.ignore_hooks:
589 raise
Sarah Owensa5be53f2012-09-09 15:37:57 -0700590 except HookError as e:
Mike Frysinger21c15752020-02-11 05:17:16 -0500591 passed = False
Sarah Owenscecd1d82012-11-01 22:59:27 -0700592 print("ERROR: %s" % str(e), file=sys.stderr)
Mike Frysinger21c15752020-02-11 05:17:16 -0500593
594 if not passed:
595 if opt.ignore_hooks:
596 print('\nWARNING: pre-upload hooks failed, but uploading anyways.',
597 file=sys.stderr)
598 else:
599 return
Doug Anderson37282b42011-03-04 11:54:18 -0800600
Joe Onorato2896a792008-11-17 16:56:36 -0500601 if opt.reviewers:
602 reviewers = _SplitEmails(opt.reviewers)
603 if opt.cc:
604 cc = _SplitEmails(opt.cc)
David Pursehouse8f62fb72012-11-14 12:09:38 +0900605 people = (reviewers, cc)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700606
Mike Frysinger163a3be2016-04-04 17:31:32 -0400607 if len(pending) == 1 and len(pending[0][1]) == 1:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700608 self._SingleBranch(opt, pending[0][1][0], people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700609 else:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700610 self._MultipleBranches(opt, pending, people)