Merge lp:~jelmer/launchpad/bzr-code-imports-ui into lp:launchpad/db-devel

Proposed by Jelmer Vernooij
Status: Superseded
Proposed branch: lp:~jelmer/launchpad/bzr-code-imports-ui
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~jelmer/launchpad/bzr-code-imports
Diff against target: 720 lines (+88/-339)
11 files modified
lib/lp/code/browser/branch.py (+4/-47)
lib/lp/code/browser/codeimport.py (+23/-5)
lib/lp/code/browser/tests/test_branch.py (+3/-13)
lib/lp/code/enums.py (+2/-2)
lib/lp/code/stories/branches/xx-creating-branches.txt (+0/-179)
lib/lp/code/stories/codeimport/xx-create-codeimport.txt (+1/-0)
lib/lp/code/templates/branch-form-macros.pt (+0/-42)
lib/lp/code/templates/branch-import-details.pt (+6/-0)
lib/lp/code/templates/codeimport-new.pt (+14/-0)
lib/lp/registry/browser/productseries.py (+34/-50)
lib/lp/registry/templates/productseries-codesummary.pt (+1/-1)
To merge this branch: bzr merge lp:~jelmer/launchpad/bzr-code-imports-ui
Reviewer Review Type Date Requested Status
Brad Crittenden (community) Approve
Michael Hudson-Doyle code Pending
Review via email: mp+65684@code.launchpad.net

This proposal has been superseded by a proposal from 2011-08-27.

Commit message

Remove the ability to register mirrored branches; instead, make it possible to register bzr code imports in the UI.

Description of the change

Make it possible to create new Bazaar code imports in the code import UI, disable the ability to create mirror branches.

This removes the user-visible distinction in the web UI between code imports (syncing from remote non-bzr branches) and mirrors (syncing from remote bzr branches), which should make the UI a bit easier to comprehend.

This doesn't migrate any of the existing mirror branches to be code imports, but that would be the next logical step.

It also doesn't remove the ability to create old-style mirrors via the API.

To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

+ bzr_branch_url = URIField(
...
+ allow_query=False, # Query makes no sense in Mercurial

:-)

Otherwise, well it looks fine. It's great to see so much code being deleted. I don't have the time to do a line by line review, and I don't have the Launchpad dev environment set up on my new laptop so I can't play around with it. I worry that there are probably more places that implicitly assume import branches are foreign in some way, but maybe not (also approximately noone uses mirror branches any more aiui).

It would be SO GREAT if we could get rid of the puller after this. I think this requires having some kind of token mechanism that grants the holder of the token the ability to read and write to a particular branch (and read any stacked on branches I guess) via the usual codehosting access. But that can wait :)

Revision history for this message
Brad Crittenden (bac) wrote :

Hi Jelmer,

Thanks for trying to clean up this UI. It has always been a bit of a mess. I think your simplifications are good.

Twice in browser/codeimport.py we have a list of valid RCS types. It would be nice to define those one place rather than using the same set of literals twice such as:

@@ -576,7 +592,8 @@
151 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
152 RevisionControlSystems.BZR_SVN,
153 RevisionControlSystems.GIT,
154 - RevisionControlSystems.HG):
155 + RevisionControlSystems.HG,
156 + RevisionControlSystems.BZR):

As with all UI changes, screenshot of before and after as well as clear instructions on how to exercise the changes makes for speedier review.

review: Approve
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Another direction the UI could go of course would be to not overly
distinguish between the various import types and just let the foreign
branch plugins figure it all out. This feels like it would make for a
simpler user experience, but it would also certainly be a big change. I
wonder if it might seem a bit too magical?

Cheers,
mwh

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

On 26/06/11 23:26, Michael Hudson-Doyle wrote:
> Another direction the UI could go of course would be to not overly
> distinguish between the various import types and just let the foreign
> branch plugins figure it all out. This feels like it would make for a
> simpler user experience, but it would also certainly be a big change. I
> wonder if it might seem a bit too magical?
That's an interesting idea. "bzr branch" has that magic to figure out
what format the source branch is in too, and it seems to work fine
there. One way to do this would be to add a "AUTO" value to
RevisionControlSystems that simply tries the probers registered for each
of the CodeImportWorkers. That would (I think) be a minimal amount of
work, though it would of course only work for those importers that
actually have probers (BZR, BZR_SVN, GIT, HG).

CVS would still need to be special, but I guess there's no way around
that anyway, as it requires a root and a module to be specified.

Cheers,

Jelmer

Revision history for this message
Robert Collins (lifeless) wrote :

