Merge lp:~canonical-ca-hackers/tarmac/jenkins into lp:tarmac

Proposed by Jonathan Lange
Status: Rejected
Rejected by: dobey
Proposed branch: lp:~canonical-ca-hackers/tarmac/jenkins
Merge into: lp:tarmac
Diff against target: 1004 lines (+536/-134)
9 files modified
bin/tarmac (+3/-1)
check.py (+89/-0)
tarmac/bin/__init__.py (+1/-1)
tarmac/bin/commands.py (+333/-81)
tarmac/bin/options.py (+6/-0)
tarmac/bin/registry.py (+2/-2)
tarmac/branch.py (+74/-46)
tarmac/plugins/command.py (+4/-3)
tarmac/plugins/commit.py (+24/-0)
To merge this branch: bzr merge lp:~canonical-ca-hackers/tarmac/jenkins
Reviewer Review Type Date Requested Status
Paul Hummer Pending
Review via email: mp+140437@code.launchpad.net

Description of the change

WIP MP to make the delta visible

To post a comment you must log in.

Unmerged revisions

410. By James Westby

Merge the check.py file.

409. By James Westby

Merge sidnei's fix for the timeout thing.

408. By James Westby

Fix typo.

407. By James Westby

Fix missing method.

406. By James Westby

Remove debugging.

405. By James Westby

Fix typo.

404. By James Westby

Add the commit plugin that sets [r=reviewer].

403. By James Westby

Ignore decode errors when decoding streams from executed commands.

402. By James Westby

