Merge lp:~james-w/tarmac/break-up-do-merges into lp:~ubuntuone-hackers/tarmac/trunk

Proposed by James Westby
Status: Merged
Merged at revision: 420
Proposed branch: lp:~james-w/tarmac/break-up-do-merges
Merge into: lp:~ubuntuone-hackers/tarmac/trunk
Prerequisite: lp:~james-w/tarmac/commit-message
Diff against target: 453 lines (+206/-191)
1 file modified
tarmac/bin/commands.py (+206/-191)
To merge this branch: bzr merge lp:~james-w/tarmac/break-up-do-merges
Reviewer Review Type Date Requested Status
Jonathan Lange (community) Needs Fixing
Review via email: mp+140554@code.launchpad.net

This proposal supersedes a proposal from 2012-12-18.

Description of the change

Hi,

This branch breaks up the _do_merges method a little, as it was huge.

I tried to keep changes to a minimum while moving the code around, but it
used "continue" for flow control, so I had to change it a little to
use return values.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :
Download full text (8.1 KiB)

On 18 December 2012 21:26, James Westby <email address hidden> wrote:
> James Westby has proposed merging lp:~james-w/tarmac/break-up-do-merges into lp:~ubuntuone-hackers/tarmac/trunk with lp:~james-w/tarmac/commit-message as a prerequisite.
>
> Requested reviews:
> Ubuntu One hackers (ubuntuone-hackers)
>
> For more details, see:
> https://code.launchpad.net/~james-w/tarmac/break-up-do-merges/+merge/140554
>
> Hi,
>
> This branch breaks up the _do_merges method a little, as it was huge.
>

It was!

See inline.

> I tried to keep changes to a minimum while moving the code around, but it
> used "continue" for flow control, so I had to change it a little to
> use return values.
>

...
> === modified file 'tarmac/bin/commands.py'
> --- tarmac/bin/commands.py 2012-09-05 18:08:50 +0000
> +++ tarmac/bin/commands.py 2012-12-18 21:25:42 +0000
> @@ -134,6 +134,194 @@
> help_commands(self.outf)
>
>
> +def _get_mergable_proposals_for_branch(lp_branch, logger, imply_commit_message=False):
> + """Return a list of the mergable proposals for the given branch."""
> + proposals = []
> + for entry in lp_branch.landing_candidates:
> + logger.debug("Considering merge proposal: {0}".format(entry.web_link))
> +
> + if entry.queue_status != u'Approved':
> + logger.debug(
> + " Skipping proposal: status is {0}, not "
> + "'Approved'".format(entry.queue_status))
> + continue
> +
> + if (not imply_commit_message and not entry.commit_message):
> + logger.debug(
> + " Skipping proposal: proposal has no commit message")
> + continue
> +
> + proposals.append(entry)
> + return proposals
> +

This seems a natural candidate for an iterator. Still, no big deal.

> +def _get_reviews(proposal):
> + """Get the set of reviews from the proposal."""
> + reviews = []
> + for vote in proposal.votes:
> + if not vote.comment:
> + continue
> + else:
> + reviews.append('%s;%s' % (vote.reviewer.display_name,
> + vote.comment.vote))
> +
> + if len(reviews) == 0:
> + return None
> +
> + return reviews
> +

Again, a potential iterator, and again, I don't care enough for this branch.

Implementation could probably be more functional, but even this is a
huge step forwards.

> +
> +def setup(logger, debug=False, http_debug=False):

"setup" is a noun, "set up" a verb phrase. This should be spelled
"set_up". Also, it should probably say what it's going to set up.

> +def merge_proposals(target, proposals, logger, config, command):
> + logger.debug('Firing tarmac_pre_merge hook')
> + tarmac_hooks.fire('tarmac_pre_merge',
> + command, target)
> +
> + statuses = []
> + try:
> + for proposal in proposals:
> + status = merge_proposal(target, proposal, logger,
> + config, command)
> + statuses.append(status)
> + # This except is here because we need the else and can't have it
> + # without an except as well.

Wat.

try:
  thing()
except:
  raise
else:
  another_thing()
...

Read more...