On Mon, Jun 27, 2011 at 11:04 AM, Jelmer Vernooij <email address hidden> wrote:
> CVS would still need to be special, but I guess there's no way around
> that anyway, as it requires a root and a module to be specified.

see config-manager for a url format for cvs that includes the root and module.

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

On Mon, 27 Jun 2011 00:01:14 +0200, Jelmer Vernooij <email address hidden> wrote:
> On 26/06/11 23:26, Michael Hudson-Doyle wrote:
> > Another direction the UI could go of course would be to not overly
> > distinguish between the various import types and just let the foreign
> > branch plugins figure it all out. This feels like it would make for a
> > simpler user experience, but it would also certainly be a big change. I
> > wonder if it might seem a bit too magical?
> That's an interesting idea. "bzr branch" has that magic to figure out
> what format the source branch is in too, and it seems to work fine
> there. One way to do this would be to add a "AUTO" value to
> RevisionControlSystems that simply tries the probers registered for each
> of the CodeImportWorkers. That would (I think) be a minimal amount of
> work, though it would of course only work for those importers that
> actually have probers (BZR, BZR_SVN, GIT, HG).

Yeah, that would be pretty neat, although presenting bzr/svn/git/hg/auto
to the user wouldn't really be progress :)

> CVS would still need to be special, but I guess there's no way around
> that anyway, as it requires a root and a module to be specified.

Apparently config-manager defines a URL format for CVS we could borrow.
But that would be more useful for cleaning up the internals, I don't
think we could get away with expecting the user to know about it.

Cheers,
mwh

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Hi Brad,

On 24/06/11 20:15, Brad Crittenden wrote:
> Thanks for trying to clean up this UI. It has always been a bit of a mess. I think your simplifications are good.
>
> Twice in browser/codeimport.py we have a list of valid RCS types. It would be nice to define those one place rather than using the same set of literals twice such as:
>
> @@ -576,7 +592,8 @@
> 151 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
> 152 RevisionControlSystems.BZR_SVN,
> 153 RevisionControlSystems.GIT,
> 154 - RevisionControlSystems.HG):
> 155 + RevisionControlSystems.HG,
> 156 + RevisionControlSystems.BZR):
Updated to use a single list.
>
>
> As with all UI changes, screenshot of before and after as well as clear instructions on how to exercise the changes makes for speedier review.
Thanks for the review! I'll make sure to include screenshots next time.

Cheers,

Jelmer

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

On Sun, 2011-06-26 at 23:19 +0000, Robert Collins wrote:
> On Mon, Jun 27, 2011 at 11:04 AM, Jelmer Vernooij <email address hidden> wrote:
> > CVS would still need to be special, but I guess there's no way around
> > that anyway, as it requires a root and a module to be specified.
> see config-manager for a url format for cvs that includes the root and module.
Even if we do that, do we want users to have to know about that
particular format?

Cheers,

Jelmer

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

On Mon, 25 Jul 2011 13:21:44 -0000, Jelmer Vernooij <email address hidden> wrote:
> On Sun, 2011-06-26 at 23:19 +0000, Robert Collins wrote:
> > On Mon, Jun 27, 2011 at 11:04 AM, Jelmer Vernooij <email address hidden> wrote:
> > > CVS would still need to be special, but I guess there's no way around
> > > that anyway, as it requires a root and a module to be specified.
> > see config-manager for a url format for cvs that includes the root and module.
> Even if we do that, do we want users to have to know about that
> particular format?

No.

