Merge lp://staging/~abentley/bzr-pipeline/lp-submit-hook into lp://staging/bzr-pipeline
- lp-submit-hook
- Merge into trunk
Proposed by
Aaron Bentley
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp://staging/~abentley/bzr-pipeline/lp-submit-hook |
Merge into: | lp://staging/bzr-pipeline |
Diff against target: |
385 lines (+29/-326) 3 files modified
__init__.py (+29/-1) commands.py (+0/-38) lp_submit.py (+0/-287) |
To merge this branch: | bzr merge lp://staging/~abentley/bzr-pipeline/lp-submit-hook |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Aaron Bentley | code | Pending | |
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Aaron Bentley (abentley) wrote : | # |
- 154. By Aaron Bentley
-
Update to match changes in lpsubmit branch.
- 155. By Aaron Bentley
-
Merged trunk into lp-submit-hook.
- 156. By Aaron Bentley
-
Merged trunk into lp-submit-hook.
- 157. By Aaron Bentley
-
Update support for lp-propose command.
- 158. By Aaron Bentley
-
Merge lower pipe.
- 159. By Aaron Bentley
-
Remove lp_submit class.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '__init__.py' |
2 | --- __init__.py 2009-11-20 14:13:57 +0000 |
3 | +++ __init__.py 2010-02-20 18:43:16 +0000 |
4 | @@ -131,7 +131,6 @@ |
5 | register('cmd_merge') |
6 | register('cmd_reconfigure_pipeline') |
7 | register('cmd_sync_pipeline') |
8 | -register('cmd_lp_submit') |
9 | plugin_cmds.register_lazy('cmd_import_loom', [], |
10 | 'bzrlib.plugins.pipeline.loom') |
11 | |
12 | @@ -157,6 +156,35 @@ |
13 | directories.register(':', PipeAliasDirectory, |
14 | 'Easy access to remembered branch locations') |
15 | |
16 | +from bzrlib.plugins.launchpad import lp_api |
17 | +try: |
18 | + from bzrlib.plugins.launchpad import lp_propose |
19 | +except ImportError: |
20 | + pass |
21 | +else: |
22 | + def get_prerequisite_from_pipe(hook_params): |
23 | + from bzrlib.plugins.pipeline.pipeline import PipeManager |
24 | + source_branch = hook_params['source_branch'] |
25 | + launchpad = hook_params['launchpad'] |
26 | + manager = PipeManager(source_branch.bzr) |
27 | + prev_pipe = manager.get_prev_pipe() |
28 | + if prev_pipe is not None: |
29 | + prerequisite_branch = lp_api.LaunchpadBranch.from_bzr(launchpad, |
30 | + prev_pipe) |
31 | + if (prerequisite_branch.lp.bzr_identity |
32 | + == hook_params['target_branch'].lp.bzr_identity): |
33 | + prerequisite_branch = None |
34 | + else: |
35 | + prerequisite_branch = None |
36 | + return prerequisite_branch |
37 | + |
38 | + |
39 | + # XXX: When we move the pipeline command, we should make sure it |
40 | + # constructs Submitter correctly. |
41 | + lp_propose.Proposer.hooks.install_named_hook( |
42 | + 'get_prerequisite', get_prerequisite_from_pipe, |
43 | + 'Get the prerequisite from the pipeline') |
44 | + |
45 | |
46 | def test_suite(): |
47 | from bzrlib.tests.TestUtil import TestLoader, TestSuite |
48 | |
49 | === modified file 'commands.py' |
50 | --- commands.py 2010-02-04 12:49:34 +0000 |
51 | +++ commands.py 2010-02-20 18:43:16 +0000 |
52 | @@ -369,41 +369,3 @@ |
53 | raise errors.BzrCommandError( |
54 | 'No location specified and none remembered.') |
55 | manager.sync_pipeline(location, remote) |
56 | - |
57 | - |
58 | -class cmd_lp_submit(PipeCommand): |
59 | - """Submit the specified pipe to Launchpad.""" |
60 | - |
61 | - takes_options = [Option('staging', |
62 | - help='Propose the merge on staging.'), |
63 | - Option('message', short_name='m', type=unicode, |
64 | - help='Commit message.'), |
65 | - ListOption('review', short_name='R', type=unicode, |
66 | - help='Requested reviewer and optional type.')] |
67 | - |
68 | - takes_args = ['submit_branch?'] |
69 | - |
70 | - def run(self, submit_branch=None, review=None, staging=False, |
71 | - message=None, open_webpage=True): |
72 | - from bzrlib.plugins.pipeline import lp_submit |
73 | - checkout, manager = self._get_checkout_manager(checkout_optional=True, |
74 | - allow_tree=True) |
75 | - if review is None: |
76 | - reviews = None |
77 | - else: |
78 | - reviews = [] |
79 | - for review in review: |
80 | - if '=' in review: |
81 | - reviews.append(review.split('=', 2)) |
82 | - else: |
83 | - reviews.append((review, '')) |
84 | - if submit_branch is None: |
85 | - submit_branch = manager.storage.branch.get_submit_branch() |
86 | - if submit_branch is None: |
87 | - target = None |
88 | - else: |
89 | - target = Branch.open(submit_branch) |
90 | - submitter = lp_submit.Submitter(checkout, manager, target, message, |
91 | - reviews, staging) |
92 | - submitter.check_submission() |
93 | - submitter.submit(open_webpage) |
94 | |
95 | === removed file 'lp_submit.py' |
96 | --- lp_submit.py 2010-02-04 12:49:34 +0000 |
97 | +++ lp_submit.py 1970-01-01 00:00:00 +0000 |
98 | @@ -1,287 +0,0 @@ |
99 | -import errno, re, webbrowser |
100 | - |
101 | -from bzrlib import ( |
102 | - branch, |
103 | - config, |
104 | - errors, |
105 | - msgeditor, |
106 | - osutils, |
107 | - trace, |
108 | - transport, |
109 | -) |
110 | - |
111 | -class NoLaunchpadLib(errors.BzrCommandError): |
112 | - |
113 | - _fmt = "LaunchpadLib must be installed for this operation." |
114 | - |
115 | - |
116 | -try: |
117 | - from launchpadlib import ( |
118 | - credentials, |
119 | - launchpad, |
120 | - ) |
121 | -except ImportError: |
122 | - raise NoLaunchpadLib() |
123 | - |
124 | -from lazr.restfulclient import errors as restful_errors |
125 | - |
126 | - |
127 | -_lp = None |
128 | -def lp(staging=False): |
129 | - if staging: |
130 | - service_root = launchpad.STAGING_SERVICE_ROOT |
131 | - else: |
132 | - service_root = launchpad.EDGE_SERVICE_ROOT |
133 | - global _lp |
134 | - if _lp is not None: |
135 | - return _lp |
136 | - cachedir = osutils.pathjoin(config.config_dir(), |
137 | - 'pipeline-cachedir') |
138 | - credentials_path = osutils.pathjoin(config.config_dir(), |
139 | - 'pipeline-credentials') |
140 | - if staging: |
141 | - credentials_path += '-staging' |
142 | - try: |
143 | - credentials_file = open(credentials_path, 'r') |
144 | - except IOError, e: |
145 | - if e.errno != errno.ENOENT: |
146 | - raise |
147 | - _lp = launchpad.Launchpad.get_token_and_login('bzr-pipeline', |
148 | - service_root, cachedir) |
149 | - _lp.credentials.save(open(credentials_path, 'w')) |
150 | - else: |
151 | - creds = credentials.Credentials() |
152 | - creds.load(open(credentials_path, 'r')) |
153 | - _lp = launchpad.Launchpad(creds, service_root, cachedir) |
154 | - return _lp |
155 | - |
156 | - |
157 | -class MegaBranch(object): |
158 | - |
159 | - def __init__(self, lp_branch, bzr_url, bzr_branch=None, check_update=True): |
160 | - self.bzr_url = bzr_url |
161 | - self._bzr = bzr_branch |
162 | - self._push_bzr = None |
163 | - self._check_update = False |
164 | - self.lp = lp_branch |
165 | - |
166 | - @property |
167 | - def bzr(self): |
168 | - if self._bzr is None: |
169 | - self._bzr = branch.Branch.open(self.bzr_url) |
170 | - return self._bzr |
171 | - |
172 | - @property |
173 | - def push_bzr(self): |
174 | - if self._push_bzr is None: |
175 | - self._push_bzr = branch.Branch.open(self.lp.bzr_identity) |
176 | - return self._push_bzr |
177 | - |
178 | - @staticmethod |
179 | - def plausible_launchpad_url(url): |
180 | - if url is None: |
181 | - return False |
182 | - if url.startswith('lp:'): |
183 | - return True |
184 | - regex = re.compile('([a-z]*\+)*(bzr\+ssh|http)' |
185 | - '://bazaar.*.launchpad.net') |
186 | - return bool(regex.match(url)) |
187 | - |
188 | - @staticmethod |
189 | - def candidate_urls(bzr_branch): |
190 | - url = bzr_branch.get_public_branch() |
191 | - if url is not None: |
192 | - yield url |
193 | - url = bzr_branch.get_push_location() |
194 | - if url is not None: |
195 | - yield url |
196 | - yield bzr_branch.base |
197 | - |
198 | - @staticmethod |
199 | - def tweak_url(url, staging): |
200 | - if not staging: |
201 | - return url |
202 | - if url is None: |
203 | - return None |
204 | - return url.replace('bazaar.launchpad.net', |
205 | - 'bazaar.staging.launchpad.net') |
206 | - |
207 | - @classmethod |
208 | - def from_bzr(cls, bzr_branch, staging=False): |
209 | - check_update = True |
210 | - for url in cls.candidate_urls(bzr_branch): |
211 | - url = cls.tweak_url(url, staging) |
212 | - if not cls.plausible_launchpad_url(url): |
213 | - continue |
214 | - lp_branch = lp(staging).branches.getByUrl(url=url) |
215 | - if lp_branch is not None: |
216 | - break |
217 | - else: |
218 | - lp_branch = cls.create_now(bzr_branch, staging) |
219 | - check_update = False |
220 | - return cls(lp_branch, bzr_branch.base, bzr_branch, check_update) |
221 | - |
222 | - @classmethod |
223 | - def create_now(cls, bzr_branch, staging): |
224 | - url = cls.tweak_url(bzr_branch.get_push_location(), staging) |
225 | - if not cls.plausible_launchpad_url(url): |
226 | - raise errors.BzrError('%s is not registered on Launchpad' % |
227 | - bzr_branch.base) |
228 | - bzr_branch.create_clone_on_transport(transport.get_transport(url)) |
229 | - lp_branch = lp(staging).branches.getByUrl(url=url) |
230 | - if lp_branch is None: |
231 | - raise errors.BzrError('%s is not registered on Launchpad' % url) |
232 | - return lp_branch |
233 | - |
234 | - @classmethod |
235 | - def from_dev_focus(cls, lp_branch): |
236 | - if lp_branch.project is None: |
237 | - raise errors.BzrError('%s has no product.' % |
238 | - lp_branch.bzr_identity) |
239 | - dev_focus = lp_branch.project.development_focus.branch |
240 | - if dev_focus is None: |
241 | - raise errors.BzrError('%s has no development focus.' % |
242 | - lp_branch.bzr_identity) |
243 | - return cls(dev_focus, dev_focus.bzr_identity) |
244 | - |
245 | - def update_lp(self): |
246 | - if not self._check_update: |
247 | - return |
248 | - self.bzr.lock_read() |
249 | - try: |
250 | - if self.lp.last_scanned_id is not None: |
251 | - if self.bzr.last_revision() == self.lp.last_scanned_id: |
252 | - trace.note('%s is already up-to-date.' % |
253 | - self.lp.bzr_identity) |
254 | - return |
255 | - graph = self.bzr.repository.get_graph() |
256 | - if not graph.is_ancestor(self.bzr.last_revision(), |
257 | - self.lp.last_scanned_id): |
258 | - raise errors.DivergedBranches(self.bzr, self.push_bzr) |
259 | - trace.note('Pushing to %s' % self.lp.bzr_identity) |
260 | - self.bzr.push(self.push_bzr) |
261 | - finally: |
262 | - self.bzr.unlock() |
263 | - |
264 | - def find_lca_tree(self, other): |
265 | - graph = self.bzr.repository.get_graph(other.bzr.repository) |
266 | - lca = graph.find_unique_lca(self.bzr.last_revision(), |
267 | - other.bzr.last_revision()) |
268 | - return self.bzr.repository.revision_tree(lca) |
269 | - |
270 | - |
271 | -class Submitter(object): |
272 | - |
273 | - def __init__(self, tree, manager, target_branch, message, reviews, |
274 | - staging=False): |
275 | - self.tree = tree |
276 | - self.manager = manager |
277 | - self.staging = staging |
278 | - self.source_branch = MegaBranch.from_bzr(self.manager.storage.branch, |
279 | - self.staging) |
280 | - if target_branch is None: |
281 | - self.target_branch = MegaBranch.from_dev_focus( |
282 | - self.source_branch.lp) |
283 | - else: |
284 | - self.target_branch = MegaBranch.from_bzr(target_branch, |
285 | - self.staging) |
286 | - self.commit_message = message |
287 | - if reviews == []: |
288 | - target_reviewer = self.target_branch.lp.reviewer |
289 | - if target_reviewer is None: |
290 | - raise errors.BzrCommandError('No reviewer specified') |
291 | - self.reviews = [(target_reviewer, '')] |
292 | - else: |
293 | - self.reviews = [(lp(self.staging).people[reviewer], review_type) |
294 | - for reviewer, review_type in |
295 | - reviews] |
296 | - |
297 | - def get_comment(self, prerequisite_branch): |
298 | - info = ["Source: %s\n" % self.source_branch.lp.bzr_identity] |
299 | - info.append("Target: %s\n" % self.target_branch.lp.bzr_identity) |
300 | - if prerequisite_branch is not None: |
301 | - info.append("Prereq: %s\n" % prerequisite_branch.lp.bzr_identity) |
302 | - for rdata in self.reviews: |
303 | - uniquename = "%s (%s)" % (rdata[0].display_name, rdata[0].name) |
304 | - info.append('Reviewer: %s, type "%s"\n' % (uniquename, rdata[1])) |
305 | - self.source_branch.bzr.lock_read() |
306 | - try: |
307 | - self.target_branch.bzr.lock_read() |
308 | - try: |
309 | - body = self.try_get_body() |
310 | - finally: |
311 | - self.target_branch.bzr.unlock() |
312 | - finally: |
313 | - self.source_branch.bzr.unlock() |
314 | - initial_comment = msgeditor.edit_commit_message(''.join(info), |
315 | - start_message=body) |
316 | - return initial_comment.strip().encode('utf-8') |
317 | - |
318 | - def try_get_body(self): |
319 | - try: |
320 | - from bzrlib.plugins.lpreview_body.body_callback import ( |
321 | - get_body, |
322 | - modified_files, |
323 | - ) |
324 | - except ImportError: |
325 | - return '' |
326 | - def list_modified_files(): |
327 | - lca_tree = self.source_branch.find_lca_tree( |
328 | - self.target_branch) |
329 | - source_tree = self.source_branch.bzr.basis_tree() |
330 | - files = modified_files(lca_tree, source_tree) |
331 | - return list(files) |
332 | - target_loc = ('bzr+ssh://bazaar.launchpad.net/%s' % |
333 | - self.target_branch.lp.unique_name) |
334 | - return get_body(self.tree, target_loc, list_modified_files, '') |
335 | - |
336 | - def check_submission(self): |
337 | - if self.source_branch.lp.self_link == self.target_branch.lp.self_link: |
338 | - raise errors.BzrCommandError( |
339 | - 'Source and target branches must be different.') |
340 | - for mp in self.source_branch.lp.landing_targets: |
341 | - if mp.queue_status in ('Merged', 'Rejected'): |
342 | - continue |
343 | - if mp.target_branch.self_link == self.target_branch.lp.self_link: |
344 | - raise errors.BzrCommandError( |
345 | - 'There is already a branch merge proposal: %s' % |
346 | - canonical_url(mp)) |
347 | - |
348 | - def submit(self, open_webpage): |
349 | - prev_pipe = self.manager.get_prev_pipe() |
350 | - if prev_pipe is not None: |
351 | - prerequisite_branch = MegaBranch.from_bzr(prev_pipe) |
352 | - else: |
353 | - prerequisite_branch = None |
354 | - self.source_branch.update_lp() |
355 | - if prerequisite_branch is not None: |
356 | - prerequisite_branch.update_lp() |
357 | - if prerequisite_branch is None: |
358 | - prereq = None |
359 | - else: |
360 | - prereq = prerequisite_branch.lp |
361 | - reviewers = [] |
362 | - review_types = [] |
363 | - for reviewer, review_type in self.reviews: |
364 | - review_types.append(review_type) |
365 | - reviewers.append(reviewer.self_link) |
366 | - initial_comment = self.get_comment(prerequisite_branch) |
367 | - try: |
368 | - mp = self.source_branch.lp.createMergeProposal( |
369 | - target_branch=self.target_branch.lp, |
370 | - prerequisite_branch=prereq, initial_comment=initial_comment, |
371 | - commit_message=self.commit_message, reviewers=reviewers, |
372 | - review_types=review_types) |
373 | - except restful_errors.HTTPError, e: |
374 | - for line in e.content.splitlines(): |
375 | - if line.startswith('Traceback (most recent call last):'): |
376 | - break |
377 | - print line |
378 | - else: |
379 | - if open_webpage: |
380 | - webbrowser.open(canonical_url(mp)) |
381 | - |
382 | -def canonical_url(object): |
383 | - url = object.self_link.replace('https://api.', 'https://code.') |
384 | - return url.replace('/beta/', '/') |
385 | - |
Land this branch once bzr-core provides lp-submit.