Merge lp:~dobey/tarmac/commands-errors-decorator into lp:tarmac

Proposed by dobey
Status: Merged
Approved by: Paul Hummer
Approved revision: 356
Merged at revision: 356
Proposed branch: lp:~dobey/tarmac/commands-errors-decorator
Merge into: lp:tarmac
Diff against target: 347 lines (+111/-59)
7 files modified
docs/writingplugins.txt (+45/-0)
tarmac/bin/commands.py (+27/-39)
tarmac/branch.py (+10/-4)
tarmac/exceptions.py (+10/-2)
tarmac/plugins/command.py (+9/-7)
tarmac/plugins/tests/test_votes.py (+2/-2)
tarmac/plugins/votes.py (+8/-5)
To merge this branch: bzr merge lp:~dobey/tarmac/commands-errors-decorator
Reviewer Review Type Date Requested Status
Paul Hummer Pending
Review via email: mp+35896@code.launchpad.net

Commit message

Add a new TarmacMergeError exception class for handling merge-preventing errors
Simplify the _do_merges method to use TarmacMergeError
Move comment handling to the point of exception raising
Replace some exceptions with ones derived from TarmacMergeError
Improve some of the exception messages and launchpad comment strings
Document the handling of errors in the writing plug-ins documentation

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'docs/writingplugins.txt'
2--- docs/writingplugins.txt 2010-07-18 16:01:17 +0000
3+++ docs/writingplugins.txt 2010-09-17 22:11:45 +0000
4@@ -67,6 +67,51 @@
5 The merge proposal that proposes the source branch for merge into the target.
6
7
8+
9+Handling Errors
10+===============
11+
12+If you are writing a plug-in to validate against some prerequisites for a
13+merge to be successful, you may wish to raise an exception if this criteria
14+goes unmet, within your plug-in, to prevent the successful merge of a branch.
15+The Tarmac merge command automatically handles exceptions. All you need to
16+do, is raise the right one. To do this, simply use TarmacMergeError, or
17+create your own exception class as a sub-class thereof. For example, if you
18+were writing a plug-in to run a test before committing a change, you might
19+define your exception like this:
20+
21+ from tarmac.exceptions import TarmacMergeError
22+
23+
24+ class TestCommandFailed(TarmacMergeError):
25+ """The test command failed to complete successfully."""
26+
27+
28+And when you raise your exception to prevent the commit from happening, you
29+might do the following:
30+
31+ message = u'The test command failed to complete successfully.'
32+ comment = (u'Tests failed to pass, with the following errors:'
33+ u'\n\n%(output)s' % {'output': test_output})
34+ raise TestCommandFailed(message, comment)
35+
36+The TarmacMergeCommand exception takes two arguments. The first is a short
37+message summarizing what failed, and the second is a comment that would be
38+posted to the merge proposal on Launchpad, to let the developer know what
39+needs to be fixed in their branch. The ``message`` argument is similar to
40+a normal Python Exception message argument. However, in this case, it should
41+be kept to a brief summary of what failed, as the message will be appended
42+to another short string for writing to a log. The ``comment`` argument is
43+typically a longer message describing the issue in more detail. This comment
44+will be posted to the merge proposal on Launchpad, to inform the developer
45+of what went wrong during the merge of their branch. The merge proposal will
46+also be set back to the ``Needs Review`` status, to avoid having tarmac
47+attempt to merge the branch again, without having the issues resolved. If
48+no ``comment`` argument is supplied, the ``message`` argument will be used
49+for the comment when posting to Launchpad, instead.
50+
51+
52+
53 Caveats
54 =======
55
56
57=== modified file 'tarmac/bin/commands.py'
58--- tarmac/bin/commands.py 2010-09-02 15:18:07 +0000
59+++ tarmac/bin/commands.py 2010-09-17 22:11:45 +0000
60@@ -5,7 +5,7 @@
61 import re
62
63 from bzrlib.commands import Command
64-from bzrlib.errors import PointlessMerge, TipChangeRejected
65+from bzrlib.errors import PointlessMerge
66 from bzrlib.help import help_commands
67 from launchpadlib.launchpad import (Credentials, Launchpad, EDGE_SERVICE_ROOT,
68 STAGING_SERVICE_ROOT)
69@@ -14,7 +14,7 @@
70 from tarmac.branch import Branch
71 from tarmac.hooks import tarmac_hooks
72 from tarmac.log import set_up_debug_logging, set_up_logging
73-from tarmac.exceptions import (BranchHasConflicts, TarmacCommandError,
74+from tarmac.exceptions import (TarmacMergeError, TarmacCommandError,
75 UnapprovedChanges)
76 from tarmac.plugin import load_plugins
77
78@@ -149,8 +149,12 @@
79 approved = proposal.reviewed_revid
80 tip = proposal.source_branch.revision_count
81 if tip > approved:
82- raise UnapprovedChanges(
83- 'Unapproved changes to branch after approval.')
84+ message = u'Unapproved changes made after approval'
85+ lp_comment = (
86+ u'There are additional revisions which have not '
87+ u'been approved in review. Please seek review and '
88+ u'approval of these new revisions.')
89+ raise UnapprovedChanges(message, lp_comment)
90
91 self.logger.debug(
92 'Merging %(source)s at revision %(revision)s' % {
93@@ -165,50 +169,34 @@
94 tarmac_hooks['tarmac_pre_commit'].fire(
95 self, target, source, proposal)
96
97- except Exception, failure:
98+ except TarmacMergeError, failure:
99+ self.logger.warn(
100+ u'Merging %(source)s into %(target)s failed: %s' %
101+ str(failure))
102+
103 subject = u'Re: [Merge] %(source)s into %(target)s' % {
104 "source": proposal.source_branch.display_name,
105 "target": proposal.target_branch.display_name}
106- comment = None
107- if isinstance(failure, BranchHasConflicts):
108- self.logger.warn(
109- u'Conflicts merging %(source)s into %(target)s' % {
110- "source": proposal.source_branch.display_name,
111- "target": proposal.target_branch.display_name})
112- comment = (
113- u'Attempt to merge %(source)s into %(target)s '
114- u'failed due to merge conflicts:\n\n%(output)s' % {
115- "source": proposal.source_branch.display_name,
116- "target": proposal.target_branch.display_name,
117- "output": target.conflicts})
118- elif isinstance(failure, PointlessMerge):
119- self.logger.warn(
120- 'Merging %(source)s into %(target)s would be '
121- 'pointless.' % {
122- 'source': proposal.source_branch.display_name,
123- 'target': proposal.target_branch.display_name})
124- target.cleanup()
125- continue
126- elif isinstance(failure, TipChangeRejected):
127- comment = failure.msg
128- elif isinstance(failure, UnapprovedChanges):
129- self.logger.warn(
130- u'Unapproved chagnes to %(source)s were made '
131- u'after approval for merge into %(target)s.' % {
132- "source": proposal.source_branch.display_name,
133- "target": proposal.target_branch.display_name})
134- comment = (
135- u'There are additional revisions which have not '
136- u'been approved in review. Please seek review and '
137- u'approval of these revisions as well.')
138+
139+ if failure.comment:
140+ comment = failure.comment
141 else:
142- raise
143+ comment = str(failure)
144
145- proposal.createComment(subject=subject, content=comment)
146+ proposal.createComment(subject=subject,
147+ content=comment)
148 proposal.setStatus(status=u'Needs review')
149 proposal.lp_save()
150 target.cleanup()
151 continue
152+ except PointlessMerge:
153+ self.logger.warn(
154+ 'Merging %(source)s into %(target)s would be '
155+ 'pointless.' % {
156+ 'source': proposal.source_branch.display_name,
157+ 'target': proposal.target_branch.display_name})
158+ target.cleanup()
159+ continue
160
161 urlp = re.compile('http[s]?://api\.(.*)launchpad\.net/beta/')
162 merge_url = urlp.sub(
163
164=== modified file 'tarmac/branch.py'
165--- tarmac/branch.py 2010-09-02 15:04:52 +0000
166+++ tarmac/branch.py 2010-09-17 22:11:45 +0000
167@@ -30,7 +30,7 @@
168 from bzrlib.workingtree import WorkingTree
169
170 from tarmac.config import BranchConfig
171-from tarmac.exceptions import BranchHasConflicts
172+from tarmac.exceptions import BranchHasConflicts, TarmacMergeError
173
174
175 class Branch(object):
176@@ -93,7 +93,13 @@
177 conflict_list = self.tree.merge_from_branch(
178 branch.bzr_branch, to_revision=revid)
179 if conflict_list:
180- raise BranchHasConflicts
181+ message = u'Conflicts merging branch.'
182+ lp_comment = (
183+ u'Attempt to merge into %(target)s failed due to conflicts: '
184+ u'\n\n%(output)s' % {
185+ 'target': self.lp_branch.display_name,
186+ "output": self.conflicts})
187+ raise BranchHasConflicts(message, lp_comment)
188
189 @property
190 def conflicts(self):
191@@ -119,8 +125,8 @@
192 if reviewers:
193 for reviewer in reviewers:
194 if '\n' in reviewer:
195- raise AssertionError('\\n is not a valid character in a '
196- ' reviewer identity')
197+ raise TarmacMergeError('\\n is not a valid character in a '
198+ 'reviewer identity.')
199 revprops['reviewers'] = '\n'.join(reviewers)
200
201 self.tree.commit(commit_message, committer='Tarmac',
202
203=== modified file 'tarmac/exceptions.py'
204--- tarmac/exceptions.py 2010-08-04 18:26:35 +0000
205+++ tarmac/exceptions.py 2010-09-17 22:11:45 +0000
206@@ -19,7 +19,15 @@
207 from bzrlib.errors import BzrCommandError
208
209
210-class BranchHasConflicts(Exception):
211+class TarmacMergeError(Exception):
212+ """An error occurred, preventing the merge of a branch."""
213+
214+ def __init__(self, message, comment=None, *args, **kwargs):
215+ super(TarmacMergeError, self).__init__(message, *args, **kwargs)
216+ self.comment = comment
217+
218+
219+class BranchHasConflicts(TarmacMergeError):
220 '''Exception for when a branch merge has conflicts.'''
221
222
223@@ -31,5 +39,5 @@
224 '''Exception for various command errors.'''
225
226
227-class UnapprovedChanges(Exception):
228+class UnapprovedChanges(TarmacMergeError):
229 '''Exception for when a branch has unapproved changes.'''
230
231=== modified file 'tarmac/plugins/command.py'
232--- tarmac/plugins/command.py 2010-09-02 15:18:07 +0000
233+++ tarmac/plugins/command.py 2010-09-17 22:11:45 +0000
234@@ -17,7 +17,6 @@
235 '''Tarmac plugin for running tests pre-commit.'''
236
237 # Head off lint warnings.
238-errors = None
239 os = None
240 subprocess = None
241
242@@ -25,14 +24,17 @@
243 lazy_import(globals(), '''
244 import os
245 import subprocess
246-
247- from bzrlib import errors
248 ''')
249
250+from tarmac.exceptions import TarmacMergeError
251 from tarmac.hooks import tarmac_hooks
252 from tarmac.plugins import TarmacPlugin
253
254
255+class VerifyCommandFailed(TarmacMergeError):
256+ """Running the verify_command failed."""
257+
258+
259 class Command(TarmacPlugin):
260 '''Tarmac plugin for running a test command.
261
262@@ -81,15 +83,15 @@
263 doesn't attempt to merge it again without human interaction. An
264 exception is then raised to prevent the commit from happening.
265 '''
266- self.logger.warn(u'Test command "%s" failed.' % self.verify_command)
267- comment = (u'The attempt to merge %(source)s into %(target)s failed.' +
268- u'Below is the output from the failed tests.\n\n' +
269+ message = u'Test command "%s" failed.' % self.verify_command
270+ comment = (u'The attempt to merge %(source)s into %(target)s failed. '
271+ u'Below is the output from the failed tests.\n\n'
272 u'%(output)s') % {
273 'source': self.proposal.source_branch.display_name,
274 'target': self.proposal.target_branch.display_name,
275 'output': u'\n'.join([stdout_value, stderr_value]),
276 }
277- raise errors.TipChangeRejected(comment)
278+ raise VerifyCommandFailed(message, comment)
279
280
281 tarmac_hooks['tarmac_pre_commit'].hook(Command(), 'Command plugin')
282
283=== modified file 'tarmac/plugins/tests/test_votes.py'
284--- tarmac/plugins/tests/test_votes.py 2010-09-09 15:01:57 +0000
285+++ tarmac/plugins/tests/test_votes.py 2010-09-17 22:11:45 +0000
286@@ -89,7 +89,7 @@
287 ("Voting does not meet specified criteria. "
288 "Required: Approve >= 2, Needs Information == 0. "
289 "Got: 1 Abstain, 2 Approve, 1 Needs Information."),
290- str(error))
291+ error.comment)
292 else:
293 self.fail("Votes.run() did not raise VotingViolation.")
294
295@@ -115,6 +115,6 @@
296 ("Voting does not meet specified criteria. "
297 "Required: Approve >= 2, Needs Information == 0. "
298 "Got: 1 Abstain, 2 Approve, 1 Needs Information."),
299- str(error))
300+ error.comment)
301 else:
302 self.fail("Votes.run() did not raise VotingViolation.")
303
304=== modified file 'tarmac/plugins/votes.py'
305--- tarmac/plugins/votes.py 2010-09-09 15:01:57 +0000
306+++ tarmac/plugins/votes.py 2010-09-17 22:11:45 +0000
307@@ -18,6 +18,7 @@
308 import operator
309 import re
310
311+from tarmac.exceptions import TarmacMergeError
312 from tarmac.hooks import tarmac_hooks
313 from tarmac.plugins import TarmacPlugin
314
315@@ -46,7 +47,7 @@
316 """A voting criterion is not understood."""
317
318
319-class VotingViolation(Exception):
320+class VotingViolation(TarmacMergeError):
321 """The voting criteria have not been met."""
322
323
324@@ -80,9 +81,10 @@
325 criteria_desc = ", ".join(
326 "%s %s %d" % (vote, operator_map_inverse[op], value)
327 for (vote, op, value) in criteria)
328- raise VotingViolation(
329- "Voting does not meet specified criteria. "
330- "Required: %s. Got: %s." % (criteria_desc, votes_desc))
331+ lp_comment = (
332+ u"Voting does not meet specified criteria. "
333+ u"Required: %s. Got: %s." % (criteria_desc, votes_desc))
334+ raise VotingViolation(u'Voting criteria not met.', lp_comment)
335
336 def count_votes(self, votes):
337 """Count and collate the votes.
338@@ -108,7 +110,8 @@
339 if len(expression) > 0:
340 match = criteria_expr.match(expression)
341 if match is None:
342- raise InvalidCriterion(expression)
343+ raise InvalidCriterion('Invalid voting criterion: %s' %
344+ expression)
345 else:
346 vote, op, value = match.groups()
347 op, value = operator_map[op], int(value)

Subscribers

People subscribed via source and target branches