Revision history for this message
Jonathan Lange (jml) :
review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'tarmac/bin/commands.py'
--- tarmac/bin/commands.py 2012-09-05 18:08:50 +0000
+++ tarmac/bin/commands.py 2012-12-18 21:25:42 +0000
@@ -134,6 +134,194 @@
134 help_commands(self.outf)134 help_commands(self.outf)
135135
136136
137def _get_mergable_proposals_for_branch(lp_branch, logger, imply_commit_message=False):
138 """Return a list of the mergable proposals for the given branch."""
139 proposals = []
140 for entry in lp_branch.landing_candidates:
141 logger.debug("Considering merge proposal: {0}".format(entry.web_link))
142
143 if entry.queue_status != u'Approved':
144 logger.debug(
145 " Skipping proposal: status is {0}, not "
146 "'Approved'".format(entry.queue_status))
147 continue
148
149 if (not imply_commit_message and not entry.commit_message):
150 logger.debug(
151 " Skipping proposal: proposal has no commit message")
152 continue
153
154 proposals.append(entry)
155 return proposals
156
157
158def _get_reviews(proposal):
159 """Get the set of reviews from the proposal."""
160 reviews = []
161 for vote in proposal.votes:
162 if not vote.comment:
163 continue
164 else:
165 reviews.append('%s;%s' % (vote.reviewer.display_name,
166 vote.comment.vote))
167
168 if len(reviews) == 0:
169 return None
170
171 return reviews
172
173
174def setup(logger, debug=False, http_debug=False):
175 if debug:
176 set_up_debug_logging()
177 logger.debug('Debug logging enabled')
178 if http_debug:
179 httplib2.debuglevel = 1
180 logger.debug('HTTP debugging enabled.')
181 logger.debug('Loading plugins')
182 load_plugins()
183 logger.debug('Plugins loaded')
184
185
186def merge_proposals(target, proposals, logger, config, command):
187 logger.debug('Firing tarmac_pre_merge hook')
188 tarmac_hooks.fire('tarmac_pre_merge',
189 command, target)
190
191 statuses = []
192 try:
193 for proposal in proposals:
194 status = merge_proposal(target, proposal, logger,
195 config, command)
196 statuses.append(status)
197 # This except is here because we need the else and can't have it
198 # without an except as well.
199 except:
200 raise
201 else:
202 logger.debug('Firing tarmac_post_merge hook')
203 tarmac_hooks.fire('tarmac_post_merge',
204 command, target)
205 finally:
206 target.cleanup()
207 return statuses
208
209
210def merge_proposal(target, proposal, logger, config, command):
211 target.cleanup()
212 logger.debug(
213 u'Preparing to merge %(source_branch)s' % {
214 'source_branch': proposal.source_branch.web_link})
215 try:
216 prerequisite = proposal.prerequisite_branch
217 if prerequisite:
218 merges = [x for x in prerequisite.landing_targets
219 if x.target_branch == target.lp_branch and
220 x.queue_status != u'Superseded']
221 if len(merges) == 0:
222 raise TarmacMergeError(
223 u'No proposals of prerequisite branch.',
224 u'No proposals found for merge of %s '
225 u'into %s.' % (
226 prerequisite.web_link,
227 target.lp_branch.web_link))
228 elif len(merges) > 1:
229 raise TarmacMergeError(
230 u'Too many proposals of prerequisite.',
231 u'More than one proposal found for merge '
232 u'of %s into %s, which is not Superseded.' % (
233 prerequisite.web_link,
234 target.lp_branch.web_link))
235 elif len(merges) == 1:
236 if merges[0].queue_status != u'Merged':
237 raise TarmacMergeError(
238 u'Prerequisite not yet merged.',
239 u'The prerequisite %s has not yet been '
240 u'merged into %s.' % (
241 prerequisite.web_link,
242 target.lp_branch.web_link))
243
244 if not proposal.reviewed_revid:
245 raise TarmacMergeError(
246 u'No approved revision specified.')
247
248
249 source = Branch.create(
250 proposal.source_branch, config, target=target)
251
252 source_bzr_branch = source.get_bzr_branch()
253 approved = source_bzr_branch.revision_id_to_revno(
254 str(proposal.reviewed_revid))
255 tip = source_bzr_branch.revno()
256
257 if tip > approved:
258 message = u'Unapproved changes made after approval'
259 lp_comment = (
260 u'There are additional revisions which have not '
261 u'been approved in review. Please seek review and '
262 u'approval of these new revisions.')
263 raise UnapprovedChanges(message, lp_comment)
264
265 logger.debug(
266 'Merging %(source)s at revision %(revision)s' % {
267 'source': proposal.source_branch.web_link,
268 'revision': proposal.reviewed_revid})
269
270 target.merge(source, str(proposal.reviewed_revid))
271
272 logger.debug('Firing tarmac_pre_commit hook')
273 tarmac_hooks.fire('tarmac_pre_commit',
274 command, target, source, proposal)
275
276 except TarmacMergeError, failure:
277 logger.warn(
278 u'Merging %(source)s into %(target)s failed: %(msg)s' %
279 {'source': proposal.source_branch.web_link,
280 'target': proposal.target_branch.web_link,
281 'msg': str(failure)})
282
283 subject = u'Re: [Merge] %(source)s into %(target)s' % {
284 "source": proposal.source_branch.display_name,
285 "target": proposal.target_branch.display_name}
286
287 if failure.comment:
288 comment = failure.comment
289 else:
290 comment = str(failure)
291
292 proposal.createComment(subject=subject, content=comment)
293 try:
294 proposal.setStatus(
295 status=config.rejected_branch_status)
296 except AttributeError:
297 proposal.setStatus(status=u'Needs review')
298 proposal.lp_save()
299 return False
300
301 except PointlessMerge:
302 logger.warn(
303 'Merging %(source)s into %(target)s would be '
304 'pointless.' % {
305 'source': proposal.source_branch.web_link,
306 'target': proposal.target_branch.web_link})
307 return False
308
309 merge_url = get_review_url(proposal)
310 revprops = {'merge_url': merge_url}
311
312 commit_message = proposal.commit_message
313 if commit_message is None and config.imply_commit_message:
314 commit_message = proposal.description
315
316 target.commit(commit_message,
317 reviews=_get_reviews(proposal))
318
319 logger.debug('Firing tarmac_post_commit hook')
320 tarmac_hooks.fire('tarmac_post_commit',
321 command, target, source, proposal)
322 return True
323
324
137class cmd_merge(TarmacCommand):325class cmd_merge(TarmacCommand):
138 '''Automatically merge approved merge proposal branches.'''326 '''Automatically merge approved merge proposal branches.'''
139327
@@ -143,7 +331,8 @@
143 options.http_debug_option,331 options.http_debug_option,
144 options.debug_option,332 options.debug_option,
145 options.imply_commit_message_option,333 options.imply_commit_message_option,
146 options.one_option]334 options.one_option,
335 ]
147336
148 def _do_merges(self, branch_url):337 def _do_merges(self, branch_url):
149338
@@ -152,204 +341,29 @@
152 self.logger.info('Not a valid branch: {0}'.format(branch_url))341 self.logger.info('Not a valid branch: {0}'.format(branch_url))
153 return342 return
154343
155 proposals = self._get_mergable_proposals_for_branch(lp_branch)344 proposals = _get_mergable_proposals_for_branch(lp_branch, self.logger,
345 imply_commit_message=self.config.imply_commit_message)
156346
157 if not proposals:347 if not proposals:
158 self.logger.info(348 self.logger.info(
159 'No approved proposals found for %(branch_url)s' % {349 'No approved proposals found for %(branch_url)s' % {
160 'branch_url': branch_url})350 'branch_url': branch_url})
161 return351 return []
352
353 if self.config.one:
354 proposals = proposals[0]
162355
163 target = Branch.create(lp_branch, self.config, create_tree=True)356 target = Branch.create(lp_branch, self.config, create_tree=True)
164357 statuses = merge_proposals(target, proposals, self.logger, self.config, self)
165 self.logger.debug('Firing tarmac_pre_merge hook')358 return statuses
166 tarmac_hooks.fire('tarmac_pre_merge',359
167 self, target)
168
169 try:
170 for proposal in proposals:
171 target.cleanup()
172 self.logger.debug(
173 u'Preparing to merge %(source_branch)s' % {
174 'source_branch': proposal.source_branch.web_link})
175 try:
176 prerequisite = proposal.prerequisite_branch
177 if prerequisite:
178 merges = [x for x in prerequisite.landing_targets
179 if x.target_branch == target.lp_branch and
180 x.queue_status != u'Superseded']
181 if len(merges) == 0:
182 raise TarmacMergeError(
183 u'No proposals of prerequisite branch.',
184 u'No proposals found for merge of %s '
185 u'into %s.' % (
186 prerequisite.web_link,
187 target.lp_branch.web_link))
188 elif len(merges) > 1:
189 raise TarmacMergeError(
190 u'Too many proposals of prerequisite.',
191 u'More than one proposal found for merge '
192 u'of %s into %s, which is not Superseded.' % (
193 prerequisite.web_link,
194 target.lp_branch.web_link))
195 elif len(merges) == 1:
196 if merges[0].queue_status != u'Merged':
197 raise TarmacMergeError(
198 u'Prerequisite not yet merged.',
199 u'The prerequisite %s has not yet been '
200 u'merged into %s.' % (
201 prerequisite.web_link,
202 target.lp_branch.web_link))
203
204 if not proposal.reviewed_revid:
205 raise TarmacMergeError(
206 u'No approved revision specified.')
207
208
209 source = Branch.create(
210 proposal.source_branch, self.config, target=target)
211
212 source_bzr_branch = source.get_bzr_branch()
213 approved = source_bzr_branch.revision_id_to_revno(
214 str(proposal.reviewed_revid))
215 tip = source_bzr_branch.revno()
216
217 if tip > approved:
218 message = u'Unapproved changes made after approval'
219 lp_comment = (
220 u'There are additional revisions which have not '
221 u'been approved in review. Please seek review and '
222 u'approval of these new revisions.')
223 raise UnapprovedChanges(message, lp_comment)
224
225 self.logger.debug(
226 'Merging %(source)s at revision %(revision)s' % {
227 'source': proposal.source_branch.web_link,
228 'revision': proposal.reviewed_revid})
229
230 target.merge(source, str(proposal.reviewed_revid))
231
232 self.logger.debug('Firing tarmac_pre_commit hook')
233 tarmac_hooks.fire('tarmac_pre_commit',
234 self, target, source, proposal)
235
236 except TarmacMergeError, failure:
237 self.logger.warn(
238 u'Merging %(source)s into %(target)s failed: %(msg)s' %
239 {'source': proposal.source_branch.web_link,
240 'target': proposal.target_branch.web_link,
241 'msg': str(failure)})
242
243 subject = u'Re: [Merge] %(source)s into %(target)s' % {
244 "source": proposal.source_branch.display_name,
245 "target": proposal.target_branch.display_name}
246
247 if failure.comment:
248 comment = failure.comment
249 else:
250 comment = str(failure)
251
252 proposal.createComment(subject=subject, content=comment)
253 try:
254 proposal.setStatus(
255 status=self.config.rejected_branch_status)
256 except AttributeError:
257 proposal.setStatus(status=u'Needs review')
258 proposal.lp_save()
259
260 # If we've been asked to only merge one branch, then exit.
261 if self.config.one:
262 return True
263
264 continue
265 except PointlessMerge:
266 self.logger.warn(
267 'Merging %(source)s into %(target)s would be '
268 'pointless.' % {
269 'source': proposal.source_branch.web_link,
270 'target': proposal.target_branch.web_link})
271 continue
272
273 merge_url = get_review_url(proposal)
274 revprops = {'merge_url': merge_url}
275
276 commit_message = proposal.commit_message
277 if commit_message is None and self.config.imply_commit_message:
278 commit_message = proposal.description
279
280 target.commit(commit_message,
281 revprops=revprops,
282 authors=source.authors,
283 reviews=self._get_reviews(proposal))
284
285 self.logger.debug('Firing tarmac_post_commit hook')
286 tarmac_hooks.fire('tarmac_post_commit',
287 self, target, source, proposal)
288
289 # If we've been asked to only merge one branch, then exit.
290 if self.config.one:
291 return True
292
293 # This except is here because we need the else and can't have it
294 # without an except as well.
295 except:
296 raise
297 else:
298 self.logger.debug('Firing tarmac_post_merge hook')
299 tarmac_hooks.fire('tarmac_post_merge',
300 self, target)
301 finally:
302 target.cleanup()
303
304 def _get_mergable_proposals_for_branch(self, lp_branch):
305 """Return a list of the mergable proposals for the given branch."""
306 proposals = []
307 for entry in lp_branch.landing_candidates:
308 self.logger.debug("Considering merge proposal: {0}".format(entry.web_link))
309
310 if entry.queue_status != u'Approved':
311 self.logger.debug(
312 " Skipping proposal: status is {0}, not "
313 "'Approved'".format(entry.queue_status))
314 continue
315
316 if (not self.config.imply_commit_message and
317 not entry.commit_message):
318 self.logger.debug(
319 " Skipping proposal: proposal has no commit message")
320 continue
321
322 proposals.append(entry)
323 return proposals
324
325 def _get_reviews(self, proposal):
326 """Get the set of reviews from the proposal."""
327 reviews = []
328 for vote in proposal.votes:
329 if not vote.comment:
330 continue
331 else:
332 reviews.append('%s;%s' % (vote.reviewer.display_name,
333 vote.comment.vote))
334
335 if len(reviews) == 0:
336 return None
337
338 return reviews
339360
340 def run(self, branch_url=None, launchpad=None, **kwargs):361 def run(self, branch_url=None, launchpad=None, **kwargs):
341 for key, value in kwargs.iteritems():362 for key, value in kwargs.iteritems():
342 self.config.set('Tarmac', key, value)363 self.config.set('Tarmac', key, value)
343364
344 if self.config.debug:365 setup(self.logger, debug=self.config.debug,
345 set_up_debug_logging()366 http_debug=self.config.http_debug)
346 self.logger.debug('Debug logging enabled')
347 if self.config.http_debug:
348 httplib2.debuglevel = 1
349 self.logger.debug('HTTP debugging enabled.')
350 self.logger.debug('Loading plugins')
351 load_plugins()
352 self.logger.debug('Plugins loaded')
353367
354 self.launchpad = launchpad368 self.launchpad = launchpad
355 if self.launchpad is None:369 if self.launchpad is None:
@@ -357,23 +371,24 @@
357 self.launchpad = self.get_launchpad_object()371 self.launchpad = self.get_launchpad_object()
358 self.logger.debug('launchpad object loaded')372 self.logger.debug('launchpad object loaded')
359373
374 statuses = []
375
360 if branch_url:376 if branch_url:
361 self.logger.debug('%(branch_url)s specified as branch_url' % {377 self.logger.debug('%(branch_url)s specified as branch_url' % {
362 'branch_url': branch_url})378 'branch_url': branch_url})
363 if not branch_url.startswith('lp:'):379 if not branch_url.startswith('lp:'):
364 raise TarmacCommandError('Branch urls must start with lp:')380 raise TarmacCommandError('Branch urls must start with lp:')
365 self._do_merges(branch_url)381 statuses.extend(self._do_merges(branch_url))
366
367 else:382 else:
368 for branch in self.config.branches:383 for branch in self.config.branches:
369 self.logger.debug(384 self.logger.debug(
370 'Merging approved branches against %(branch)s' % {385 'Merging approved branches against %(branch)s' % {
371 'branch': branch})386 'branch': branch})
372 try:387 try:
373 merged = self._do_merges(branch)388 statuses.extend(self._do_merges(branch))
374389
375 # If we've been asked to only merge one branch, then exit.390 # If we've been asked to only merge one branch, then exit.
376 if merged and self.config.one:391 if statuses and self.config.one:
377 break392 break
378 except LockContention:393 except LockContention:
379 continue394 continue

Subscribers

People subscribed via source and target branches