Merge lp:~ubuntuone-hackers/tarmac/trunk into lp:tarmac
- trunk
- Merge into main
Status: | Rejected |
---|---|
Rejected by: | dobey |
Proposed branch: | lp:~ubuntuone-hackers/tarmac/trunk |
Merge into: | lp:tarmac |
Diff against target: |
1069 lines (+532/-80) (has conflicts) 13 files modified
bin/tarmac (+3/-1) docs/introduction.txt (+5/-0) tarmac/bin/__init__.py (+1/-1) tarmac/bin/commands.py (+311/-15) tarmac/bin/options.py (+12/-3) tarmac/bin/registry.py (+2/-2) tarmac/branch.py (+111/-20) tarmac/plugins/command.py (+4/-3) tarmac/plugins/commitmessage.py (+10/-6) tarmac/plugins/tests/test_commitmessage.py (+7/-2) tarmac/tests/mock.py (+2/-0) tarmac/tests/test_branch.py (+19/-14) tarmac/tests/test_commands.py (+45/-13) Text conflict in tarmac/bin/commands.py Text conflict in tarmac/bin/options.py Text conflict in tarmac/branch.py |
To merge this branch: | bzr merge lp:~ubuntuone-hackers/tarmac/trunk |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Hummer | Pending | ||
Review via email: mp+140435@code.launchpad.net |
Commit message
Description of the change
WIP MP to make the delta visible.
- 415. By Jonathan Lange
-
Fix the test failures in u1's tarmac fork
- 416. By Jonathan Lange
-
Oops. Really fix the tests.
- 417. By Jonathan Lange
-
Handle unicode output from tests. (~james-w)
- 418. By Jonathan Lange
-
Commit message plugin that sets review & bugs fixed (james-w)
- 419. By Jonathan Lange
-
Make the tests pass.
- 420. By Jonathan Lange
-
Merge James's branch to break up _do_merges, plus changes from my review.
- 421. By Jonathan Lange
-
Merge exit-status
- 422. By Jonathan Lange
-
Add "check" command.
- 423. By Jonathan Lange
-
Allow verify command to be specified on command line.
- 424. By Sidnei da Silva
-
- Fix to not turn 'proposals' into a single Entry object.
- 425. By James Westby
-
Also rename the other call to set_up.
- 426. By dobey
-
Remove the broken commit plug-in.
- 427. By Jonathan Lange
-
Better error message when private branches attack
- 428. By Sidnei da Silva
-
Use HTTPError directly to make it work in Lucid.
- 429. By James Westby
-
Merge trunk.
- 430. By James Westby
-
Allow the branch name to be used in the commit message template. (David Britton)
Unmerged revisions
- 431. By Vincent Ladeuil
-
Set the MP status to Merged after committing instead of waiting for lp to do so.
- 430. By James Westby
-
Allow the branch name to be used in the commit message template. (David Britton)
- 429. By James Westby
-
Merge trunk.
- 428. By Sidnei da Silva
-
Use HTTPError directly to make it work in Lucid.
- 427. By Jonathan Lange
-
Better error message when private branches attack
- 426. By dobey
-
Remove the broken commit plug-in.
- 425. By James Westby
-
Also rename the other call to set_up.
- 424. By Sidnei da Silva
-
- Fix to not turn 'proposals' into a single Entry object.
- 423. By Jonathan Lange
-
Allow verify command to be specified on command line.
- 422. By Jonathan Lange
-
Add "check" command.
Preview Diff
1 | === modified file 'bin/tarmac' | |||
2 | --- bin/tarmac 2010-06-17 17:23:37 +0000 | |||
3 | +++ bin/tarmac 2013-03-13 16:57:22 +0000 | |||
4 | @@ -2,5 +2,7 @@ | |||
5 | 2 | # vim:filetype=python | 2 | # vim:filetype=python |
6 | 3 | '''Main tarmac script.''' | 3 | '''Main tarmac script.''' |
7 | 4 | 4 | ||
8 | 5 | import sys | ||
9 | 6 | |||
10 | 5 | from tarmac.bin import main | 7 | from tarmac.bin import main |
12 | 6 | main() | 8 | sys.exit(main()) |
13 | 7 | 9 | ||
14 | === modified file 'docs/introduction.txt' | |||
15 | --- docs/introduction.txt 2012-06-25 21:05:28 +0000 | |||
16 | +++ docs/introduction.txt 2013-03-13 16:57:22 +0000 | |||
17 | @@ -151,6 +151,11 @@ | |||
18 | 151 | **reviewer** | 151 | **reviewer** |
19 | 152 | The display name of the merge proposal reviewer. | 152 | The display name of the merge proposal reviewer. |
20 | 153 | 153 | ||
21 | 154 | **branch_name** | ||
22 | 155 | The short branch name. i.e.: branch_name in "lp:~user/project/branch_name" | ||
23 | 156 | |||
24 | 157 | **\n** | ||
25 | 158 | A \n in the commit message template will be rendered as a newline character. | ||
26 | 154 | 159 | ||
27 | 155 | Command | 160 | Command |
28 | 156 | ======= | 161 | ======= |
29 | 157 | 162 | ||
30 | === modified file 'tarmac/bin/__init__.py' | |||
31 | --- tarmac/bin/__init__.py 2010-09-02 15:18:07 +0000 | |||
32 | +++ tarmac/bin/__init__.py 2013-03-13 16:57:22 +0000 | |||
33 | @@ -27,4 +27,4 @@ | |||
34 | 27 | args = sys.argv[1:] | 27 | args = sys.argv[1:] |
35 | 28 | if not args: | 28 | if not args: |
36 | 29 | args = ['help'] | 29 | args = ['help'] |
38 | 30 | registry.run(args) | 30 | return registry.run(args) |
39 | 31 | 31 | ||
40 | === modified file 'tarmac/bin/commands.py' | |||
41 | --- tarmac/bin/commands.py 2013-01-31 20:03:27 +0000 | |||
42 | +++ tarmac/bin/commands.py 2013-03-13 16:57:22 +0000 | |||
43 | @@ -10,6 +10,7 @@ | |||
44 | 10 | from launchpadlib.launchpad import Launchpad | 10 | from launchpadlib.launchpad import Launchpad |
45 | 11 | from launchpadlib.uris import (LPNET_SERVICE_ROOT, | 11 | from launchpadlib.uris import (LPNET_SERVICE_ROOT, |
46 | 12 | STAGING_SERVICE_ROOT) | 12 | STAGING_SERVICE_ROOT) |
47 | 13 | from lazr.restfulclient.errors import HTTPError | ||
48 | 13 | 14 | ||
49 | 14 | from tarmac.bin import options | 15 | from tarmac.bin import options |
50 | 15 | from tarmac.branch import Branch | 16 | from tarmac.branch import Branch |
51 | @@ -134,6 +135,237 @@ | |||
52 | 134 | help_commands(self.outf) | 135 | help_commands(self.outf) |
53 | 135 | 136 | ||
54 | 136 | 137 | ||
55 | 138 | def _get_login_name(lp): | ||
56 | 139 | """Return the name of the user `lp` is logged in as. | ||
57 | 140 | |||
58 | 141 | `None` if `lp` is an anonymous connection. | ||
59 | 142 | """ | ||
60 | 143 | try: | ||
61 | 144 | me = lp.me | ||
62 | 145 | except HTTPError, e: | ||
63 | 146 | # XXX Newer lazr.restfulclient has a proper Unauthorized exception, but | ||
64 | 147 | # the version in Lucid does not. | ||
65 | 148 | if e.status == 401: | ||
66 | 149 | return None | ||
67 | 150 | raise | ||
68 | 151 | if me: | ||
69 | 152 | return me.name | ||
70 | 153 | return None | ||
71 | 154 | |||
72 | 155 | |||
73 | 156 | def _get_mergable_proposals_for_branch(lp_branch, logger, imply_commit_message=False): | ||
74 | 157 | """Return a list of the mergable proposals for the given branch.""" | ||
75 | 158 | proposals = [] | ||
76 | 159 | for entry in lp_branch.landing_candidates: | ||
77 | 160 | logger.debug("Considering merge proposal: {0}".format(entry.web_link)) | ||
78 | 161 | |||
79 | 162 | if entry.queue_status != u'Approved': | ||
80 | 163 | logger.debug( | ||
81 | 164 | " Skipping proposal: status is {0}, not " | ||
82 | 165 | "'Approved'".format(entry.queue_status)) | ||
83 | 166 | continue | ||
84 | 167 | |||
85 | 168 | if (not imply_commit_message and not entry.commit_message): | ||
86 | 169 | logger.debug( | ||
87 | 170 | " Skipping proposal: proposal has no commit message") | ||
88 | 171 | continue | ||
89 | 172 | |||
90 | 173 | proposals.append(entry) | ||
91 | 174 | return proposals | ||
92 | 175 | |||
93 | 176 | |||
94 | 177 | def _get_branch(branch_url, launchpad, logger): | ||
95 | 178 | lp_branch = launchpad.branches.getByUrl(url=branch_url) | ||
96 | 179 | if lp_branch is None: | ||
97 | 180 | logger.info( | ||
98 | 181 | 'User {0} could not find {1} branch on Launchpad'.format( | ||
99 | 182 | _get_login_name(launchpad), branch_url)) | ||
100 | 183 | return lp_branch | ||
101 | 184 | |||
102 | 185 | |||
103 | 186 | def _get_branch_and_mps(branch_url, launchpad, logger, | ||
104 | 187 | imply_commit_message=False): | ||
105 | 188 | """Get branch and merge proposals from Launchpad. | ||
106 | 189 | |||
107 | 190 | :param branch_url: The Launchpad URL of the branch. e.g `lp:foo`. | ||
108 | 191 | :param launchpad: A Launchpad API object. | ||
109 | 192 | :param logger: A Python logger. | ||
110 | 193 | :param imply_commit_message: Whether to make up a commit message | ||
111 | 194 | if the merge proposal lacks an explicit one. Defaults to False. | ||
112 | 195 | :return: `(lp_branch, [mergable_mp, ...])` | ||
113 | 196 | """ | ||
114 | 197 | lp_branch = _get_branch(branch_url, launchpad, logger) | ||
115 | 198 | proposals = [] | ||
116 | 199 | |||
117 | 200 | if lp_branch: | ||
118 | 201 | proposals = _get_mergable_proposals_for_branch( | ||
119 | 202 | lp_branch, logger, | ||
120 | 203 | imply_commit_message=imply_commit_message) | ||
121 | 204 | |||
122 | 205 | if not proposals: | ||
123 | 206 | logger.info( | ||
124 | 207 | 'No approved proposals found for %(branch_url)s' % { | ||
125 | 208 | 'branch_url': branch_url}) | ||
126 | 209 | |||
127 | 210 | return lp_branch, proposals | ||
128 | 211 | |||
129 | 212 | |||
130 | 213 | |||
131 | 214 | def _get_reviews(proposal): | ||
132 | 215 | """Get the set of reviews from the proposal.""" | ||
133 | 216 | votes = [vote for vote in proposal.votes if vote.comment] | ||
134 | 217 | if not votes: | ||
135 | 218 | return None | ||
136 | 219 | return [ | ||
137 | 220 | '%s;%s' % (vote.reviewer.display_name, vote.comment.vote) | ||
138 | 221 | for vote in votes] | ||
139 | 222 | |||
140 | 223 | |||
141 | 224 | def set_up(logger, debug=False, http_debug=False): | ||
142 | 225 | if debug: | ||
143 | 226 | set_up_debug_logging() | ||
144 | 227 | logger.debug('Debug logging enabled') | ||
145 | 228 | if http_debug: | ||
146 | 229 | httplib2.debuglevel = 1 | ||
147 | 230 | logger.debug('HTTP debugging enabled.') | ||
148 | 231 | logger.debug('Loading plugins') | ||
149 | 232 | load_plugins() | ||
150 | 233 | logger.debug('Plugins loaded') | ||
151 | 234 | |||
152 | 235 | |||
153 | 236 | def merge_proposals(target, proposals, logger, config, command): | ||
154 | 237 | logger.debug('Firing tarmac_pre_merge hook') | ||
155 | 238 | tarmac_hooks.fire('tarmac_pre_merge', | ||
156 | 239 | command, target) | ||
157 | 240 | try: | ||
158 | 241 | statuses = [ | ||
159 | 242 | merge_proposal(target, proposal, logger, config, command) | ||
160 | 243 | for proposal in proposals] | ||
161 | 244 | logger.debug('Firing tarmac_post_merge hook') | ||
162 | 245 | tarmac_hooks.fire('tarmac_post_merge', | ||
163 | 246 | command, target, success_count=sum(statuses)) | ||
164 | 247 | finally: | ||
165 | 248 | target.cleanup() | ||
166 | 249 | return statuses | ||
167 | 250 | |||
168 | 251 | |||
169 | 252 | def merge_proposal(target, proposal, logger, config, command): | ||
170 | 253 | target.cleanup() | ||
171 | 254 | logger.debug( | ||
172 | 255 | u'Preparing to merge %(source_branch)s' % { | ||
173 | 256 | 'source_branch': proposal.source_branch.web_link}) | ||
174 | 257 | try: | ||
175 | 258 | prerequisite = proposal.prerequisite_branch | ||
176 | 259 | if prerequisite: | ||
177 | 260 | merges = [x for x in prerequisite.landing_targets | ||
178 | 261 | if x.target_branch == target.lp_branch and | ||
179 | 262 | x.queue_status != u'Superseded'] | ||
180 | 263 | if len(merges) == 0: | ||
181 | 264 | raise TarmacMergeError( | ||
182 | 265 | u'No proposals of prerequisite branch.', | ||
183 | 266 | u'No proposals found for merge of %s ' | ||
184 | 267 | u'into %s.' % ( | ||
185 | 268 | prerequisite.web_link, | ||
186 | 269 | target.lp_branch.web_link)) | ||
187 | 270 | elif len(merges) > 1: | ||
188 | 271 | raise TarmacMergeError( | ||
189 | 272 | u'Too many proposals of prerequisite.', | ||
190 | 273 | u'More than one proposal found for merge ' | ||
191 | 274 | u'of %s into %s, which is not Superseded.' % ( | ||
192 | 275 | prerequisite.web_link, | ||
193 | 276 | target.lp_branch.web_link)) | ||
194 | 277 | elif len(merges) == 1: | ||
195 | 278 | if merges[0].queue_status != u'Merged': | ||
196 | 279 | raise TarmacMergeError( | ||
197 | 280 | u'Prerequisite not yet merged.', | ||
198 | 281 | u'The prerequisite %s has not yet been ' | ||
199 | 282 | u'merged into %s.' % ( | ||
200 | 283 | prerequisite.web_link, | ||
201 | 284 | target.lp_branch.web_link)) | ||
202 | 285 | |||
203 | 286 | if not proposal.reviewed_revid: | ||
204 | 287 | raise TarmacMergeError( | ||
205 | 288 | u'No approved revision specified.') | ||
206 | 289 | |||
207 | 290 | |||
208 | 291 | source = Branch.create( | ||
209 | 292 | proposal.source_branch, config, target=target) | ||
210 | 293 | |||
211 | 294 | source_bzr_branch = source.get_bzr_branch() | ||
212 | 295 | approved = source_bzr_branch.revision_id_to_revno( | ||
213 | 296 | str(proposal.reviewed_revid)) | ||
214 | 297 | tip = source_bzr_branch.revno() | ||
215 | 298 | |||
216 | 299 | if tip > approved: | ||
217 | 300 | message = u'Unapproved changes made after approval' | ||
218 | 301 | lp_comment = ( | ||
219 | 302 | u'There are additional revisions which have not ' | ||
220 | 303 | u'been approved in review. Please seek review and ' | ||
221 | 304 | u'approval of these new revisions.') | ||
222 | 305 | raise UnapprovedChanges(message, lp_comment) | ||
223 | 306 | |||
224 | 307 | logger.debug( | ||
225 | 308 | 'Merging %(source)s at revision %(revision)s' % { | ||
226 | 309 | 'source': proposal.source_branch.web_link, | ||
227 | 310 | 'revision': proposal.reviewed_revid}) | ||
228 | 311 | |||
229 | 312 | target.merge(source, str(proposal.reviewed_revid)) | ||
230 | 313 | |||
231 | 314 | logger.debug('Firing tarmac_pre_commit hook') | ||
232 | 315 | tarmac_hooks.fire('tarmac_pre_commit', | ||
233 | 316 | command, target, source, proposal) | ||
234 | 317 | |||
235 | 318 | except TarmacMergeError, failure: | ||
236 | 319 | logger.warn( | ||
237 | 320 | u'Merging %(source)s into %(target)s failed: %(msg)s' % | ||
238 | 321 | {'source': proposal.source_branch.web_link, | ||
239 | 322 | 'target': proposal.target_branch.web_link, | ||
240 | 323 | 'msg': str(failure)}) | ||
241 | 324 | |||
242 | 325 | subject = u'Re: [Merge] %(source)s into %(target)s' % { | ||
243 | 326 | "source": proposal.source_branch.display_name, | ||
244 | 327 | "target": proposal.target_branch.display_name} | ||
245 | 328 | |||
246 | 329 | if failure.comment: | ||
247 | 330 | comment = failure.comment | ||
248 | 331 | else: | ||
249 | 332 | comment = str(failure) | ||
250 | 333 | |||
251 | 334 | proposal.createComment(subject=subject, content=comment) | ||
252 | 335 | try: | ||
253 | 336 | proposal.setStatus( | ||
254 | 337 | status=config.rejected_branch_status) | ||
255 | 338 | except AttributeError: | ||
256 | 339 | proposal.setStatus(status=u'Needs review') | ||
257 | 340 | proposal.lp_save() | ||
258 | 341 | return False | ||
259 | 342 | |||
260 | 343 | except PointlessMerge: | ||
261 | 344 | logger.warn( | ||
262 | 345 | 'Merging %(source)s into %(target)s would be ' | ||
263 | 346 | 'pointless.' % { | ||
264 | 347 | 'source': proposal.source_branch.web_link, | ||
265 | 348 | 'target': proposal.target_branch.web_link}) | ||
266 | 349 | return False | ||
267 | 350 | |||
268 | 351 | merge_url = get_review_url(proposal) | ||
269 | 352 | revprops = {'merge_url': merge_url} | ||
270 | 353 | |||
271 | 354 | commit_message = proposal.commit_message | ||
272 | 355 | if commit_message is None and config.imply_commit_message: | ||
273 | 356 | commit_message = proposal.description | ||
274 | 357 | |||
275 | 358 | target.commit(commit_message, | ||
276 | 359 | revprops=revprops, | ||
277 | 360 | authors=source.authors, | ||
278 | 361 | reviews=_get_reviews(proposal)) | ||
279 | 362 | |||
280 | 363 | logger.debug('Firing tarmac_post_commit hook') | ||
281 | 364 | tarmac_hooks.fire('tarmac_post_commit', | ||
282 | 365 | command, target, source, proposal) | ||
283 | 366 | return True | ||
284 | 367 | |||
285 | 368 | |||
286 | 137 | class cmd_merge(TarmacCommand): | 369 | class cmd_merge(TarmacCommand): |
287 | 138 | '''Automatically merge approved merge proposal branches.''' | 370 | '''Automatically merge approved merge proposal branches.''' |
288 | 139 | 371 | ||
289 | @@ -142,6 +374,7 @@ | |||
290 | 142 | takes_options = [ | 374 | takes_options = [ |
291 | 143 | options.http_debug_option, | 375 | options.http_debug_option, |
292 | 144 | options.debug_option, | 376 | options.debug_option, |
293 | 377 | <<<<<<< TREE | ||
294 | 145 | options.imply_commit_message_option, | 378 | options.imply_commit_message_option, |
295 | 146 | options.one_option] | 379 | options.one_option] |
296 | 147 | 380 | ||
297 | @@ -153,14 +386,26 @@ | |||
298 | 153 | return | 386 | return |
299 | 154 | 387 | ||
300 | 155 | proposals = self._get_mergable_proposals_for_branch(lp_branch) | 388 | proposals = self._get_mergable_proposals_for_branch(lp_branch) |
301 | 389 | ======= | ||
302 | 390 | options.imply_commit_message_option, | ||
303 | 391 | options.one_option, | ||
304 | 392 | options.verify_command_option, | ||
305 | 393 | ] | ||
306 | 394 | |||
307 | 395 | def _do_merges(self, branch_url, verify_command=None): | ||
308 | 396 | lp_branch, proposals = _get_branch_and_mps( | ||
309 | 397 | branch_url, self.launchpad, self.logger, | ||
310 | 398 | self.config.imply_commit_message) | ||
311 | 399 | >>>>>>> MERGE-SOURCE | ||
312 | 156 | 400 | ||
313 | 157 | if not proposals: | 401 | if not proposals: |
318 | 158 | self.logger.info( | 402 | return [] |
319 | 159 | 'No approved proposals found for %(branch_url)s' % { | 403 | |
320 | 160 | 'branch_url': branch_url}) | 404 | if self.config.one: |
321 | 161 | return | 405 | proposals = [proposals[0]] |
322 | 162 | 406 | ||
323 | 163 | target = Branch.create(lp_branch, self.config, create_tree=True) | 407 | target = Branch.create(lp_branch, self.config, create_tree=True) |
324 | 408 | <<<<<<< TREE | ||
325 | 164 | 409 | ||
326 | 165 | self.logger.debug('Firing tarmac_pre_merge hook') | 410 | self.logger.debug('Firing tarmac_pre_merge hook') |
327 | 166 | tarmac_hooks.fire('tarmac_pre_merge', | 411 | tarmac_hooks.fire('tarmac_pre_merge', |
328 | @@ -337,18 +582,19 @@ | |||
329 | 337 | return reviews | 582 | return reviews |
330 | 338 | 583 | ||
331 | 339 | def run(self, branch_url=None, launchpad=None, **kwargs): | 584 | def run(self, branch_url=None, launchpad=None, **kwargs): |
332 | 585 | ======= | ||
333 | 586 | if verify_command is not None: | ||
334 | 587 | setattr(target.config, 'verify_command', verify_command) | ||
335 | 588 | statuses = merge_proposals(target, proposals, self.logger, self.config, self) | ||
336 | 589 | return statuses | ||
337 | 590 | |||
338 | 591 | def run(self, branch_url=None, launchpad=None, verify_command=None, **kwargs): | ||
339 | 592 | >>>>>>> MERGE-SOURCE | ||
340 | 340 | for key, value in kwargs.iteritems(): | 593 | for key, value in kwargs.iteritems(): |
341 | 341 | self.config.set('Tarmac', key, value) | 594 | self.config.set('Tarmac', key, value) |
342 | 342 | 595 | ||
352 | 343 | if self.config.debug: | 596 | set_up(self.logger, debug=self.config.debug, |
353 | 344 | set_up_debug_logging() | 597 | http_debug=self.config.http_debug) |
345 | 345 | self.logger.debug('Debug logging enabled') | ||
346 | 346 | if self.config.http_debug: | ||
347 | 347 | httplib2.debuglevel = 1 | ||
348 | 348 | self.logger.debug('HTTP debugging enabled.') | ||
349 | 349 | self.logger.debug('Loading plugins') | ||
350 | 350 | load_plugins() | ||
351 | 351 | self.logger.debug('Plugins loaded') | ||
354 | 352 | 598 | ||
355 | 353 | self.launchpad = launchpad | 599 | self.launchpad = launchpad |
356 | 354 | if self.launchpad is None: | 600 | if self.launchpad is None: |
357 | @@ -356,19 +602,21 @@ | |||
358 | 356 | self.launchpad = self.get_launchpad_object() | 602 | self.launchpad = self.get_launchpad_object() |
359 | 357 | self.logger.debug('launchpad object loaded') | 603 | self.logger.debug('launchpad object loaded') |
360 | 358 | 604 | ||
361 | 605 | statuses = [] | ||
362 | 606 | |||
363 | 359 | if branch_url: | 607 | if branch_url: |
364 | 360 | self.logger.debug('%(branch_url)s specified as branch_url' % { | 608 | self.logger.debug('%(branch_url)s specified as branch_url' % { |
365 | 361 | 'branch_url': branch_url}) | 609 | 'branch_url': branch_url}) |
366 | 362 | if not branch_url.startswith('lp:'): | 610 | if not branch_url.startswith('lp:'): |
367 | 363 | raise TarmacCommandError('Branch urls must start with lp:') | 611 | raise TarmacCommandError('Branch urls must start with lp:') |
370 | 364 | self._do_merges(branch_url) | 612 | statuses.extend(self._do_merges(branch_url, verify_command=verify_command)) |
369 | 365 | |||
371 | 366 | else: | 613 | else: |
372 | 367 | for branch in self.config.branches: | 614 | for branch in self.config.branches: |
373 | 368 | self.logger.debug( | 615 | self.logger.debug( |
374 | 369 | 'Merging approved branches against %(branch)s' % { | 616 | 'Merging approved branches against %(branch)s' % { |
375 | 370 | 'branch': branch}) | 617 | 'branch': branch}) |
376 | 371 | try: | 618 | try: |
377 | 619 | <<<<<<< TREE | ||
378 | 372 | merged = self._do_merges(branch) | 620 | merged = self._do_merges(branch) |
379 | 373 | 621 | ||
380 | 374 | # If we've been asked to only merge one branch, then exit. | 622 | # If we've been asked to only merge one branch, then exit. |
381 | @@ -376,8 +624,56 @@ | |||
382 | 376 | break | 624 | break |
383 | 377 | except LockContention: | 625 | except LockContention: |
384 | 378 | continue | 626 | continue |
385 | 627 | ======= | ||
386 | 628 | statuses.extend(self._do_merges(branch, verify_command=verify_command)) | ||
387 | 629 | |||
388 | 630 | # If we've been asked to only merge one branch, then exit. | ||
389 | 631 | if statuses and self.config.one: | ||
390 | 632 | break | ||
391 | 633 | except LockContention: | ||
392 | 634 | continue | ||
393 | 635 | >>>>>>> MERGE-SOURCE | ||
394 | 379 | except Exception, error: | 636 | except Exception, error: |
395 | 380 | self.logger.error( | 637 | self.logger.error( |
396 | 381 | 'An error occurred trying to merge %s: %s', | 638 | 'An error occurred trying to merge %s: %s', |
397 | 382 | branch, error) | 639 | branch, error) |
398 | 383 | raise | 640 | raise |
399 | 641 | if not all(statuses): | ||
400 | 642 | return 2 | ||
401 | 643 | return 0 | ||
402 | 644 | |||
403 | 645 | |||
404 | 646 | class cmd_check(TarmacCommand): | ||
405 | 647 | '''Check whether there are any merge proposals ready to land.''' | ||
406 | 648 | |||
407 | 649 | takes_args = ['branch_url'] | ||
408 | 650 | takes_options = [ | ||
409 | 651 | options.http_debug_option, | ||
410 | 652 | options.debug_option] | ||
411 | 653 | |||
412 | 654 | def _any_merges(self, branch_url): | ||
413 | 655 | lp_branch, proposals = _get_branch_and_mps( | ||
414 | 656 | branch_url, self.launchpad, self.logger) | ||
415 | 657 | return bool(proposals) | ||
416 | 658 | |||
417 | 659 | def run(self, branch_url, launchpad=None, **kwargs): | ||
418 | 660 | for key, value in kwargs.iteritems(): | ||
419 | 661 | self.config.set('Tarmac', key, value) | ||
420 | 662 | |||
421 | 663 | set_up(self.logger, debug=self.config.debug, | ||
422 | 664 | http_debug=self.config.http_debug) | ||
423 | 665 | |||
424 | 666 | self.launchpad = launchpad | ||
425 | 667 | if self.launchpad is None: | ||
426 | 668 | self.logger.debug('Loading launchpad object') | ||
427 | 669 | self.launchpad = self.get_launchpad_object() | ||
428 | 670 | self.logger.debug('launchpad object loaded') | ||
429 | 671 | |||
430 | 672 | self.logger.debug('%(branch_url)s specified as branch_url' % { | ||
431 | 673 | 'branch_url': branch_url}) | ||
432 | 674 | if not branch_url.startswith('lp:'): | ||
433 | 675 | raise TarmacCommandError('Branch urls must start with lp:') | ||
434 | 676 | ret = self._any_merges(branch_url) | ||
435 | 677 | if ret: | ||
436 | 678 | return 0 | ||
437 | 679 | return 1 | ||
438 | 384 | 680 | ||
439 | === modified file 'tarmac/bin/options.py' | |||
440 | --- tarmac/bin/options.py 2012-05-26 04:14:45 +0000 | |||
441 | +++ tarmac/bin/options.py 2013-03-13 16:57:22 +0000 | |||
442 | @@ -15,6 +15,15 @@ | |||
443 | 15 | 'imply-commit-message', | 15 | 'imply-commit-message', |
444 | 16 | help=("Use the description as a commit message if the branch " | 16 | help=("Use the description as a commit message if the branch " |
445 | 17 | "doesn't have a message")) | 17 | "doesn't have a message")) |
449 | 18 | one_option = Option( | 18 | <<<<<<< TREE |
450 | 19 | 'one', short_name='1', | 19 | one_option = Option( |
451 | 20 | help='Merge only one branch and exit.') | 20 | 'one', short_name='1', |
452 | 21 | help='Merge only one branch and exit.') | ||
453 | 22 | ======= | ||
454 | 23 | one_option = Option( | ||
455 | 24 | 'one', short_name='1', | ||
456 | 25 | help='Merge only one branch and exit.') | ||
457 | 26 | verify_command_option = Option( | ||
458 | 27 | 'verify-command', type=str, | ||
459 | 28 | help='The verify command to run.') | ||
460 | 29 | >>>>>>> MERGE-SOURCE | ||
461 | 21 | 30 | ||
462 | === modified file 'tarmac/bin/registry.py' | |||
463 | --- tarmac/bin/registry.py 2010-10-25 20:20:18 +0000 | |||
464 | +++ tarmac/bin/registry.py 2013-03-13 16:57:22 +0000 | |||
465 | @@ -45,7 +45,7 @@ | |||
466 | 45 | 45 | ||
467 | 46 | def _run(self, args): | 46 | def _run(self, args): |
468 | 47 | '''Execute the command.''' | 47 | '''Execute the command.''' |
470 | 48 | run_bzr(args) | 48 | return run_bzr(args) |
471 | 49 | 49 | ||
472 | 50 | def install_hooks(self): | 50 | def install_hooks(self): |
473 | 51 | '''Use the bzrlib Command support for running commands.''' | 51 | '''Use the bzrlib Command support for running commands.''' |
474 | @@ -57,7 +57,7 @@ | |||
475 | 57 | def run(self, args): | 57 | def run(self, args): |
476 | 58 | '''Execute the command.''' | 58 | '''Execute the command.''' |
477 | 59 | try: | 59 | try: |
479 | 60 | self._run(args) | 60 | return self._run(args) |
480 | 61 | except BzrCommandError, e: | 61 | except BzrCommandError, e: |
481 | 62 | sys.exit('tarmac: ERROR: ' + str(e)) | 62 | sys.exit('tarmac: ERROR: ' + str(e)) |
482 | 63 | 63 | ||
483 | 64 | 64 | ||
484 | === modified file 'tarmac/branch.py' | |||
485 | --- tarmac/branch.py 2013-02-04 21:07:51 +0000 | |||
486 | +++ tarmac/branch.py 2013-03-13 16:57:22 +0000 | |||
487 | @@ -37,7 +37,6 @@ | |||
488 | 37 | 37 | ||
489 | 38 | def __init__(self, lp_branch, config=False, target=None): | 38 | def __init__(self, lp_branch, config=False, target=None): |
490 | 39 | self.lp_branch = lp_branch | 39 | self.lp_branch = lp_branch |
491 | 40 | self.bzr_branch = bzr_branch.Branch.open(self.lp_branch.bzr_identity) | ||
492 | 41 | if config: | 40 | if config: |
493 | 42 | self.config = BranchConfig(lp_branch.bzr_identity, config) | 41 | self.config = BranchConfig(lp_branch.bzr_identity, config) |
494 | 43 | else: | 42 | else: |
495 | @@ -45,15 +44,27 @@ | |||
496 | 45 | 44 | ||
497 | 46 | self.target = target | 45 | self.target = target |
498 | 47 | self.logger = logging.getLogger('tarmac') | 46 | self.logger = logging.getLogger('tarmac') |
499 | 47 | self.temp_tree_dir = None | ||
500 | 48 | |||
501 | 49 | def get_bzr_branch(self): | ||
502 | 50 | return bzr_branch.Branch.open(self.lp_branch.bzr_identity) | ||
503 | 51 | |||
504 | 52 | # For backwards compatibility | ||
505 | 53 | bzr_branch = property(get_bzr_branch) | ||
506 | 54 | |||
507 | 55 | def get_tree(self): | ||
508 | 56 | if self.temp_tree_dir is not None: | ||
509 | 57 | return WorkingTree.open(self.temp_tree_dir) | ||
510 | 58 | if os.path.exists(self.config.tree_dir): | ||
511 | 59 | return WorkingTree.open(self.config.tree_dir) | ||
512 | 60 | |||
513 | 61 | # For backwards compatibility | ||
514 | 62 | tree = property(get_tree) | ||
515 | 48 | 63 | ||
516 | 49 | def __del__(self): | 64 | def __del__(self): |
517 | 50 | """Do some potenetially necessary cleanup during deletion.""" | 65 | """Do some potenetially necessary cleanup during deletion.""" |
520 | 51 | try: | 66 | if self.temp_tree_dir is not None: |
519 | 52 | # If we were using a temp directory, then remove it | ||
521 | 53 | shutil.rmtree(self.temp_tree_dir) | 67 | shutil.rmtree(self.temp_tree_dir) |
522 | 54 | except AttributeError: | ||
523 | 55 | # Not using a tempdir | ||
524 | 56 | pass | ||
525 | 57 | 68 | ||
526 | 58 | @classmethod | 69 | @classmethod |
527 | 59 | def create(cls, lp_branch, config, create_tree=False, target=None): | 70 | def create(cls, lp_branch, config, create_tree=False, target=None): |
528 | @@ -62,12 +73,25 @@ | |||
529 | 62 | clazz.create_tree() | 73 | clazz.create_tree() |
530 | 63 | return clazz | 74 | return clazz |
531 | 64 | 75 | ||
532 | 76 | |||
533 | 65 | def create_tree(self): | 77 | def create_tree(self): |
534 | 66 | '''Create the dir and working tree.''' | 78 | '''Create the dir and working tree.''' |
535 | 79 | bzr_branch = self.get_bzr_branch() | ||
536 | 67 | try: | 80 | try: |
537 | 81 | tree = self.get_tree() | ||
538 | 82 | if tree is None: | ||
539 | 83 | self.logger.debug('Tree does not exist. Creating dir') | ||
540 | 84 | # Create the path up to but not including tree_dir if it does | ||
541 | 85 | # not exist. | ||
542 | 86 | parent_dir = os.path.dirname(self.config.tree_dir) | ||
543 | 87 | if not os.path.exists(parent_dir): | ||
544 | 88 | os.makedirs(parent_dir) | ||
545 | 89 | tree = bzr_branch.create_checkout( | ||
546 | 90 | self.config.tree_dir, lightweight=False) | ||
547 | 68 | self.logger.debug( | 91 | self.logger.debug( |
548 | 69 | 'Using tree in %(tree_dir)s' % { | 92 | 'Using tree in %(tree_dir)s' % { |
549 | 70 | 'tree_dir': self.config.tree_dir}) | 93 | 'tree_dir': self.config.tree_dir}) |
550 | 94 | <<<<<<< TREE | ||
551 | 71 | if os.path.exists(self.config.tree_dir): | 95 | if os.path.exists(self.config.tree_dir): |
552 | 72 | self.tree = WorkingTree.open(self.config.tree_dir) | 96 | self.tree = WorkingTree.open(self.config.tree_dir) |
553 | 73 | else: | 97 | else: |
554 | @@ -79,33 +103,43 @@ | |||
555 | 79 | os.makedirs(parent_dir) | 103 | os.makedirs(parent_dir) |
556 | 80 | self.tree = self.bzr_branch.create_checkout( | 104 | self.tree = self.bzr_branch.create_checkout( |
557 | 81 | self.config.tree_dir, lightweight=True) | 105 | self.config.tree_dir, lightweight=True) |
558 | 106 | ======= | ||
559 | 107 | >>>>>>> MERGE-SOURCE | ||
560 | 82 | except AttributeError: | 108 | except AttributeError: |
561 | 83 | # Store this so we can rmtree later | 109 | # Store this so we can rmtree later |
562 | 84 | self.temp_tree_dir = tempfile.mkdtemp() | 110 | self.temp_tree_dir = tempfile.mkdtemp() |
563 | 85 | self.logger.debug( | 111 | self.logger.debug( |
564 | 86 | 'Using temp dir at %(tree_dir)s' % { | 112 | 'Using temp dir at %(tree_dir)s' % { |
565 | 87 | 'tree_dir': self.temp_tree_dir}) | 113 | 'tree_dir': self.temp_tree_dir}) |
567 | 88 | self.tree = self.bzr_branch.create_checkout(self.temp_tree_dir) | 114 | tree = bzr_branch.create_checkout(self.temp_tree_dir) |
568 | 89 | 115 | ||
569 | 90 | self.cleanup() | 116 | self.cleanup() |
570 | 91 | 117 | ||
571 | 92 | def cleanup(self): | 118 | def cleanup(self): |
572 | 93 | '''Remove the working tree from the temp dir.''' | 119 | '''Remove the working tree from the temp dir.''' |
576 | 94 | assert self.tree | 120 | tree = self.get_tree() |
577 | 95 | self.tree.revert() | 121 | assert tree |
578 | 96 | for filename in [self.tree.abspath(f) for f in self.unmanaged_files]: | 122 | self.logger.info("Running cleanup in %s." % ( |
579 | 123 | self.lp_branch.bzr_identity)) | ||
580 | 124 | tree.revert() | ||
581 | 125 | self.logger.info("Reverted changes in %s." % ( | ||
582 | 126 | self.lp_branch.bzr_identity)) | ||
583 | 127 | for filename in [tree.abspath(f) for f in self.unmanaged_files]: | ||
584 | 97 | if os.path.isdir(filename) and not os.path.islink(filename): | 128 | if os.path.isdir(filename) and not os.path.islink(filename): |
585 | 98 | shutil.rmtree(filename) | 129 | shutil.rmtree(filename) |
586 | 99 | else: | 130 | else: |
587 | 100 | os.remove(filename) | 131 | os.remove(filename) |
588 | 101 | 132 | ||
590 | 102 | self.tree.update() | 133 | self.logger.info("Successfully removed extra files from %s." % ( |
591 | 134 | self.lp_branch.bzr_identity)) | ||
592 | 135 | tree.update() | ||
593 | 103 | 136 | ||
594 | 104 | def merge(self, branch, revid=None): | 137 | def merge(self, branch, revid=None): |
595 | 105 | '''Merge from another tarmac.branch.Branch instance.''' | 138 | '''Merge from another tarmac.branch.Branch instance.''' |
599 | 106 | assert self.tree | 139 | tree = self.get_tree() |
600 | 107 | conflict_list = self.tree.merge_from_branch( | 140 | assert tree |
601 | 108 | branch.bzr_branch, to_revision=revid) | 141 | conflict_list = tree.merge_from_branch( |
602 | 142 | branch.get_bzr_branch(), to_revision=revid) | ||
603 | 109 | if conflict_list: | 143 | if conflict_list: |
604 | 110 | message = u'Conflicts merging branch.' | 144 | message = u'Conflicts merging branch.' |
605 | 111 | lp_comment = ( | 145 | lp_comment = ( |
606 | @@ -118,6 +152,7 @@ | |||
607 | 118 | @property | 152 | @property |
608 | 119 | def unmanaged_files(self): | 153 | def unmanaged_files(self): |
609 | 120 | """Get the list of ignored and unknown files in the tree.""" | 154 | """Get the list of ignored and unknown files in the tree.""" |
610 | 155 | <<<<<<< TREE | ||
611 | 121 | unmanaged = [] | 156 | unmanaged = [] |
612 | 122 | try: | 157 | try: |
613 | 123 | self.tree.lock_read() | 158 | self.tree.lock_read() |
614 | @@ -125,20 +160,31 @@ | |||
615 | 125 | unmanaged.extend([x[0] for x in self.tree.ignored_files()]) | 160 | unmanaged.extend([x[0] for x in self.tree.ignored_files()]) |
616 | 126 | finally: | 161 | finally: |
617 | 127 | self.tree.unlock() | 162 | self.tree.unlock() |
618 | 163 | ======= | ||
619 | 164 | tree = self.get_tree() | ||
620 | 165 | assert tree | ||
621 | 166 | tree.lock_read() | ||
622 | 167 | unmanaged = [x for x in tree.unknowns()] | ||
623 | 168 | unmanaged.extend([x[0] for x in tree.ignored_files()]) | ||
624 | 169 | tree.unlock() | ||
625 | 170 | >>>>>>> MERGE-SOURCE | ||
626 | 128 | return unmanaged | 171 | return unmanaged |
627 | 129 | 172 | ||
628 | 130 | @property | 173 | @property |
629 | 131 | def conflicts(self): | 174 | def conflicts(self): |
630 | 132 | '''Print the conflicts.''' | 175 | '''Print the conflicts.''' |
632 | 133 | assert self.tree.conflicts() | 176 | tree = self.get_tree() |
633 | 177 | assert tree | ||
634 | 134 | conflicts = [] | 178 | conflicts = [] |
636 | 135 | for conflict in self.tree.conflicts(): | 179 | for conflict in tree.conflicts(): |
637 | 136 | conflicts.append( | 180 | conflicts.append( |
638 | 137 | u'%s in %s' % (conflict.typestring, conflict.path)) | 181 | u'%s in %s' % (conflict.typestring, conflict.path)) |
639 | 138 | return '\n'.join(conflicts) | 182 | return '\n'.join(conflicts) |
640 | 139 | 183 | ||
641 | 140 | def commit(self, commit_message, revprops=None, **kwargs): | 184 | def commit(self, commit_message, revprops=None, **kwargs): |
642 | 141 | '''Commit changes.''' | 185 | '''Commit changes.''' |
643 | 186 | tree = self.get_tree() | ||
644 | 187 | assert tree | ||
645 | 142 | if not revprops: | 188 | if not revprops: |
646 | 143 | revprops = {} | 189 | revprops = {} |
647 | 144 | 190 | ||
648 | @@ -155,8 +201,8 @@ | |||
649 | 155 | 'review identity or vote.') | 201 | 'review identity or vote.') |
650 | 156 | revprops['reviews'] = '\n'.join(reviews) | 202 | revprops['reviews'] = '\n'.join(reviews) |
651 | 157 | 203 | ||
654 | 158 | self.tree.commit(commit_message, committer='Tarmac', | 204 | tree.commit(commit_message, committer='Tarmac', |
655 | 159 | revprops=revprops, authors=authors) | 205 | revprops=revprops, authors=authors) |
656 | 160 | 206 | ||
657 | 161 | @property | 207 | @property |
658 | 162 | def landing_candidates(self): | 208 | def landing_candidates(self): |
659 | @@ -167,7 +213,9 @@ | |||
660 | 167 | def authors(self): | 213 | def authors(self): |
661 | 168 | author_list = [] | 214 | author_list = [] |
662 | 169 | 215 | ||
663 | 216 | bzr_branch = self.get_bzr_branch() | ||
664 | 170 | if self.target: | 217 | if self.target: |
665 | 218 | <<<<<<< TREE | ||
666 | 171 | try: | 219 | try: |
667 | 172 | self.bzr_branch.lock_read() | 220 | self.bzr_branch.lock_read() |
668 | 173 | self.target.bzr_branch.lock_read() | 221 | self.target.bzr_branch.lock_read() |
669 | @@ -190,10 +238,33 @@ | |||
670 | 190 | finally: | 238 | finally: |
671 | 191 | self.target.bzr_branch.unlock() | 239 | self.target.bzr_branch.unlock() |
672 | 192 | self.bzr_branch.unlock() | 240 | self.bzr_branch.unlock() |
673 | 241 | ======= | ||
674 | 242 | target_bzr_branch = self.target.get_bzr_branch() | ||
675 | 243 | bzr_branch.lock_read() | ||
676 | 244 | target_bzr_branch.lock_read() | ||
677 | 245 | |||
678 | 246 | graph = bzr_branch.repository.get_graph( | ||
679 | 247 | target_bzr_branch.repository) | ||
680 | 248 | |||
681 | 249 | unique_ids = graph.find_unique_ancestors( | ||
682 | 250 | bzr_branch.last_revision(), | ||
683 | 251 | [target_bzr_branch.last_revision()]) | ||
684 | 252 | |||
685 | 253 | revs = bzr_branch.repository.get_revisions(unique_ids) | ||
686 | 254 | for rev in revs: | ||
687 | 255 | apparent_authors = rev.get_apparent_authors() | ||
688 | 256 | for author in apparent_authors: | ||
689 | 257 | author.replace('\n', '') | ||
690 | 258 | if author not in author_list: | ||
691 | 259 | author_list.append(author) | ||
692 | 260 | |||
693 | 261 | target_bzr_branch.unlock() | ||
694 | 262 | bzr_branch.unlock() | ||
695 | 263 | >>>>>>> MERGE-SOURCE | ||
696 | 193 | else: | 264 | else: |
698 | 194 | last_rev = self.bzr_branch.last_revision() | 265 | last_rev = bzr_branch.last_revision() |
699 | 195 | if last_rev != 'null:': | 266 | if last_rev != 'null:': |
701 | 196 | rev = self.bzr_branch.repository.get_revision(last_rev) | 267 | rev = bzr_branch.repository.get_revision(last_rev) |
702 | 197 | apparent_authors = rev.get_apparent_authors() | 268 | apparent_authors = rev.get_apparent_authors() |
703 | 198 | author_list.extend( | 269 | author_list.extend( |
704 | 199 | [a.replace('\n', '') for a in apparent_authors]) | 270 | [a.replace('\n', '') for a in apparent_authors]) |
705 | @@ -205,6 +276,7 @@ | |||
706 | 205 | """Return the list of bugs fixed by the branch.""" | 276 | """Return the list of bugs fixed by the branch.""" |
707 | 206 | bugs_list = [] | 277 | bugs_list = [] |
708 | 207 | 278 | ||
709 | 279 | <<<<<<< TREE | ||
710 | 208 | try: | 280 | try: |
711 | 209 | self.bzr_branch.lock_read() | 281 | self.bzr_branch.lock_read() |
712 | 210 | oldrevid = self.bzr_branch.get_rev_id(self.lp_branch.revision_count) | 282 | oldrevid = self.bzr_branch.get_rev_id(self.lp_branch.revision_count) |
713 | @@ -218,7 +290,26 @@ | |||
714 | 218 | 'https://launchpad.net/bugs/', '')) | 290 | 'https://launchpad.net/bugs/', '')) |
715 | 219 | except NoSuchRevision: | 291 | except NoSuchRevision: |
716 | 220 | continue | 292 | continue |
717 | 293 | ======= | ||
718 | 294 | bzr_branch = self.get_bzr_branch() | ||
719 | 295 | bzr_branch.lock_read() | ||
720 | 296 | oldrevid = bzr_branch.get_rev_id(self.lp_branch.revision_count) | ||
721 | 297 | for rev_info in bzr_branch.iter_merge_sorted_revisions( | ||
722 | 298 | stop_revision_id=oldrevid): | ||
723 | 299 | try: | ||
724 | 300 | rev = bzr_branch.repository.get_revision(rev_info[0]) | ||
725 | 301 | for bug in rev.iter_bugs(): | ||
726 | 302 | if bug[0].startswith('https://launchpad.net/bugs/'): | ||
727 | 303 | bugs_list.append(bug[0].replace( | ||
728 | 304 | 'https://launchpad.net/bugs/', '')) | ||
729 | 305 | except NoSuchRevision: | ||
730 | 306 | continue | ||
731 | 307 | >>>>>>> MERGE-SOURCE | ||
732 | 221 | 308 | ||
733 | 309 | <<<<<<< TREE | ||
734 | 222 | finally: | 310 | finally: |
735 | 223 | self.bzr_branch.unlock() | 311 | self.bzr_branch.unlock() |
736 | 312 | ======= | ||
737 | 313 | bzr_branch.unlock() | ||
738 | 314 | >>>>>>> MERGE-SOURCE | ||
739 | 224 | return bugs_list | 315 | return bugs_list |
740 | 225 | 316 | ||
741 | === modified file 'tarmac/plugins/command.py' | |||
742 | --- tarmac/plugins/command.py 2011-09-02 04:06:25 +0000 | |||
743 | +++ tarmac/plugins/command.py 2013-03-13 16:57:22 +0000 | |||
744 | @@ -103,7 +103,8 @@ | |||
745 | 103 | shell=True, | 103 | shell=True, |
746 | 104 | stdin=subprocess.PIPE, | 104 | stdin=subprocess.PIPE, |
747 | 105 | stdout=subprocess.PIPE, | 105 | stdout=subprocess.PIPE, |
749 | 106 | stderr=subprocess.PIPE) | 106 | stderr=subprocess.PIPE, |
750 | 107 | preexec_fn=os.setsid) | ||
751 | 107 | proc.stdin.close() | 108 | proc.stdin.close() |
752 | 108 | stdout = tempfile.TemporaryFile() | 109 | stdout = tempfile.TemporaryFile() |
753 | 109 | stderr = tempfile.TemporaryFile() | 110 | stderr = tempfile.TemporaryFile() |
754 | @@ -125,7 +126,7 @@ | |||
755 | 125 | killem(proc.pid, signal.SIGTERM) | 126 | killem(proc.pid, signal.SIGTERM) |
756 | 126 | time.sleep(5) | 127 | time.sleep(5) |
757 | 127 | 128 | ||
759 | 128 | if proc.poll() is not None: | 129 | if proc.poll() is None: |
760 | 129 | self.logger.debug("SIGTERM did not work. Sending SIGKILL.") | 130 | self.logger.debug("SIGTERM did not work. Sending SIGKILL.") |
761 | 130 | killem(proc.pid, signal.SIGKILL) | 131 | killem(proc.pid, signal.SIGKILL) |
762 | 131 | 132 | ||
763 | @@ -184,7 +185,7 @@ | |||
764 | 184 | u'%(output)s') % { | 185 | u'%(output)s') % { |
765 | 185 | 'source': self.proposal.source_branch.display_name, | 186 | 'source': self.proposal.source_branch.display_name, |
766 | 186 | 'target': self.proposal.target_branch.display_name, | 187 | 'target': self.proposal.target_branch.display_name, |
768 | 187 | 'output': u'\n'.join([stdout_value, stderr_value]), | 188 | 'output': u'\n'.join([stdout_value.decode('utf-8', 'ignore'), stderr_value.decode('utf-8', 'ignore')]), |
769 | 188 | } | 189 | } |
770 | 189 | raise VerifyCommandFailed(message, comment) | 190 | raise VerifyCommandFailed(message, comment) |
771 | 190 | 191 | ||
772 | 191 | 192 | ||
773 | === modified file 'tarmac/plugins/commitmessage.py' | |||
774 | --- tarmac/plugins/commitmessage.py 2010-10-25 21:27:39 +0000 | |||
775 | +++ tarmac/plugins/commitmessage.py 2013-03-13 16:57:22 +0000 | |||
776 | @@ -30,18 +30,17 @@ | |||
777 | 30 | 30 | ||
778 | 31 | def run(self, command, target, source, proposal): | 31 | def run(self, command, target, source, proposal): |
779 | 32 | # pylint: disable-msg=W0613 | 32 | # pylint: disable-msg=W0613 |
780 | 33 | |||
781 | 34 | try: | 33 | try: |
784 | 35 | template = target.config.commit_message_template | 34 | proposal.commit_message = self.render( |
785 | 36 | template = template.replace('<', '%(').replace('>', ')s') | 35 | target.config.commit_message_template, |
786 | 36 | CommitMessageTemplateInfo(proposal)) | ||
787 | 37 | except AttributeError: | 37 | except AttributeError: |
788 | 38 | return | 38 | return |
789 | 39 | 39 | ||
790 | 40 | proposal.commit_message = self.render( | ||
791 | 41 | template, CommitMessageTemplateInfo(proposal)) | ||
792 | 42 | |||
793 | 43 | def render(self, template, info): | 40 | def render(self, template, info): |
794 | 44 | """Render a template using the given information.""" | 41 | """Render a template using the given information.""" |
795 | 42 | template = template.replace('<', '%(').replace('>', ')s') | ||
796 | 43 | template = template.replace('\\n', '\n') | ||
797 | 45 | return template % info | 44 | return template % info |
798 | 46 | 45 | ||
799 | 47 | 46 | ||
800 | @@ -77,6 +76,11 @@ | |||
801 | 77 | return self._proposal.commit_message | 76 | return self._proposal.commit_message |
802 | 78 | 77 | ||
803 | 79 | @property | 78 | @property |
804 | 79 | def branch_name(self): | ||
805 | 80 | """The branch name under review.""" | ||
806 | 81 | return self._proposal.source_branch.name | ||
807 | 82 | |||
808 | 83 | @property | ||
809 | 80 | def reviewer(self): | 84 | def reviewer(self): |
810 | 81 | """The display name of the merge proposal reviewer. | 85 | """The display name of the merge proposal reviewer. |
811 | 82 | 86 | ||
812 | 83 | 87 | ||
813 | === modified file 'tarmac/plugins/tests/test_commitmessage.py' | |||
814 | --- tarmac/plugins/tests/test_commitmessage.py 2010-10-25 21:27:39 +0000 | |||
815 | +++ tarmac/plugins/tests/test_commitmessage.py 2013-03-13 16:57:22 +0000 | |||
816 | @@ -12,6 +12,7 @@ | |||
817 | 12 | super(TestCommitMessageTemplateInfo, self).setUp() | 12 | super(TestCommitMessageTemplateInfo, self).setUp() |
818 | 13 | self.proposal = Thing( | 13 | self.proposal = Thing( |
819 | 14 | source_branch=Thing( | 14 | source_branch=Thing( |
820 | 15 | name="name", | ||
821 | 15 | owner=Thing(display_name="Arthur Author", name="arthur"), | 16 | owner=Thing(display_name="Arthur Author", name="arthur"), |
822 | 16 | linked_bugs=[Thing(id=1234), Thing(id=5678)]), | 17 | linked_bugs=[Thing(id=1234), Thing(id=5678)]), |
823 | 17 | commit_message="Awesome", | 18 | commit_message="Awesome", |
824 | @@ -83,8 +84,8 @@ | |||
825 | 83 | return "{info:%s}" % name | 84 | return "{info:%s}" % name |
826 | 84 | 85 | ||
827 | 85 | 86 | ||
830 | 86 | class TestCommitMessageTemplate(TarmacTestCase): | 87 | class TestCommitMessageTemplate(TestCommitMessageTemplateInfo): |
831 | 87 | 88 | ||
832 | 88 | def test_render(self): | 89 | def test_render(self): |
833 | 89 | message_template = CommitMessageTemplate() | 90 | message_template = CommitMessageTemplate() |
834 | 90 | message_info = FakeCommitMessageTemplateInfo() | 91 | message_info = FakeCommitMessageTemplateInfo() |
835 | @@ -99,3 +100,7 @@ | |||
836 | 99 | self.assertEqual( | 100 | self.assertEqual( |
837 | 100 | "{info:author} {info:reviewer}", | 101 | "{info:author} {info:reviewer}", |
838 | 101 | render("%(author)s %(reviewer)s", message_info)) | 102 | render("%(author)s %(reviewer)s", message_info)) |
839 | 103 | self.assertEqual( | ||
840 | 104 | "{info:author} {info:branch_name} {info:reviewer}", | ||
841 | 105 | render("<author> <branch_name> <reviewer>", message_info)) | ||
842 | 106 | self.assertEqual("one\ntwo", render("one\\ntwo", message_info)) | ||
843 | 102 | 107 | ||
844 | === modified file 'tarmac/tests/mock.py' | |||
845 | --- tarmac/tests/mock.py 2010-11-19 18:11:56 +0000 | |||
846 | +++ tarmac/tests/mock.py 2013-03-13 16:57:22 +0000 | |||
847 | @@ -50,6 +50,8 @@ | |||
848 | 50 | self.revision_count = 0 | 50 | self.revision_count = 0 |
849 | 51 | self.bzr_identity = 'lp:%s' % os.path.basename(self.tree_dir) | 51 | self.bzr_identity = 'lp:%s' % os.path.basename(self.tree_dir) |
850 | 52 | self.project = MockLPProject() | 52 | self.project = MockLPProject() |
851 | 53 | self.web_link = 'http://code.launchpad.net/+branch/%s' % ( | ||
852 | 54 | os.path.basename(self.tree_dir),) | ||
853 | 53 | 55 | ||
854 | 54 | 56 | ||
855 | 55 | class cmd_mock(TarmacCommand): | 57 | class cmd_mock(TarmacCommand): |
856 | 56 | 58 | ||
857 | === modified file 'tarmac/tests/test_branch.py' | |||
858 | --- tarmac/tests/test_branch.py 2012-06-29 20:03:55 +0000 | |||
859 | +++ tarmac/tests/test_branch.py 2013-03-13 16:57:22 +0000 | |||
860 | @@ -38,7 +38,25 @@ | |||
861 | 38 | a_branch = branch.Branch.create(MockLPBranch(tree_dir), self.config) | 38 | a_branch = branch.Branch.create(MockLPBranch(tree_dir), self.config) |
862 | 39 | self.assertTrue(isinstance(a_branch, branch.Branch)) | 39 | self.assertTrue(isinstance(a_branch, branch.Branch)) |
863 | 40 | self.assertTrue(a_branch.lp_branch.bzr_identity is not None) | 40 | self.assertTrue(a_branch.lp_branch.bzr_identity is not None) |
865 | 41 | self.assertFalse(hasattr(a_branch, 'tree')) | 41 | self.remove_branch_config(tree_dir) |
866 | 42 | |||
867 | 43 | def test_create_missing_parent_dir(self): | ||
868 | 44 | '''Test the creation of a TarmacBranch instance in a path that does | ||
869 | 45 | not fully exist, with a tree''' | ||
870 | 46 | branch_name = 'test_branch' | ||
871 | 47 | parent_dir = os.path.join(self.TEST_ROOT, 'missing') | ||
872 | 48 | tree_dir = os.path.join(parent_dir, branch_name) | ||
873 | 49 | self.add_branch_config(tree_dir) | ||
874 | 50 | # Create the mock somewhere other than where the tarmac branch will be | ||
875 | 51 | # located. Keep it right under TEST_ROOT so the | ||
876 | 52 | # TarmacDirectoryFactory mocking will work. | ||
877 | 53 | mock = MockLPBranch(os.path.join(self.TEST_ROOT, branch_name)) | ||
878 | 54 | self.assertFalse(os.path.exists(parent_dir)) | ||
879 | 55 | a_branch = branch.Branch.create(mock, self.config, create_tree=True) | ||
880 | 56 | self.assertTrue(os.path.exists(parent_dir)) | ||
881 | 57 | self.assertTrue(isinstance(a_branch, branch.Branch)) | ||
882 | 58 | self.assertTrue(a_branch.lp_branch.bzr_identity is not None) | ||
883 | 59 | self.assertTrue(hasattr(a_branch, 'tree')) | ||
884 | 42 | self.remove_branch_config(tree_dir) | 60 | self.remove_branch_config(tree_dir) |
885 | 43 | 61 | ||
886 | 44 | def test_create_missing_parent_dir(self): | 62 | def test_create_missing_parent_dir(self): |
887 | @@ -66,19 +84,6 @@ | |||
888 | 66 | self.assertTrue(self.branch1.lp_branch.bzr_identity is not None) | 84 | self.assertTrue(self.branch1.lp_branch.bzr_identity is not None) |
889 | 67 | self.assertTrue(hasattr(self.branch1, 'tree')) | 85 | self.assertTrue(hasattr(self.branch1, 'tree')) |
890 | 68 | 86 | ||
891 | 69 | def test_merge_raises_exception_with_no_tree(self): | ||
892 | 70 | '''A merge on a branch with no tree will raise an exception.''' | ||
893 | 71 | branch3_dir = os.path.join(self.TEST_ROOT, 'branch3') | ||
894 | 72 | self.add_branch_config(branch3_dir) | ||
895 | 73 | branch3 = branch.Branch.create(MockLPBranch( | ||
896 | 74 | branch3_dir, source_branch=self.branch1.lp_branch), | ||
897 | 75 | self.config) | ||
898 | 76 | |||
899 | 77 | self.assertRaises( | ||
900 | 78 | AttributeError, branch3.merge, self.branch2) | ||
901 | 79 | self.remove_branch_config(branch3_dir) | ||
902 | 80 | shutil.rmtree(branch3_dir) | ||
903 | 81 | |||
904 | 82 | def test_merge_no_changes(self): | 87 | def test_merge_no_changes(self): |
905 | 83 | '''A merge on a branch with a tree will raise an exception if no | 88 | '''A merge on a branch with a tree will raise an exception if no |
906 | 84 | changes are present.''' | 89 | changes are present.''' |
907 | 85 | 90 | ||
908 | === modified file 'tarmac/tests/test_commands.py' | |||
909 | --- tarmac/tests/test_commands.py 2012-06-29 20:03:55 +0000 | |||
910 | +++ tarmac/tests/test_commands.py 2013-03-13 16:57:22 +0000 | |||
911 | @@ -1,5 +1,6 @@ | |||
912 | 1 | '''Tests for tarmac.bin.commands.py.''' | 1 | '''Tests for tarmac.bin.commands.py.''' |
913 | 2 | from cStringIO import StringIO | 2 | from cStringIO import StringIO |
914 | 3 | import logging | ||
915 | 3 | import os | 4 | import os |
916 | 4 | import shutil | 5 | import shutil |
917 | 5 | import sys | 6 | import sys |
918 | @@ -105,15 +106,20 @@ | |||
919 | 105 | display_name=self.branch2.lp_branch.bzr_identity, | 106 | display_name=self.branch2.lp_branch.bzr_identity, |
920 | 106 | name='source', | 107 | name='source', |
921 | 107 | revision_count=self.branch2.lp_branch.revision_count, | 108 | revision_count=self.branch2.lp_branch.revision_count, |
923 | 108 | landing_candidates=[]), | 109 | landing_candidates=[], |
924 | 110 | linked_bugs=[], | ||
925 | 111 | web_link=u'http://launchpad.net/branches/source'), | ||
926 | 109 | Thing( | 112 | Thing( |
927 | 110 | bzr_identity=self.branch1.lp_branch.bzr_identity, | 113 | bzr_identity=self.branch1.lp_branch.bzr_identity, |
928 | 111 | display_name=self.branch1.lp_branch.bzr_identity, | 114 | display_name=self.branch1.lp_branch.bzr_identity, |
929 | 112 | name='target', | 115 | name='target', |
930 | 113 | revision_count=self.branch1.lp_branch.revision_count, | 116 | revision_count=self.branch1.lp_branch.revision_count, |
932 | 114 | landing_candidates=None)] | 117 | landing_candidates=None, |
933 | 118 | linked_bugs=[], | ||
934 | 119 | web_link=u'http://launchpad.net/branches/target')] | ||
935 | 115 | self.proposals = [Thing( | 120 | self.proposals = [Thing( |
936 | 116 | self_link=u'http://api.edge.launchpad.net/devel/proposal0', | 121 | self_link=u'http://api.edge.launchpad.net/devel/proposal0', |
937 | 122 | web_link=u'http://edge.launchpad.net/proposal0', | ||
938 | 117 | queue_status=u'Needs Review', | 123 | queue_status=u'Needs Review', |
939 | 118 | commit_message=u'Commitable.', | 124 | commit_message=u'Commitable.', |
940 | 119 | source_branch=self.branches[0], | 125 | source_branch=self.branches[0], |
941 | @@ -128,6 +134,7 @@ | |||
942 | 128 | reviewer=Thing(display_name=u'Reviewer'))]), | 134 | reviewer=Thing(display_name=u'Reviewer'))]), |
943 | 129 | Thing( | 135 | Thing( |
944 | 130 | self_link=u'https://api.launchpad.net/1.0/proposal1', | 136 | self_link=u'https://api.launchpad.net/1.0/proposal1', |
945 | 137 | web_link=u'https://launchpad.net/proposal1', | ||
946 | 131 | queue_status=u'Approved', | 138 | queue_status=u'Approved', |
947 | 132 | commit_message=u'Commit this.', | 139 | commit_message=u'Commit this.', |
948 | 133 | source_branch=self.branches[0], | 140 | source_branch=self.branches[0], |
949 | @@ -137,6 +144,7 @@ | |||
950 | 137 | setStatus=self.lp_save, | 144 | setStatus=self.lp_save, |
951 | 138 | lp_save=self.lp_save, | 145 | lp_save=self.lp_save, |
952 | 139 | reviewed_revid=None, | 146 | reviewed_revid=None, |
953 | 147 | reviewer=Thing(name=u'reviewer', display_name=u'Reviewer'), | ||
954 | 140 | votes=[Thing( | 148 | votes=[Thing( |
955 | 141 | comment=Thing(vote=u'Approve'), | 149 | comment=Thing(vote=u'Approve'), |
956 | 142 | reviewer=Thing(display_name=u'Reviewer')), | 150 | reviewer=Thing(display_name=u'Reviewer')), |
957 | @@ -145,7 +153,8 @@ | |||
958 | 145 | reviewer=Thing(display_name=u'Reviewer2'))])] | 153 | reviewer=Thing(display_name=u'Reviewer2'))])] |
959 | 146 | self.branches[1].landing_candidates = self.proposals | 154 | self.branches[1].landing_candidates = self.proposals |
960 | 147 | 155 | ||
962 | 148 | self.launchpad = Thing(branches=Thing(getByUrl=self.getBranchByUrl)) | 156 | self.launchpad = Thing(branches=Thing(getByUrl=self.getBranchByUrl), |
963 | 157 | me=None) | ||
964 | 149 | self.error = None | 158 | self.error = None |
965 | 150 | registry = CommandRegistry(config=self.config) | 159 | registry = CommandRegistry(config=self.config) |
966 | 151 | registry.register_command('merge', commands.cmd_merge) | 160 | registry.register_command('merge', commands.cmd_merge) |
967 | @@ -167,6 +176,20 @@ | |||
968 | 167 | except IndexError: | 176 | except IndexError: |
969 | 168 | return None | 177 | return None |
970 | 169 | 178 | ||
971 | 179 | def test__get_branch_exists(self): | ||
972 | 180 | # _get_branch returns the lp_branch if it exists. | ||
973 | 181 | branch = commands._get_branch( | ||
974 | 182 | self.branch2.lp_branch.bzr_identity, | ||
975 | 183 | self.launchpad, | ||
976 | 184 | logging.getLogger('tarmac')) | ||
977 | 185 | self.assertIs(self.branches[0], branch) | ||
978 | 186 | |||
979 | 187 | def test__get_branch_doesnt_exist(self): | ||
980 | 188 | # _get_branch returns None if it doesn't exist. | ||
981 | 189 | branch = commands._get_branch( | ||
982 | 190 | 'doesntexist', self.launchpad, logging.getLogger('tarmac')) | ||
983 | 191 | self.assertIs(None, branch) | ||
984 | 192 | |||
985 | 170 | def test_run(self): | 193 | def test_run(self): |
986 | 171 | """Test that the merge command merges a branch successfully.""" | 194 | """Test that the merge command merges a branch successfully.""" |
987 | 172 | self.proposals[1].reviewed_revid = \ | 195 | self.proposals[1].reviewed_revid = \ |
988 | @@ -191,10 +214,12 @@ | |||
989 | 191 | 214 | ||
990 | 192 | def test_get_reviews(self): | 215 | def test_get_reviews(self): |
991 | 193 | """Test that the _get_reviews method gives the right lists.""" | 216 | """Test that the _get_reviews method gives the right lists.""" |
996 | 194 | self.assertEqual(self.command._get_reviews(self.proposals[0]), | 217 | self.assertEqual( |
997 | 195 | [u'Reviewer;Needs Fixing']) | 218 | commands._get_reviews(self.proposals[0]), |
998 | 196 | self.assertEqual(self.command._get_reviews(self.proposals[1]), | 219 | [u'Reviewer;Needs Fixing']) |
999 | 197 | [u'Reviewer;Approve', u'Reviewer2;Abstain']) | 220 | self.assertEqual( |
1000 | 221 | commands._get_reviews(self.proposals[1]), | ||
1001 | 222 | [u'Reviewer;Approve', u'Reviewer2;Abstain']) | ||
1002 | 198 | 223 | ||
1003 | 199 | def test_run_merge_url_substitution(self): | 224 | def test_run_merge_url_substitution(self): |
1004 | 200 | """Test that the merge urls get substituted correctly.""" | 225 | """Test that the merge urls get substituted correctly.""" |
1005 | @@ -212,6 +237,7 @@ | |||
1006 | 212 | # Make a new commit, approve the propsoal, merge, and verify | 237 | # Make a new commit, approve the propsoal, merge, and verify |
1007 | 213 | self.branch2.commit('New commit to merge.') | 238 | self.branch2.commit('New commit to merge.') |
1008 | 214 | self.proposals[0].queue_status = u'Approved' | 239 | self.proposals[0].queue_status = u'Approved' |
1009 | 240 | self.proposals[0].reviewer = Thing(name=u'reviewer') | ||
1010 | 215 | self.proposals[0].reviewed_revid = \ | 241 | self.proposals[0].reviewed_revid = \ |
1011 | 216 | self.branch2.bzr_branch.last_revision() | 242 | self.branch2.bzr_branch.last_revision() |
1012 | 217 | self.command.run(launchpad=self.launchpad) | 243 | self.command.run(launchpad=self.launchpad) |
1013 | @@ -242,6 +268,7 @@ | |||
1014 | 242 | branch3.lp_branch.landing_candidates = [] | 268 | branch3.lp_branch.landing_candidates = [] |
1015 | 243 | b3_proposal = Thing( | 269 | b3_proposal = Thing( |
1016 | 244 | self_link=u'http://api.edge.launchpad.net/devel/proposal3', | 270 | self_link=u'http://api.edge.launchpad.net/devel/proposal3', |
1017 | 271 | web_link=u'http://edge.launchpad.net/proposal3', | ||
1018 | 245 | queue_status=u'Work in Progress', | 272 | queue_status=u'Work in Progress', |
1019 | 246 | commit_message=u'Commitable.', | 273 | commit_message=u'Commitable.', |
1020 | 247 | source_branch=branch3.lp_branch, | 274 | source_branch=branch3.lp_branch, |
1021 | @@ -264,8 +291,9 @@ | |||
1022 | 264 | self.command.run(launchpad=self.launchpad) | 291 | self.command.run(launchpad=self.launchpad) |
1023 | 265 | shutil.rmtree(branch3_dir) | 292 | shutil.rmtree(branch3_dir) |
1024 | 266 | self.assertEqual(self.error.comment, | 293 | self.assertEqual(self.error.comment, |
1027 | 267 | u'The prerequisite lp:branch3 has not yet been ' | 294 | u'The prerequisite ' |
1028 | 268 | u'merged into lp:branch1.') | 295 | u'http://code.launchpad.net/+branch/branch3 has not ' |
1029 | 296 | u'yet been merged into http://launchpad.net/branches/target.') | ||
1030 | 269 | 297 | ||
1031 | 270 | def test_run_merge_with_unproposed_prerequisite_fails(self): | 298 | def test_run_merge_with_unproposed_prerequisite_fails(self): |
1032 | 271 | """Test that mereging a branch with an unmerged prerequisite fails.""" | 299 | """Test that mereging a branch with an unmerged prerequisite fails.""" |
1033 | @@ -289,6 +317,7 @@ | |||
1034 | 289 | branch3.lp_branch.landing_candidates = [] | 317 | branch3.lp_branch.landing_candidates = [] |
1035 | 290 | b3_proposal = Thing( | 318 | b3_proposal = Thing( |
1036 | 291 | self_link=u'http://api.edge.launchpad.net/devel/proposal3', | 319 | self_link=u'http://api.edge.launchpad.net/devel/proposal3', |
1037 | 320 | web_link=u'http://edge.launchpadnet/proposal3', | ||
1038 | 292 | queue_status=u'Work in Progress', | 321 | queue_status=u'Work in Progress', |
1039 | 293 | commit_message=u'Commitable.', | 322 | commit_message=u'Commitable.', |
1040 | 294 | source_branch=branch3.lp_branch, | 323 | source_branch=branch3.lp_branch, |
1041 | @@ -311,8 +340,9 @@ | |||
1042 | 311 | self.command.run(launchpad=self.launchpad) | 340 | self.command.run(launchpad=self.launchpad) |
1043 | 312 | shutil.rmtree(branch3_dir) | 341 | shutil.rmtree(branch3_dir) |
1044 | 313 | self.assertEqual(self.error.comment, | 342 | self.assertEqual(self.error.comment, |
1047 | 314 | u'No proposals found for merge of lp:branch3 ' | 343 | u'No proposals found for merge of ' |
1048 | 315 | u'into lp:branch1.') | 344 | u'http://code.launchpad.net/+branch/branch3 ' |
1049 | 345 | u'into http://launchpad.net/branches/target.') | ||
1050 | 316 | 346 | ||
1051 | 317 | def test_run_merge_with_prerequisite_with_multiple_proposals_fails(self): | 347 | def test_run_merge_with_prerequisite_with_multiple_proposals_fails(self): |
1052 | 318 | """Test that mereging a branch with an unmerged prerequisite fails.""" | 348 | """Test that mereging a branch with an unmerged prerequisite fails.""" |
1053 | @@ -336,6 +366,7 @@ | |||
1054 | 336 | branch3.lp_branch.landing_candidates = [] | 366 | branch3.lp_branch.landing_candidates = [] |
1055 | 337 | b3_proposal = Thing( | 367 | b3_proposal = Thing( |
1056 | 338 | self_link=u'http://api.edge.launchpad.net/devel/proposal3', | 368 | self_link=u'http://api.edge.launchpad.net/devel/proposal3', |
1057 | 369 | web_link=u'http://edge.launchpadnet/proposal3', | ||
1058 | 339 | queue_status=u'Work in Progress', | 370 | queue_status=u'Work in Progress', |
1059 | 340 | commit_message=u'Commitable.', | 371 | commit_message=u'Commitable.', |
1060 | 341 | source_branch=branch3.lp_branch, | 372 | source_branch=branch3.lp_branch, |
1061 | @@ -363,5 +394,6 @@ | |||
1062 | 363 | shutil.rmtree(branch3_dir) | 394 | shutil.rmtree(branch3_dir) |
1063 | 364 | self.assertEqual(self.error.comment, | 395 | self.assertEqual(self.error.comment, |
1064 | 365 | u'More than one proposal found for merge of ' | 396 | u'More than one proposal found for merge of ' |
1067 | 366 | u'lp:branch3 into lp:branch1, which is not ' | 397 | u'http://code.launchpad.net/+branch/branch3 into ' |
1068 | 367 | u'Superseded.') | 398 | u'http://launchpad.net/branches/target, which ' |
1069 | 399 | u'is not Superseded.') |