Add commands for better jenkins integration.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/tarmac'
2--- bin/tarmac 2010-06-17 17:23:37 +0000
3+++ bin/tarmac 2012-12-18 13:35:32 +0000
4@@ -2,5 +2,7 @@
5 # vim:filetype=python
6 '''Main tarmac script.'''
7
8+import sys
9+
10 from tarmac.bin import main
11-main()
12+sys.exit(main())
13
14=== added file 'check.py'
15--- check.py 1970-01-01 00:00:00 +0000
16+++ check.py 2012-12-18 13:35:32 +0000
17@@ -0,0 +1,89 @@
18+#!/usr/bin/env python
19+import httplib2
20+import sys
21+
22+from tarmac.bin.commands import TarmacCommand
23+from tarmac.bin.registry import CommandRegistry
24+from tarmac.exceptions import TarmacCommandError
25+from tarmac.log import set_up_debug_logging
26+from tarmac.plugin import load_plugins
27+
28+
29+registry = CommandRegistry()
30+
31+
32+class cmd_check(TarmacCommand):
33+
34+ takes_args = ['branch_url?']
35+
36+ def _check_merges(self, branch_url):
37+
38+ lp_branch = self.launchpad.branches.getByUrl(url=branch_url)
39+ if lp_branch is None:
40+ self.logger.info('Not a valid branch: {0}'.format(branch_url))
41+ return
42+
43+ proposals = self._get_mergable_proposals_for_branch(lp_branch)
44+
45+ if not proposals:
46+ self.logger.info(
47+ 'No approved proposals found for %(branch_url)s' % {
48+ 'branch_url': branch_url})
49+ return 1
50+ return 0
51+
52+ def _get_mergable_proposals_for_branch(self, lp_branch):
53+ """Return a list of the mergable proposals for the given branch."""
54+ proposals = []
55+ for entry in lp_branch.landing_candidates:
56+ self.logger.debug("Considering merge proposal: {0}".format(entry))
57+
58+ if entry.queue_status != u'Approved':
59+ self.logger.debug(
60+ " Skipping proposal: status is {0}, not "
61+ "'Approved'".format(entry.queue_status))
62+ continue
63+
64+ if (not entry.commit_message):
65+ self.logger.debug(
66+ " Skipping proposal: proposal has no commit message")
67+ continue
68+
69+ proposals.append(entry)
70+ return proposals
71+
72+ def run(self, branch_url=None, launchpad=None, **kwargs):
73+ for key, value in kwargs.iteritems():
74+ self.config.set('Tarmac', key, value)
75+
76+ set_up_debug_logging()
77+
78+ self.launchpad = launchpad
79+ if self.launchpad is None:
80+ self.logger.debug('Loading launchpad object')
81+ self.launchpad = self.get_launchpad_object()
82+ self.logger.debug('launchpad object loaded')
83+
84+ if branch_url:
85+ self.logger.debug(
86+ '%(branch_url)s specified as branch_url' % {
87+ 'branch_url': branch_url})
88+ if not branch_url.startswith('lp:'):
89+ raise TarmacCommandError('Branch urls must start with lp:')
90+ return self._check_merges(branch_url)
91+ else:
92+ for branch in self.config.branches:
93+ self.logger.debug(
94+ 'Merging approved branches against %(branch)s' % {
95+ 'branch': branch})
96+ try:
97+ return self._check_merges(branch)
98+ except Exception, error:
99+ self.logger.error(
100+ 'An error occurred trying to merge %s: %s',
101+ branch, error)
102+ raise
103+
104+
105+cmd = cmd_check(registry)
106+sys.exit(cmd.run(sys.argv[1]))
107
108=== modified file 'tarmac/bin/__init__.py'
109--- tarmac/bin/__init__.py 2010-09-02 15:18:07 +0000
110+++ tarmac/bin/__init__.py 2012-12-18 13:35:32 +0000
111@@ -27,4 +27,4 @@
112 args = sys.argv[1:]
113 if not args:
114 args = ['help']
115- registry.run(args)
116+ return registry.run(args)
117
118=== modified file 'tarmac/bin/commands.py'
119--- tarmac/bin/commands.py 2012-06-29 20:03:55 +0000
120+++ tarmac/bin/commands.py 2012-12-18 13:35:32 +0000
121@@ -5,7 +5,7 @@
122 import re
123
124 from bzrlib.commands import Command
125-from bzrlib.errors import PointlessMerge
126+from bzrlib.errors import PointlessMerge, LockContention
127 from bzrlib.help import help_commands
128 from launchpadlib.launchpad import Launchpad
129 from launchpadlib.uris import (LPNET_SERVICE_ROOT,
130@@ -134,6 +134,55 @@
131 help_commands(self.outf)
132
133
134+def _get_mergable_proposals_for_branch(lp_branch, logger, imply_commit_message=False):
135+ """Return a list of the mergable proposals for the given branch."""
136+ proposals = []
137+ for entry in lp_branch.landing_candidates:
138+ logger.debug("Considering merge proposal: {0}".format(entry.web_link))
139+
140+ if entry.queue_status != u'Approved':
141+ logger.debug(
142+ " Skipping proposal: status is {0}, not "
143+ "'Approved'".format(entry.queue_status))
144+ continue
145+
146+ if (not imply_commit_message and not entry.commit_message):
147+ logger.debug(
148+ " Skipping proposal: proposal has no commit message")
149+ continue
150+
151+ proposals.append(entry)
152+ return proposals
153+
154+
155+def _get_reviews(proposal):
156+ """Get the set of reviews from the proposal."""
157+ reviews = []
158+ for vote in proposal.votes:
159+ if not vote.comment:
160+ continue
161+ else:
162+ reviews.append('%s;%s' % (vote.reviewer.display_name,
163+ vote.comment.vote))
164+
165+ if len(reviews) == 0:
166+ return None
167+
168+ return reviews
169+
170+
171+def setup(logger, debug=False, http_debug=False):
172+ if debug:
173+ set_up_debug_logging()
174+ logger.debug('Debug logging enabled')
175+ if http_debug:
176+ httplib2.debuglevel = 1
177+ logger.debug('HTTP debugging enabled.')
178+ logger.debug('Loading plugins')
179+ load_plugins()
180+ logger.debug('Plugins loaded')
181+
182+
183 class cmd_merge(TarmacCommand):
184 '''Automatically merge approved merge proposal branches.'''
185
186@@ -142,7 +191,8 @@
187 takes_options = [
188 options.http_debug_option,
189 options.debug_option,
190- options.imply_commit_message_option]
191+ options.imply_commit_message_option,
192+ options.one_option]
193
194 def _do_merges(self, branch_url):
195
196@@ -151,7 +201,8 @@
197 self.logger.info('Not a valid branch: {0}'.format(branch_url))
198 return
199
200- proposals = self._get_mergable_proposals_for_branch(lp_branch)
201+ proposals = _get_mergable_proposals_for_branch(lp_branch, self.logger,
202+ imply_commit_message=self.config.imply_commit_message)
203
204 if not proposals:
205 self.logger.info(
206@@ -167,13 +218,10 @@
207
208 try:
209 for proposal in proposals:
210-
211+ target.cleanup()
212 self.logger.debug(
213 u'Preparing to merge %(source_branch)s' % {
214- 'source_branch': proposal.source_branch.bzr_identity})
215- source = Branch.create(
216- proposal.source_branch, self.config, target=target)
217-
218+ 'source_branch': proposal.source_branch.web_link})
219 try:
220 prerequisite = proposal.prerequisite_branch
221 if prerequisite:
222@@ -185,31 +233,37 @@
223 u'No proposals of prerequisite branch.',
224 u'No proposals found for merge of %s '
225 u'into %s.' % (
226- prerequisite.display_name,
227- target.lp_branch.display_name))
228+ prerequisite.web_link,
229+ target.lp_branch.web_link))
230 elif len(merges) > 1:
231 raise TarmacMergeError(
232 u'Too many proposals of prerequisite.',
233 u'More than one proposal found for merge '
234 u'of %s into %s, which is not Superseded.' % (
235- prerequisite.display_name,
236- target.lp_branch.display_name))
237+ prerequisite.web_link,
238+ target.lp_branch.web_link))
239 elif len(merges) == 1:
240 if merges[0].queue_status != u'Merged':
241 raise TarmacMergeError(
242- u'Prerequsiite not yet merged.',
243+ u'Prerequisite not yet merged.',
244 u'The prerequisite %s has not yet been '
245 u'merged into %s.' % (
246- prerequisite.display_name,
247- target.lp_branch.display_name))
248+ prerequisite.web_link,
249+ target.lp_branch.web_link))
250
251 if not proposal.reviewed_revid:
252 raise TarmacMergeError(
253 u'No approved revision specified.')
254
255- approved = source.bzr_branch.revision_id_to_revno(
256+
257+ source = Branch.create(
258+ proposal.source_branch, self.config, target=target)
259+
260+ source_bzr_branch = source.get_bzr_branch()
261+ approved = source_bzr_branch.revision_id_to_revno(
262 str(proposal.reviewed_revid))
263- tip = source.bzr_branch.revno()
264+ tip = source_bzr_branch.revno()
265+
266 if tip > approved:
267 message = u'Unapproved changes made after approval'
268 lp_comment = (
269@@ -220,12 +274,10 @@
270
271 self.logger.debug(
272 'Merging %(source)s at revision %(revision)s' % {
273- 'source': proposal.source_branch.display_name,
274+ 'source': proposal.source_branch.web_link,
275 'revision': proposal.reviewed_revid})
276
277- target.merge(
278- source,
279- str(proposal.reviewed_revid))
280+ target.merge(source, str(proposal.reviewed_revid))
281
282 self.logger.debug('Firing tarmac_pre_commit hook')
283 tarmac_hooks.fire('tarmac_pre_commit',
284@@ -234,8 +286,8 @@
285 except TarmacMergeError, failure:
286 self.logger.warn(
287 u'Merging %(source)s into %(target)s failed: %(msg)s' %
288- {'source': proposal.source_branch.display_name,
289- 'target': proposal.target_branch.display_name,
290+ {'source': proposal.source_branch.web_link,
291+ 'target': proposal.target_branch.web_link,
292 'msg': str(failure)})
293
294 subject = u'Re: [Merge] %(source)s into %(target)s' % {
295@@ -247,23 +299,25 @@
296 else:
297 comment = str(failure)
298
299- proposal.createComment(subject=subject,
300- content=comment)
301+ proposal.createComment(subject=subject, content=comment)
302 try:
303 proposal.setStatus(
304 status=self.config.rejected_branch_status)
305 except AttributeError:
306 proposal.setStatus(status=u'Needs review')
307 proposal.lp_save()
308- target.cleanup()
309+
310+ # If we've been asked to only merge one branch, then exit.
311+ if self.config.one:
312+ return True
313+
314 continue
315 except PointlessMerge:
316 self.logger.warn(
317 'Merging %(source)s into %(target)s would be '
318 'pointless.' % {
319- 'source': proposal.source_branch.display_name,
320- 'target': proposal.target_branch.display_name})
321- target.cleanup()
322+ 'source': proposal.source_branch.web_link,
323+ 'target': proposal.target_branch.web_link})
324 continue
325
326 merge_url = get_review_url(proposal)
327@@ -272,16 +326,17 @@
328 commit_message = proposal.commit_message
329 if commit_message is None and self.config.imply_commit_message:
330 commit_message = proposal.description
331+
332 target.commit(commit_message,
333- revprops=revprops,
334- authors=source.authors,
335- reviews=self._get_reviews(proposal))
336+ reviews=_get_reviews(proposal))
337
338 self.logger.debug('Firing tarmac_post_commit hook')
339 tarmac_hooks.fire('tarmac_post_commit',
340 self, target, source, proposal)
341
342- target.cleanup()
343+ # If we've been asked to only merge one branch, then exit.
344+ if self.config.one:
345+ return True
346
347 # This except is here because we need the else and can't have it
348 # without an except as well.
349@@ -294,55 +349,13 @@
350 finally:
351 target.cleanup()
352
353- def _get_mergable_proposals_for_branch(self, lp_branch):
354- """Return a list of the mergable proposals for the given branch."""
355- proposals = []
356- for entry in lp_branch.landing_candidates:
357- self.logger.debug("Considering merge proposal: {0}".format(entry))
358-
359- if entry.queue_status != u'Approved':
360- self.logger.debug(
361- " Skipping proposal: status is {0}, not "
362- "'Approved'".format(entry.queue_status))
363- continue
364-
365- if (not self.config.imply_commit_message and
366- not entry.commit_message):
367- self.logger.debug(
368- " Skipping proposal: proposal has no commit message")
369- continue
370-
371- proposals.append(entry)
372- return proposals
373-
374- def _get_reviews(self, proposal):
375- """Get the set of reviews from the proposal."""
376- reviews = []
377- for vote in proposal.votes:
378- if not vote.comment:
379- continue
380- else:
381- reviews.append('%s;%s' % (vote.reviewer.display_name,
382- vote.comment.vote))
383-
384- if len(reviews) == 0:
385- return None
386-
387- return reviews
388
389 def run(self, branch_url=None, launchpad=None, **kwargs):
390 for key, value in kwargs.iteritems():
391 self.config.set('Tarmac', key, value)
392
393- if self.config.debug:
394- set_up_debug_logging()
395- self.logger.debug('Debug logging enabled')
396- if self.config.http_debug:
397- httplib2.debuglevel = 1
398- self.logger.debug('HTTP debugging enabled.')
399- self.logger.debug('Loading plugins')
400- load_plugins()
401- self.logger.debug('Plugins loaded')
402+ setup(self.logger, debug=self.config.debug,
403+ http_debug=self.config.http_debug)
404
405 self.launchpad = launchpad
406 if self.launchpad is None:
407@@ -351,9 +364,8 @@
408 self.logger.debug('launchpad object loaded')
409
410 if branch_url:
411- self.logger.debug(
412- '%(branch_url)s specified as branch_url' % {
413- 'branch_url': branch_url})
414+ self.logger.debug('%(branch_url)s specified as branch_url' % {
415+ 'branch_url': branch_url})
416 if not branch_url.startswith('lp:'):
417 raise TarmacCommandError('Branch urls must start with lp:')
418 self._do_merges(branch_url)
419@@ -364,9 +376,249 @@
420 'Merging approved branches against %(branch)s' % {
421 'branch': branch})
422 try:
423- self._do_merges(branch)
424+ merged = self._do_merges(branch)
425+
426+ # If we've been asked to only merge one branch, then exit.
427+ if merged and self.config.one:
428+ break
429+ except LockContention:
430+ continue
431 except Exception, error:
432 self.logger.error(
433 'An error occurred trying to merge %s: %s',
434 branch, error)
435 raise
436+
437+
438+class cmd_check(TarmacCommand):
439+ '''Check whether there are any merge proposals ready to land.'''
440+
441+ takes_args = ['branch_url']
442+ takes_options = [
443+ options.http_debug_option,
444+ options.debug_option]
445+
446+ def _any_merges(self, branch_url):
447+
448+ lp_branch = self.launchpad.branches.getByUrl(url=branch_url)
449+ if lp_branch is None:
450+ self.logger.info('Not a valid branch: {0}'.format(branch_url))
451+ return
452+
453+ proposals = _get_mergable_proposals_for_branch(lp_branch, self.logger)
454+
455+ if not proposals:
456+ self.logger.info(
457+ 'No approved proposals found for %(branch_url)s' % {
458+ 'branch_url': branch_url})
459+ return False
460+ return True
461+
462+ def run(self, branch_url, launchpad=None, **kwargs):
463+ for key, value in kwargs.iteritems():
464+ self.config.set('Tarmac', key, value)
465+
466+ setup(self.logger, debug=self.config.debug,
467+ http_debug=self.config.http_debug)
468+
469+ self.launchpad = launchpad
470+ if self.launchpad is None:
471+ self.logger.debug('Loading launchpad object')
472+ self.launchpad = self.get_launchpad_object()
473+ self.logger.debug('launchpad object loaded')
474+
475+ self.logger.debug('%(branch_url)s specified as branch_url' % {
476+ 'branch_url': branch_url})
477+ if not branch_url.startswith('lp:'):
478+ raise TarmacCommandError('Branch urls must start with lp:')
479+ ret = self._any_merges(branch_url)
480+ if ret:
481+ return 0
482+ return 1
483+
484+
485+class cmd_merge_one(TarmacCommand):
486+ '''Try and merge one branch.
487+
488+ Returns 0 if a branch was merged.
489+ Return 1 if there were no branches to merge.
490+ Returns 2 if the branch failed to merge.
491+ '''
492+
493+ takes_args = ['branch_url']
494+ takes_options = [
495+ options.http_debug_option,
496+ options.debug_option,
497+ options.verify_command_option]
498+
499+ def _pick_merge(self, lp_branch, branch_url):
500+ proposals = _get_mergable_proposals_for_branch(lp_branch, self.logger)
501+
502+ if not proposals:
503+ self.logger.info(
504+ 'No approved proposals found for %(branch_url)s' % {
505+ 'branch_url': branch_url})
506+ return None
507+ return proposals[0]
508+
509+ def _do_merge(self, lp_branch, proposal, verify_command=None):
510+ target = Branch.create(lp_branch, self.config, create_tree=True)
511+
512+ if verify_command is not None:
513+ setattr(target.config, 'verify_command', verify_command)
514+
515+ self.logger.debug('Firing tarmac_pre_merge hook')
516+ tarmac_hooks['tarmac_pre_merge'].fire(self, target)
517+
518+ try:
519+ target.cleanup()
520+ self.logger.debug(
521+ u'Preparing to merge %(source_branch)s' % {
522+ 'source_branch': proposal.source_branch.web_link})
523+ try:
524+ prerequisite = proposal.prerequisite_branch
525+ if prerequisite:
526+ merges = [x for x in prerequisite.landing_targets
527+ if x.target_branch == target.lp_branch and
528+ x.queue_status != u'Superseded']
529+ if len(merges) == 0:
530+ raise TarmacMergeError(
531+ u'No proposals of prerequisite branch.',
532+ u'No proposals found for merge of %s '
533+ u'into %s.' % (
534+ prerequisite.web_link,
535+ target.lp_branch.web_link))
536+ elif len(merges) > 1:
537+ raise TarmacMergeError(
538+ u'Too many proposals of prerequisite.',
539+ u'More than one proposal found for merge '
540+ u'of %s into %s, which is not Superseded.' % (
541+ prerequisite.web_link,
542+ target.lp_branch.web_link))
543+ elif len(merges) == 1:
544+ if merges[0].queue_status != u'Merged':
545+ raise TarmacMergeError(
546+ u'Prerequisite not yet merged.',
547+ u'The prerequisite %s has not yet been '
548+ u'merged into %s.' % (
549+ prerequisite.web_link,
550+ target.lp_branch.web_link))
551+
552+ if not proposal.reviewed_revid:
553+ raise TarmacMergeError(
554+ u'No approved revision specified.')
555+
556+
557+ source = Branch.create(
558+ proposal.source_branch, self.config, target=target)
559+
560+ approved = source.bzr_branch.revision_id_to_revno(
561+ str(proposal.reviewed_revid))
562+ tip = source.bzr_branch.revno()
563+
564+ if tip > approved:
565+ message = u'Unapproved changes made after approval'
566+ lp_comment = (
567+ u'There are additional revisions which have not '
568+ u'been approved in review. Please seek review and '
569+ u'approval of these new revisions.')
570+ raise UnapprovedChanges(message, lp_comment)
571+
572+ self.logger.debug(
573+ 'Merging %(source)s at revision %(revision)s' % {
574+ 'source': proposal.source_branch.web_link,
575+ 'revision': proposal.reviewed_revid})
576+
577+ target.merge(source, str(proposal.reviewed_revid))
578+
579+ self.logger.debug('Firing tarmac_pre_commit hook')
580+ tarmac_hooks['tarmac_pre_commit'].fire(
581+ self, target, source, proposal)
582+
583+ except TarmacMergeError, failure:
584+ self.logger.warn(
585+ u'Merging %(source)s into %(target)s failed: %(msg)s' %
586+ {'source': proposal.source_branch.web_link,
587+ 'target': proposal.target_branch.web_link,
588+ 'msg': str(failure)})
589+
590+ subject = u'Re: [Merge] %(source)s into %(target)s' % {
591+ "source": proposal.source_branch.display_name,
592+ "target": proposal.target_branch.display_name}
593+
594+ if failure.comment:
595+ comment = failure.comment
596+ else:
597+ comment = str(failure)
598+
599+ proposal.createComment(subject=subject, content=comment)
600+ try:
601+ proposal.setStatus(
602+ status=self.config.rejected_branch_status)
603+ except AttributeError:
604+ proposal.setStatus(status=u'Needs review')
605+ proposal.lp_save()
606+
607+ return False
608+ except PointlessMerge:
609+ self.logger.warn(
610+ 'Merging %(source)s into %(target)s would be '
611+ 'pointless.' % {
612+ 'source': proposal.source_branch.web_link,
613+ 'target': proposal.target_branch.web_link})
614+ return False
615+
616+ merge_url = get_review_url(proposal)
617+ revprops = {'merge_url': merge_url}
618+
619+ commit_message = proposal.commit_message
620+ if commit_message is None and self.config.imply_commit_message:
621+ commit_message = proposal.description
622+ target.commit(commit_message,
623+ revprops=revprops,
624+ authors=source.authors,
625+ reviews=_get_reviews(proposal))
626+
627+ self.logger.debug('Firing tarmac_post_commit hook')
628+ tarmac_hooks['tarmac_post_commit'].fire(
629+ self, target, source, proposal)
630+ return True
631+
632+ # This except is here because we need the else and can't have it
633+ # without an except as well.
634+ except:
635+ raise
636+ else:
637+ self.logger.debug('Firing tarmac_post_merge hook')
638+ tarmac_hooks['tarmac_post_merge'].fire(self, target)
639+ finally:
640+ target.cleanup()
641+
642+ def run(self, branch_url, launchpad=None, verify_command=None, **kwargs):
643+ for key, value in kwargs.iteritems():
644+ self.config.set('Tarmac', key, value)
645+
646+ setup(self.logger, debug=self.config.debug,
647+ http_debug=self.config.http_debug)
648+
649+ self.launchpad = launchpad
650+ if self.launchpad is None:
651+ self.logger.debug('Loading launchpad object')
652+ self.launchpad = self.get_launchpad_object()
653+ self.logger.debug('launchpad object loaded')
654+
655+ self.logger.debug('%(branch_url)s specified as branch_url' % {
656+ 'branch_url': branch_url})
657+ if not branch_url.startswith('lp:'):
658+ raise TarmacCommandError('Branch urls must start with lp:')
659+ lp_branch = self.launchpad.branches.getByUrl(url=branch_url)
660+ if lp_branch is None:
661+ self.logger.info('Not a valid branch: {0}'.format(branch_url))
662+ return 3
663+ proposal = self._pick_merge(lp_branch, branch_url)
664+ if proposal is None:
665+ return 1
666+ successful = self._do_merge(lp_branch, proposal, verify_command=verify_command)
667+ if successful:
668+ return 0
669+ return 2
670
671=== modified file 'tarmac/bin/options.py'
672--- tarmac/bin/options.py 2011-01-06 20:20:19 +0000
673+++ tarmac/bin/options.py 2012-12-18 13:35:32 +0000
674@@ -15,3 +15,9 @@
675 'imply-commit-message',
676 help=("Use the description as a commit message if the branch "
677 "doesn't have a message"))
678+one_option = Option(
679+ 'one', short_name='1',
680+ help='Merge only one branch and exit.')
681+verify_command_option = Option(
682+ 'verify-command', type=str,
683+ help='The verify command to run.')
684
685=== modified file 'tarmac/bin/registry.py'
686--- tarmac/bin/registry.py 2010-10-25 20:20:18 +0000
687+++ tarmac/bin/registry.py 2012-12-18 13:35:32 +0000
688@@ -45,7 +45,7 @@
689
690 def _run(self, args):
691 '''Execute the command.'''
692- run_bzr(args)
693+ return run_bzr(args)
694
695 def install_hooks(self):
696 '''Use the bzrlib Command support for running commands.'''
697@@ -57,7 +57,7 @@
698 def run(self, args):
699 '''Execute the command.'''
700 try:
701- self._run(args)
702+ return self._run(args)
703 except BzrCommandError, e:
704 sys.exit('tarmac: ERROR: ' + str(e))
705
706
707=== modified file 'tarmac/branch.py'
708--- tarmac/branch.py 2012-06-29 20:03:55 +0000
709+++ tarmac/branch.py 2012-12-18 13:35:32 +0000
710@@ -37,7 +37,6 @@
711
712 def __init__(self, lp_branch, config=False, target=None):
713 self.lp_branch = lp_branch
714- self.bzr_branch = bzr_branch.Branch.open(self.lp_branch.bzr_identity)
715 if config:
716 self.config = BranchConfig(lp_branch.bzr_identity, config)
717 else:
718@@ -45,15 +44,27 @@
719
720 self.target = target
721 self.logger = logging.getLogger('tarmac')
722+ self.temp_tree_dir = None
723+
724+ def get_bzr_branch(self):
725+ return bzr_branch.Branch.open(self.lp_branch.bzr_identity)
726+
727+ # For backwards compatibility
728+ bzr_branch = property(get_bzr_branch)
729+
730+ def get_tree(self):
731+ if self.temp_tree_dir is not None:
732+ return WorkingTree.open(self.temp_tree_dir)
733+ if os.path.exists(self.config.tree_dir):
734+ return WorkingTree.open(self.config.tree_dir)
735+
736+ # For backwards compatibility
737+ tree = property(get_tree)
738
739 def __del__(self):
740 """Do some potenetially necessary cleanup during deletion."""
741- try:
742- # If we were using a temp directory, then remove it
743+ if self.temp_tree_dir is not None:
744 shutil.rmtree(self.temp_tree_dir)
745- except AttributeError:
746- # Not using a tempdir
747- pass
748
749 @classmethod
750 def create(cls, lp_branch, config, create_tree=False, target=None):
751@@ -62,50 +73,59 @@
752 clazz.create_tree()
753 return clazz
754
755+
756 def create_tree(self):
757 '''Create the dir and working tree.'''
758+ bzr_branch = self.get_bzr_branch()
759 try:
760- self.logger.debug(
761- 'Using tree in %(tree_dir)s' % {
762- 'tree_dir': self.config.tree_dir})
763- if os.path.exists(self.config.tree_dir):
764- self.tree = WorkingTree.open(self.config.tree_dir)
765- else:
766+ tree = self.get_tree()
767+ if tree is None:
768 self.logger.debug('Tree does not exist. Creating dir')
769 # Create the path up to but not including tree_dir if it does
770 # not exist.
771 parent_dir = os.path.dirname(self.config.tree_dir)
772 if not os.path.exists(parent_dir):
773 os.makedirs(parent_dir)
774- self.tree = self.bzr_branch.create_checkout(
775- self.config.tree_dir, lightweight=True)
776+ tree = bzr_branch.create_checkout(
777+ self.config.tree_dir, lightweight=False)
778+ self.logger.debug(
779+ 'Using tree in %(tree_dir)s' % {
780+ 'tree_dir': self.config.tree_dir})
781 except AttributeError:
782 # Store this so we can rmtree later
783 self.temp_tree_dir = tempfile.mkdtemp()
784 self.logger.debug(
785 'Using temp dir at %(tree_dir)s' % {
786 'tree_dir': self.temp_tree_dir})
787- self.tree = self.bzr_branch.create_checkout(self.temp_tree_dir)
788+ tree = bzr_branch.create_checkout(self.temp_tree_dir)
789
790 self.cleanup()
791
792 def cleanup(self):
793 '''Remove the working tree from the temp dir.'''
794- assert self.tree
795- self.tree.revert()
796- for filename in [self.tree.abspath(f) for f in self.unmanaged_files]:
797+ tree = self.get_tree()
798+ assert tree
799+ self.logger.info("Running cleanup in %s." % (
800+ self.lp_branch.bzr_identity))
801+ tree.revert()
802+ self.logger.info("Reverted changes in %s." % (
803+ self.lp_branch.bzr_identity))
804+ for filename in [tree.abspath(f) for f in self.unmanaged_files]:
805 if os.path.isdir(filename) and not os.path.islink(filename):
806 shutil.rmtree(filename)
807 else:
808 os.remove(filename)
809
810- self.tree.update()
811+ self.logger.info("Successfully removed extra files from %s." % (
812+ self.lp_branch.bzr_identity))
813+ tree.update()
814
815 def merge(self, branch, revid=None):
816 '''Merge from another tarmac.branch.Branch instance.'''
817- assert self.tree
818- conflict_list = self.tree.merge_from_branch(
819- branch.bzr_branch, to_revision=revid)
820+ tree = self.get_tree()
821+ assert tree
822+ conflict_list = tree.merge_from_branch(
823+ branch.get_bzr_branch(), to_revision=revid)
824 if conflict_list:
825 message = u'Conflicts merging branch.'
826 lp_comment = (
827@@ -118,24 +138,29 @@
828 @property
829 def unmanaged_files(self):
830 """Get the list of ignored and unknown files in the tree."""
831- self.tree.lock_read()
832- unmanaged = [x for x in self.tree.unknowns()]
833- unmanaged.extend([x[0] for x in self.tree.ignored_files()])
834- self.tree.unlock()
835+ tree = self.get_tree()
836+ assert tree
837+ tree.lock_read()
838+ unmanaged = [x for x in tree.unknowns()]
839+ unmanaged.extend([x[0] for x in tree.ignored_files()])
840+ tree.unlock()
841 return unmanaged
842
843 @property
844 def conflicts(self):
845 '''Print the conflicts.'''
846- assert self.tree.conflicts()
847+ tree = self.get_tree()
848+ assert tree
849 conflicts = []
850- for conflict in self.tree.conflicts():
851+ for conflict in tree.conflicts():
852 conflicts.append(
853 u'%s in %s' % (conflict.typestring, conflict.path))
854 return '\n'.join(conflicts)
855
856 def commit(self, commit_message, revprops=None, **kwargs):
857 '''Commit changes.'''
858+ tree = self.get_tree()
859+ assert tree
860 if not revprops:
861 revprops = {}
862
863@@ -152,8 +177,8 @@
864 'review identity or vote.')
865 revprops['reviews'] = '\n'.join(reviews)
866
867- self.tree.commit(commit_message, committer='Tarmac',
868- revprops=revprops, authors=authors)
869+ tree.commit(commit_message, committer='Tarmac',
870+ revprops=revprops, authors=authors)
871
872 @property
873 def landing_candidates(self):
874@@ -164,18 +189,20 @@
875 def authors(self):
876 author_list = []
877
878+ bzr_branch = self.get_bzr_branch()
879 if self.target:
880- self.bzr_branch.lock_read()
881- self.target.bzr_branch.lock_read()
882+ target_bzr_branch = self.target.get_bzr_branch()
883+ bzr_branch.lock_read()
884+ target_bzr_branch.lock_read()
885
886- graph = self.bzr_branch.repository.get_graph(
887- self.target.bzr_branch.repository)
888+ graph = bzr_branch.repository.get_graph(
889+ target_bzr_branch.repository)
890
891 unique_ids = graph.find_unique_ancestors(
892- self.bzr_branch.last_revision(),
893- [self.target.bzr_branch.last_revision()])
894+ bzr_branch.last_revision(),
895+ [target_bzr_branch.last_revision()])
896
897- revs = self.bzr_branch.repository.get_revisions(unique_ids)
898+ revs = bzr_branch.repository.get_revisions(unique_ids)
899 for rev in revs:
900 apparent_authors = rev.get_apparent_authors()
901 for author in apparent_authors:
902@@ -183,12 +210,12 @@
903 if author not in author_list:
904 author_list.append(author)
905
906- self.target.bzr_branch.unlock()
907- self.bzr_branch.unlock()
908+ target_bzr_branch.unlock()
909+ bzr_branch.unlock()
910 else:
911- last_rev = self.bzr_branch.last_revision()
912+ last_rev = bzr_branch.last_revision()
913 if last_rev != 'null:':
914- rev = self.bzr_branch.repository.get_revision(last_rev)
915+ rev = bzr_branch.repository.get_revision(last_rev)
916 apparent_authors = rev.get_apparent_authors()
917 author_list.extend(
918 [a.replace('\n', '') for a in apparent_authors])
919@@ -200,12 +227,13 @@
920 """Return the list of bugs fixed by the branch."""
921 bugs_list = []
922
923- self.bzr_branch.lock_read()
924- oldrevid = self.bzr_branch.get_rev_id(self.lp_branch.revision_count)
925- for rev_info in self.bzr_branch.iter_merge_sorted_revisions(
926+ bzr_branch = self.get_bzr_branch()
927+ bzr_branch.lock_read()
928+ oldrevid = bzr_branch.get_rev_id(self.lp_branch.revision_count)
929+ for rev_info in bzr_branch.iter_merge_sorted_revisions(
930 stop_revision_id=oldrevid):
931 try:
932- rev = self.bzr_branch.repository.get_revision(rev_info[0])
933+ rev = bzr_branch.repository.get_revision(rev_info[0])
934 for bug in rev.iter_bugs():
935 if bug[0].startswith('https://launchpad.net/bugs/'):
936 bugs_list.append(bug[0].replace(
937@@ -213,5 +241,5 @@
938 except NoSuchRevision:
939 continue
940
941- self.bzr_branch.unlock()
942+ bzr_branch.unlock()
943 return bugs_list
944
945=== modified file 'tarmac/plugins/command.py'
946--- tarmac/plugins/command.py 2011-09-02 04:06:25 +0000
947+++ tarmac/plugins/command.py 2012-12-18 13:35:32 +0000
948@@ -103,7 +103,8 @@
949 shell=True,
950 stdin=subprocess.PIPE,
951 stdout=subprocess.PIPE,
952- stderr=subprocess.PIPE)
953+ stderr=subprocess.PIPE,
954+ preexec_fn=os.setsid)
955 proc.stdin.close()
956 stdout = tempfile.TemporaryFile()
957 stderr = tempfile.TemporaryFile()
958@@ -125,7 +126,7 @@
959 killem(proc.pid, signal.SIGTERM)
960 time.sleep(5)
961
962- if proc.poll() is not None:
963+ if proc.poll() is None:
964 self.logger.debug("SIGTERM did not work. Sending SIGKILL.")
965 killem(proc.pid, signal.SIGKILL)
966
967@@ -184,7 +185,7 @@
968 u'%(output)s') % {
969 'source': self.proposal.source_branch.display_name,
970 'target': self.proposal.target_branch.display_name,
971- 'output': u'\n'.join([stdout_value, stderr_value]),
972+ 'output': u'\n'.join([stdout_value.decode('utf-8', 'ignore'), stderr_value.decode('utf-8', 'ignore')]),
973 }
974 raise VerifyCommandFailed(message, comment)
975
976
977=== added file 'tarmac/plugins/commit.py'
978--- tarmac/plugins/commit.py 1970-01-01 00:00:00 +0000
979+++ tarmac/plugins/commit.py 2012-12-18 13:35:32 +0000
980@@ -0,0 +1,24 @@
981+import re
982+
983+from tarmac.hooks import tarmac_hooks
984+from tarmac.plugins import TarmacPlugin
985+
986+
987+class CommitMessage(TarmacPlugin):
988+
989+ def run(self, command, target, source, proposal):
990+ if not re.match(r'^\[.+?\].*$', proposal.commit_message):
991+ params = ["[r=%s]" % proposal.reviewer.name]
992+ for bug in proposal.source_branch.linked_bugs:
993+ params.append("[bug=%s]" % bug.id)
994+
995+ proposal.commit_message = ("%s " % ",".join(params) +
996+ proposal.commit_message)
997+ self.logger.debug("Setting commit_message to %s",
998+ proposal.commit_message)
999+ else:
1000+ self.logger.debug("%s has already right commit message",
1001+ proposal)
1002+
1003+tarmac_hooks['tarmac_pre_commit'].hook(CommitMessage(),
1004+ 'Commit messsage template editor.')

Subscribers

People subscribed via source and target branches