Cheers,
mwh

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/branch.py'
2--- lib/lp/code/browser/branch.py 2011-08-24 11:52:43 +0000
3+++ lib/lp/code/browser/branch.py 2011-08-25 10:47:30 +0000
4@@ -1120,15 +1120,12 @@
5
6 class schema(Interface):
7 use_template(
8- IBranch, include=['owner', 'name', 'url', 'lifecycle_status'])
9- branch_type = copy_field(
10- IBranch['branch_type'], vocabulary=UICreatableBranchType)
11+ IBranch, include=['owner', 'name', 'lifecycle_status'])
12
13 for_input = True
14- field_names = ['owner', 'name', 'branch_type', 'url', 'lifecycle_status']
15+ field_names = ['owner', 'name', 'lifecycle_status']
16
17 branch = None
18- custom_widget('branch_type', LaunchpadRadioWidgetWithDescription)
19 custom_widget('lifecycle_status', LaunchpadRadioWidgetWithDescription)
20
21 initial_focus_widget = 'name'
22@@ -1157,27 +1154,17 @@
23 """
24 return IPerson(self.context, self.user)
25
26- def showOptionalMarker(self, field_name):
27- """Don't show the optional marker for url."""
28- if field_name == 'url':
29- return False
30- else:
31- return LaunchpadFormView.showOptionalMarker(self, field_name)
32-
33 @action('Register Branch', name='add')
34 def add_action(self, action, data):
35 """Handle a request to create a new branch for this product."""
36 try:
37- ui_branch_type = data['branch_type']
38 namespace = self.target.getNamespace(data['owner'])
39 self.branch = namespace.createBranch(
40- branch_type=BranchType.items[ui_branch_type.name],
41+ branch_type=BranchType.HOSTED,
42 name=data['name'],
43 registrant=self.user,
44- url=data.get('url'),
45+ url=None,
46 lifecycle_status=data['lifecycle_status'])
47- if self.branch.branch_type == BranchType.MIRRORED:
48- self.branch.requestMirror()
49 except BranchCreationForbidden:
50 self.addError(
51 "You are not allowed to create branches in %s." %
52@@ -1195,36 +1182,6 @@
53 'owner',
54 'You are not a member of %s' % owner.displayname)
55
56- branch_type = data.get('branch_type')
57- # If branch_type failed to validate, then the rest of the method
58- # doesn't make any sense.
59- if branch_type is None:
60- return
61-
62- # If the branch is a MIRRORED branch, then the url
63- # must be supplied, and if HOSTED the url must *not*
64- # be supplied.
65- url = data.get('url')
66- if branch_type == UICreatableBranchType.MIRRORED:
67- if url is None:
68- # If the url is not set due to url validation errors,
69- # there will be an error set for it.
70- error = self.getFieldError('url')
71- if not error:
72- self.setFieldError(
73- 'url',
74- 'Branch URLs are required for Mirrored branches.')
75- elif branch_type == UICreatableBranchType.HOSTED:
76- if url is not None:
77- self.setFieldError(
78- 'url',
79- 'Branch URLs cannot be set for Hosted branches.')
80- elif branch_type == UICreatableBranchType.REMOTE:
81- # A remote location can, but doesn't have to be set.
82- pass
83- else:
84- raise AssertionError('Unknown branch type')
85-
86 @property
87 def cancel_url(self):
88 return canonical_url(self.context)
89
90=== modified file 'lib/lp/code/browser/codeimport.py'
91--- lib/lp/code/browser/codeimport.py 2011-05-27 21:12:25 +0000
92+++ lib/lp/code/browser/codeimport.py 2011-08-25 10:47:30 +0000
93@@ -271,6 +271,16 @@
94 allow_fragment=False, # Fragment makes no sense in Mercurial
95 trailing_slash=False) # See http://launchpad.net/bugs/56357.
96
97+ bzr_branch_url = URIField(
98+ title=_("Bazaar branch URL"), required=False,
99+ description=_("The URL of the Bazaar branch."),
100+ allowed_schemes=["http", "https", "bzr"],
101+ allow_userinfo=False, # Only anonymous access is supported.
102+ allow_port=True,
103+ allow_query=False, # Query makes no sense in Mercurial
104+ allow_fragment=False, # Fragment makes no sense in Mercurial
105+ trailing_slash=False)
106+
107 branch_name = copy_field(
108 IBranch['name'],
109 __name__='branch_name',
110@@ -346,9 +356,9 @@
111 # display them separately in the form.
112 soup = BeautifulSoup(self.widgets['rcs_type']())
113 fields = soup.findAll('input')
114- [cvs_button, svn_button, git_button, hg_button, empty_marker] = [
115- field for field in fields
116- if field.get('value') in ['CVS', 'BZR_SVN', 'GIT', 'HG', '1']]
117+ [cvs_button, svn_button, git_button, hg_button, bzr_button,
118+ empty_marker] = [field for field in fields
119+ if field.get('value') in ['CVS', 'BZR_SVN', 'GIT', 'HG', 'BZR', '1']]
120 cvs_button['onclick'] = 'updateWidgets()'
121 svn_button['onclick'] = 'updateWidgets()'
122 git_button['onclick'] = 'updateWidgets()'
123@@ -358,6 +368,7 @@
124 self.rcs_type_svn = str(svn_button)
125 self.rcs_type_git = str(git_button)
126 self.rcs_type_hg = str(hg_button)
127+ self.rcs_type_bzr = str(bzr_button)
128 self.rcs_type_emptymarker = str(empty_marker)
129
130 def _getImportLocation(self, data):
131@@ -371,6 +382,8 @@
132 return None, None, data.get('git_repo_url')
133 elif rcs_type == RevisionControlSystems.HG:
134 return None, None, data.get('hg_repo_url')
135+ elif rcs_type == RevisionControlSystems.BZR:
136+ return None, None, data.get('bzr_branch_url')
137 else:
138 raise AssertionError(
139 'Unexpected revision control type %r.' % rcs_type)
140@@ -485,6 +498,9 @@
141 elif rcs_type == RevisionControlSystems.HG:
142 self._validateURL(
143 data.get('hg_repo_url'), field_name='hg_repo_url')
144+ elif rcs_type == RevisionControlSystems.BZR:
145+ self._validateURL(
146+ data.get('bzr_branch_url'), field_name='bzr_branch_url')
147 else:
148 raise AssertionError(
149 'Unexpected revision control type %r.' % rcs_type)
150@@ -576,7 +592,8 @@
151 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
152 RevisionControlSystems.BZR_SVN,
153 RevisionControlSystems.GIT,
154- RevisionControlSystems.HG):
155+ RevisionControlSystems.HG,
156+ RevisionControlSystems.BZR):
157 self.form_fields = self.form_fields.omit(
158 'cvs_root', 'cvs_module')
159 else:
160@@ -611,7 +628,8 @@
161 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
162 RevisionControlSystems.BZR_SVN,
163 RevisionControlSystems.GIT,
164- RevisionControlSystems.HG):
165+ RevisionControlSystems.HG,
166+ RevisionControlSystems.BZR):
167 self._validateURL(data.get('url'), self.code_import)
168 else:
169 raise AssertionError('Unknown rcs_type for code import.')
170
171=== modified file 'lib/lp/code/browser/tests/test_branch.py'
172--- lib/lp/code/browser/tests/test_branch.py 2011-08-22 18:10:47 +0000
173+++ lib/lp/code/browser/tests/test_branch.py 2011-08-25 10:47:30 +0000
174@@ -191,8 +191,8 @@
175 "This is a short error message.",
176 branch_view.mirror_status_message)
177
178- def testBranchAddRequestsMirror(self):
179- """Registering a mirrored branch requests a mirror."""
180+ def testBranchAddRequests(self):
181+ """Registering a branch that requests a mirror."""
182 arbitrary_person = self.factory.makePerson()
183 arbitrary_product = self.factory.makeProduct()
184 login_person(arbitrary_person)
185@@ -200,9 +200,8 @@
186 add_view = BranchAddView(arbitrary_person, self.request)
187 add_view.initialize()
188 data = {
189- 'branch_type': BranchType.MIRRORED,
190+ 'branch_type': BranchType.HOSTED,
191 'name': 'some-branch',
192- 'url': 'http://example.com',
193 'title': 'Branch Title',
194 'summary': '',
195 'lifecycle_status': BranchLifecycleStatus.DEVELOPMENT,
196@@ -212,15 +211,6 @@
197 'product': arbitrary_product,
198 }
199 add_view.add_action.success(data)
200- # Make sure that next_mirror_time is a datetime, not an sqlbuilder
201- # expression.
202- removeSecurityProxy(add_view.branch).sync()
203- now = datetime.now(pytz.timezone('UTC'))
204- self.assertNotEqual(None, add_view.branch.next_mirror_time)
205- self.assertTrue(
206- add_view.branch.next_mirror_time < now,
207- "next_mirror_time not set to UTC_NOW: %s < %s"
208- % (add_view.branch.next_mirror_time, now))
209 finally:
210 logout()
211
212
213=== modified file 'lib/lp/code/enums.py'
214--- lib/lp/code/enums.py 2011-08-25 10:47:05 +0000
215+++ lib/lp/code/enums.py 2011-08-25 10:47:30 +0000
216@@ -99,8 +99,8 @@
217 IMPORTED = DBItem(3, """
218 Imported
219
220- Branches that have been converted from some other revision
221- control system into bzr and are made available through Launchpad.
222+ Branches that have been imported from an externally hosted
223+ branch in bzr or another VCS and are made available through Launchpad.
224 """)
225
226 REMOTE = DBItem(4, """
227
228=== modified file 'lib/lp/code/stories/branches/xx-creating-branches.txt'
229--- lib/lp/code/stories/branches/xx-creating-branches.txt 2010-10-18 22:24:59 +0000
230+++ lib/lp/code/stories/branches/xx-creating-branches.txt 2011-08-25 10:47:30 +0000
231@@ -32,94 +32,8 @@
232 >>> browser.open('http://code.launchpad.dev/redfish')
233 >>> browser.getLink("Register a branch").click()
234
235- >>> browser.getControl('Hosted').click()
236 >>> browser.getControl('Name').value = 'hosted-branch'
237
238-Branch URLs are not valid for hosted branches.
239-
240- >>> browser.getControl('Branch URL').value = (
241- ... 'http://example.com/hosted-branch')
242- >>> browser.getControl('Register Branch').click()
243- >>> for message in get_feedback_messages(browser.contents):
244- ... print message
245- There is 1 error.
246- Branch URLs cannot be set for Hosted branches.
247-
248- >>> browser.getControl('Branch URL').value = ''
249- >>> browser.getControl('Register Branch').click()
250-
251- >>> def print_branch_info(browser):
252- ... branch_info = find_tag_by_id(browser.contents, 'branch-info')
253- ... print extract_text(branch_info)
254- >>> print_branch_info(browser)
255- Branch information
256- Owner: Sample Person
257- Project: Redfish
258- Status: Development
259-
260-
261-Creating a mirrored branch
262---------------------------
263-
264-Mirrored branches are primarily hosted outside of Launchpad, and
265-Launchpad mirrors the branch.
266-
267- >>> browser.open('http://code.launchpad.dev/redfish')
268- >>> browser.getLink("Register a branch").click()
269-
270- >>> print_radio_button_field(browser.contents, 'branch_type')
271- ( ) Hosted
272- (*) Mirrored
273- ( ) Remote
274-
275- >>> browser.getControl('Name').value = 'mirrored-branch'
276-
277-Branch URLs are required for mirrored branches.
278-
279- >>> browser.getControl('Register Branch').click()
280- >>> for message in get_feedback_messages(browser.contents):
281- ... print message
282- There is 1 error.
283- Branch URLs are required for Mirrored branches.
284-
285- >>> browser.getControl('Branch URL').value = (
286- ... 'http://example.com/mirrored-branch')
287- >>> browser.getControl('Register Branch').click()
288-
289- >>> print_branch_info(browser)
290- Branch information
291- Owner: Sample Person
292- Project: Redfish
293- Status: Development
294- Location: http://example.com/mirrored-branch
295- Last mirrored: Not mirrored yet
296- Next mirror: As soon as possible
297-
298-
299-Creating a remote branch
300-------------------------
301-
302-The contents of Remote branches are not accessible through Launchpad.
303-
304- >>> browser.open('http://code.launchpad.dev/redfish')
305- >>> browser.getLink("Register a branch").click()
306-
307- >>> browser.getControl('Remote').click()
308- >>> browser.getControl('Name').value = 'remote-branch'
309-
310-A remote branch may have a URL, but doesn't have to.
311-
312- >>> browser.getControl('Branch URL').value = (
313- ... 'http://example.com/remote-branch')
314- >>> browser.getControl('Register Branch').click()
315- >>> print_branch_info(browser)
316- Branch information
317- Owner: Sample Person
318- Project: Redfish
319- Status: Development
320- Location: http://example.com/remote-branch
321-
322-
323 Finding product/+addbranch
324 --------------------------
325
326@@ -209,34 +123,6 @@
327 Other branches owned by No Privileges Person
328
329
330-Adding a branch via a product series
331-------------------------------------
332-
333-A user can register a branch from the project series page using the
334-'Submit code' link, or in the case of a series driver, by using the
335-'registering a mirrored branch' link.
336-
337- >>> browser.open('http://launchpad.dev/gnome-terminal/trunk')
338- >>> browser.getLink('registering a mirrored branch').click()
339- >>> print browser.title
340- Register a branch : Code : GNOME Terminal
341-
342-The user sees that he is registering a branch for the series' project.
343-
344- >>> print extract_text(find_main_content(browser.contents).h1)
345- Register a branch on GNOME Terminal
346-
347-After posting the form, he sees the registered branch page.
348-
349- >>> browser.getControl('Branch URL').value = (
350- ... 'http://example.com/applets/master')
351- >>> browser.getControl('Name').value = 'master'
352- >>> browser.getControl('Experimental').click()
353- >>> browser.getControl('Register Branch').click()
354- >>> print browser.title
355- master : Code : GNOME Terminal
356-
357-
358 Finding person/+addbranch
359 -------------------------
360
361@@ -271,7 +157,6 @@
362 Ubuntu Gnome Team (name18)
363 Warty Security Team (name20)
364
365-
366 Conflict on unique name
367 -----------------------
368
369@@ -288,8 +173,6 @@
370 Try a adding a conflicting branch from the product/+addbranch form.
371
372 >>> browser.open('http://code.launchpad.dev/gnome-terminal/+addbranch')
373- >>> browser.getControl('Branch URL').value = (
374- ... 'http://example.com/gnome-terminal/main-dup')
375
376 Trying to post the form without filling a name at all should not cause
377 an oops!
378@@ -321,7 +204,6 @@
379 >>> browser.getControl('Owner').displayValue = [
380 ... 'Landscape Developers']
381 >>> browser.getControl('Name').value = 'main'
382- >>> browser.getControl('Hosted').click()
383 >>> browser.getControl('Register Branch').click()
384 >>> print browser.url
385 http://code.launchpad.dev/~landscape-developers/gnome-terminal/main
386@@ -335,7 +217,6 @@
387 >>> browser.getControl('Owner').displayValue = [
388 ... 'Landscape Developers']
389 >>> browser.getControl('Name').value = 'main'
390- >>> browser.getControl('Hosted').click()
391 >>> browser.getControl('Register Branch').click()
392 >>> for message in get_feedback_messages(browser.contents):
393 ... print extract_text(message)
394@@ -343,65 +224,6 @@
395 Landscape Developers already has a branch for GNOME Terminal called main.
396
397
398-Checking URLs
399--------------
400-
401-URL validation should check that the entered URL is not the root of a
402-site.
403-
404- >>> user_browser.open('http://code.launchpad.dev/applets')
405- >>> user_browser.getLink("Register a branch").click()
406- >>> user_browser.getControl('Branch URL').value = 'http://example.com'
407- >>> user_browser.getControl('Name').value = 'unique-name'
408- >>> user_browser.getControl('Register Branch').click()
409- >>> messages = find_tags_by_class(user_browser.contents, 'message')
410- >>> for element in messages:
411- ... print element.renderContents()
412- There is 1 error.
413- URLs for branches cannot point to the root of a site.
414-
415-URL validation should check that the entered URL is not from Launchpad.
416-
417- >>> user_browser.open('http://code.launchpad.dev/applets')
418- >>> user_browser.getLink("Register a branch").click()
419- >>> user_browser.getControl('Branch URL').value = (
420- ... 'http://code.launchpad.dev/~testuser/')
421- >>> user_browser.getControl('Name').value = 'unique-name'
422- >>> user_browser.getControl('Register Branch').click()
423- >>> for message in get_feedback_messages(user_browser.contents):
424- ... print message
425- There is 1 error.
426- For Launchpad to mirror a branch, the original branch cannot be on
427- launchpad.dev.
428-
429-As well as checking against the root site set in the config, a check is
430-also done against the value stored as a database constraint.
431-
432- >>> user_browser.open('http://code.launchpad.dev/applets')
433- >>> user_browser.getLink("Register a branch").click()
434- >>> user_browser.getControl('Branch URL').value = (
435- ... 'http://bazaar.launchpad.net/foo/bar/')
436- >>> user_browser.getControl('Name').value = 'unique-name'
437- >>> user_browser.getControl('Register Branch').click()
438- >>> for message in get_feedback_messages(user_browser.contents):
439- ... print message
440- There is 1 error.
441- For Launchpad to mirror a branch, the original branch cannot be on
442- http://bazaar.launchpad.net.
443-
444-Trailing slashes on URLs are removed.
445-
446- >>> user_browser.getControl('Branch URL').value = (
447- ... 'sftp://example.com/~lifeless/pyresources/')
448- >>> user_browser.getControl('Register Branch').click()
449- >>> print_tag_with_id(user_browser.contents, 'branch-info')
450- Branch information
451- Owner: ...
452- ...
453- Location: sftp://example.com/~lifeless/pyresources
454- ...
455-
456-
457 Attempting to create a branch in a forbidden project
458 ----------------------------------------------------
459
460@@ -423,7 +245,6 @@
461
462 >>> user_browser.open('http://code.launchpad.dev/landscape')
463 >>> user_browser.getLink("Register a branch").click()
464- >>> user_browser.getControl('Branch URL').value = 'http://foo.com/bar'
465 >>> user_browser.getControl('Name').value = 'landscape1'
466 >>> user_browser.getControl('Register Branch').click()
467 >>> messages = find_tags_by_class(user_browser.contents, 'message')
468
469=== modified file 'lib/lp/code/stories/codeimport/xx-create-codeimport.txt'
470--- lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-09-28 19:25:54 +0000
471+++ lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2011-08-25 10:47:30 +0000
472@@ -56,6 +56,7 @@
473 The default foreign VCS type is Subversion.
474
475 >>> print_radio_button_field(browser.contents, "rcs_type")
476+ ( ) Bazaar
477 ( ) Git
478 ( ) Mercurial
479 (*) Subversion
480
481=== modified file 'lib/lp/code/templates/branch-form-macros.pt'
482--- lib/lp/code/templates/branch-form-macros.pt 2009-10-02 15:02:23 +0000
483+++ lib/lp/code/templates/branch-form-macros.pt 2011-08-25 10:47:30 +0000
484@@ -17,20 +17,6 @@
485
486 <script type="text/javascript">
487 //<![CDATA[
488-function update_branch_url()
489-{
490- var branch_type_field = document.getElementsByName('field.branch_type')
491- var form = branch_type_field[0].form;
492- var branch_type = 'None';
493- for (var i = 0; i < branch_type_field.length; i++) {
494- if (branch_type_field[i].checked) {
495- branch_type = branch_type_field[i].value;
496- break;
497- }
498- }
499- updateField(form['field.url'], branch_type != 'HOSTED');
500-}
501-
502 function update_branch_unique_name()
503 {
504 var unique_name = document.getElementById("branch-unique-name")
505@@ -43,44 +29,16 @@
506 unique_name.innerHTML = branch_name
507 }
508
509-function populate_branch_name_from_url()
510-{
511- url_field = document.getElementById('field.url');
512- var url_text = trim(url_field.value);
513- // strip of any trailing slashes
514- url_text = url_text.replace(/\/+$/, '')
515- if (url_text != url_field.value) {
516- url_field.value = url_text;
517- }
518- var name_field = document.getElementById('field.name');
519- if (name_field.value == '')
520- {
521- // parse the value of the url field
522- url_bits = url_text.split('/');
523- if (url_bits.length > 2) {
524- // attempt at not barfing on something completely invalid
525- last_bit = url_bits[url_bits.length - 1];
526- name_field.value = last_bit;
527- }
528- }
529-}
530-
531 function hookUpBranchFieldFunctions()
532 {
533 connect('field.owner', 'onkeyup', update_branch_unique_name);
534 connect('field.owner', 'onchange', update_branch_unique_name);
535 connect('field.name', 'onkeyup', update_branch_unique_name);
536- connect('field.branch_type.0', 'onclick', update_branch_url);
537- connect('field.branch_type.1', 'onclick', update_branch_url);
538- connect('field.branch_type.2', 'onclick', update_branch_url);
539 update_branch_unique_name();
540- connect('field.url', 'onchange', populate_branch_name_from_url);
541- connect('field.url', 'onblur', populate_branch_name_from_url);
542 document.getElementById("branch-unique-name-div").style.display = "block";
543 }
544
545 registerLaunchpadFunction(hookUpBranchFieldFunctions);
546-registerLaunchpadFunction(update_branch_url);
547
548 //]]>
549 </script>
550
551=== modified file 'lib/lp/code/templates/branch-import-details.pt'
552--- lib/lp/code/templates/branch-import-details.pt 2010-06-10 07:54:59 +0000
553+++ lib/lp/code/templates/branch-import-details.pt 2011-08-25 10:47:30 +0000
554@@ -51,6 +51,12 @@
555 </p>
556 </tal:hg-import>
557
558+ <tal:bzr-import condition="code_import/rcs_type/enumvalue:BZR">
559+ <p>This branch is an import of the Bazaar branch at
560+ <span tal:replace="code_import/url" />.
561+ </p>
562+ </tal:bzr-import>
563+
564 <tal:svn-import condition="view/is_svn_import">
565 <p id="svn-import-details">
566 This branch is an import of the
567
568=== modified file 'lib/lp/code/templates/codeimport-new.pt'
569--- lib/lp/code/templates/codeimport-new.pt 2010-04-22 23:50:51 +0000
570+++ lib/lp/code/templates/codeimport-new.pt 2011-08-25 10:47:30 +0000
571@@ -53,6 +53,20 @@
572 <tr>
573 <td>
574 <label>
575+ <input tal:replace="structure view/rcs_type_bzr" />
576+ Bazaar
577+ </label>
578+ <table class="importdetails">
579+ <tal:widget define="widget nocall:view/widgets/bzr_branch_url">
580+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
581+ </tal:widget>
582+ </table>
583+ </td>
584+ </tr>
585+
586+ <tr>
587+ <td>
588+ <label>
589 <input tal:replace="structure view/rcs_type_git" />
590 Git
591 </label>
592
593=== modified file 'lib/lp/registry/browser/productseries.py'
594--- lib/lp/registry/browser/productseries.py 2011-08-25 10:47:05 +0000
595+++ lib/lp/registry/browser/productseries.py 2011-08-25 10:47:30 +0000
596@@ -1090,8 +1090,7 @@
597
598 # Create a new branch.
599 if branch_type == CREATE_NEW:
600- branch = self._createBzrBranch(
601- BranchType.HOSTED, branch_name, branch_owner)
602+ branch = self._createBzrBranch(branch_name, branch_owner)
603 if branch is not None:
604 self.context.branch = branch
605 self.request.response.addInfoNotification(
606@@ -1102,55 +1101,42 @@
607 # Either create an externally hosted bzr branch
608 # (a.k.a. 'mirrored') or create a new code import.
609 rcs_type = data.get('rcs_type')
610- if rcs_type == RevisionControlSystems.BZR:
611- branch = self._createBzrBranch(
612- BranchType.MIRRORED, branch_name, branch_owner,
613- data['repo_url'])
614- if branch is None:
615- return
616-
617- self.context.branch = branch
618- self.request.response.addInfoNotification(
619- 'Mirrored branch created and linked to '
620- 'the series.')
621+ # We need to create an import request.
622+ if rcs_type == RevisionControlSystems.CVS:
623+ cvs_root = data.get('repo_url')
624+ cvs_module = data.get('cvs_module')
625+ url = None
626 else:
627- # We need to create an import request.
628- if rcs_type == RevisionControlSystems.CVS:
629- cvs_root = data.get('repo_url')
630- cvs_module = data.get('cvs_module')
631- url = None
632- else:
633- cvs_root = None
634- cvs_module = None
635- url = data.get('repo_url')
636- rcs_item = RevisionControlSystems.items[rcs_type.name]
637- try:
638- code_import = getUtility(ICodeImportSet).new(
639- registrant=branch_owner,
640- target=IBranchTarget(self.context.product),
641- branch_name=branch_name,
642- rcs_type=rcs_item,
643- url=url,
644- cvs_root=cvs_root,
645- cvs_module=cvs_module)
646- except BranchExists, e:
647- self._setBranchExists(e.existing_branch,
648- 'branch_name')
649- self.errors_in_action = True
650- # Abort transaction. This is normally handled
651- # by LaunchpadFormView, but we are already in
652- # the success handler.
653- self._abort()
654- return
655- self.context.branch = code_import.branch
656- self.request.response.addInfoNotification(
657- 'Code import created and branch linked to the '
658- 'series.')
659+ cvs_root = None
660+ cvs_module = None
661+ url = data.get('repo_url')
662+ rcs_item = RevisionControlSystems.items[rcs_type.name]
663+ try:
664+ code_import = getUtility(ICodeImportSet).new(
665+ registrant=branch_owner,
666+ target=IBranchTarget(self.context.product),
667+ branch_name=branch_name,
668+ rcs_type=rcs_item,
669+ url=url,
670+ cvs_root=cvs_root,
671+ cvs_module=cvs_module)
672+ except BranchExists, e:
673+ self._setBranchExists(e.existing_branch,
674+ 'branch_name')
675+ self.errors_in_action = True
676+ # Abort transaction. This is normally handled
677+ # by LaunchpadFormView, but we are already in
678+ # the success handler.
679+ self._abort()
680+ return
681+ self.context.branch = code_import.branch
682+ self.request.response.addInfoNotification(
683+ 'Code import created and branch linked to the '
684+ 'series.')
685 else:
686 raise UnexpectedFormData(branch_type)
687
688- def _createBzrBranch(self, branch_type, branch_name,
689- branch_owner, repo_url=None):
690+ def _createBzrBranch(self, branch_name, branch_owner, repo_url=None):
691 """Create a new Bazaar branch. It may be hosted or mirrored.
692
693 Return the branch on success or None.
694@@ -1158,12 +1144,10 @@
695 branch = None
696 try:
697 namespace = self.target.getNamespace(branch_owner)
698- branch = namespace.createBranch(branch_type=branch_type,
699+ branch = namespace.createBranch(branch_type=BranchType.HOSTED,
700 name=branch_name,
701 registrant=self.user,
702 url=repo_url)
703- if branch_type == BranchType.MIRRORED:
704- branch.requestMirror()
705 except BranchCreationForbidden:
706 self.addError(
707 "You are not allowed to create branches in %s." %
708
709=== modified file 'lib/lp/registry/templates/productseries-codesummary.pt'
710--- lib/lp/registry/templates/productseries-codesummary.pt 2011-05-16 20:55:08 +0000
711+++ lib/lp/registry/templates/productseries-codesummary.pt 2011-08-25 10:47:30 +0000
712@@ -54,7 +54,7 @@
713 </li>
714
715 <li>
716- If the code is in Git, Mercurial, CVS or Subversion you can
717+ If the code is in Git, Mercurial, CVS, Subversion or an external Bazaar branch you can
718 <a tal:attributes="href view/request_import_link">request that the branch be imported to Bazaar</a>.
719 </li>
720 </ul>

Subscribers

People subscribed via source and target branches

to status/vote changes: