Merge lp:~jelmer/launchpad/bzr-code-imports into lp:launchpad/db-devel
- bzr-code-imports
- Merge into db-devel
Status: | Superseded |
---|---|
Proposed branch: | lp:~jelmer/launchpad/bzr-code-imports |
Merge into: | lp:launchpad/db-devel |
Prerequisite: | lp:~jelmer/launchpad/bzr-2.4b4 |
Diff against target: |
2037 lines (+1137/-48) (has conflicts) 35 files modified
database/schema/patch-2208-74-0.sql (+20/-0) lib/canonical/config/schema-lazr.conf (+5/-0) lib/canonical/launchpad/daemons/tachandler.py (+9/-0) lib/canonical/launchpad/doc/vocabulary-json.txt (+10/-0) lib/lp/app/javascript/picker/person_picker.js (+71/-0) lib/lp/app/javascript/picker/picker_patcher.js (+4/-0) lib/lp/app/javascript/picker/tests/test_personpicker.js (+73/-0) lib/lp/app/javascript/picker/tests/test_picker_patcher.js (+179/-0) lib/lp/code/errors.py (+3/-0) lib/lp/code/mail/codeimport.py (+3/-1) lib/lp/code/model/branchjob.py (+16/-0) lib/lp/code/model/codeimport.py (+8/-3) lib/lp/code/model/codeimportevent.py (+2/-1) lib/lp/code/model/tests/test_codeimport.py (+44/-0) lib/lp/codehosting/codeimport/tests/servers.py (+21/-0) lib/lp/codehosting/codeimport/tests/test_worker.py (+53/-4) lib/lp/codehosting/codeimport/tests/test_workermonitor.py (+20/-0) lib/lp/codehosting/codeimport/worker.py (+35/-4) lib/lp/registry/browser/productseries.py (+9/-17) lib/lp/registry/browser/tests/test_distroseries.py (+59/-0) lib/lp/registry/interfaces/distroseries.py (+5/-0) lib/lp/registry/model/distribution.py (+34/-0) lib/lp/registry/model/product.py (+12/-0) lib/lp/soyuz/browser/queue.py (+105/-0) lib/lp/soyuz/browser/tests/test_queue.py (+166/-0) lib/lp/soyuz/interfaces/archive.py (+10/-3) lib/lp/soyuz/interfaces/files.py (+4/-0) lib/lp/soyuz/model/packagesetsources.py (+44/-0) lib/lp/soyuz/scripts/tests/test_queue.py (+43/-0) lib/lp/soyuz/stories/webservice/xx-archive.txt (+5/-0) lib/lp/soyuz/tests/test_packageupload.py (+3/-0) lib/lp/testing/factory.py (+30/-3) scripts/code-import-worker.py (+14/-4) utilities/sourcedeps.cache (+14/-8) versions.cfg (+4/-0) Text conflict in database/schema/patch-2208-74-0.sql Text conflict in lib/canonical/launchpad/daemons/tachandler.py Text conflict in lib/canonical/launchpad/doc/vocabulary-json.txt Text conflict in lib/lp/app/javascript/picker/person_picker.js Text conflict in lib/lp/app/javascript/picker/picker_patcher.js Text conflict in lib/lp/app/javascript/picker/tests/test_personpicker.js Text conflict in lib/lp/app/javascript/picker/tests/test_picker_patcher.js Text conflict in lib/lp/code/errors.py Text conflict in lib/lp/code/model/branchjob.py Text conflict in lib/lp/codehosting/codeimport/worker.py Text conflict in lib/lp/registry/browser/tests/test_distroseries.py Text conflict in lib/lp/registry/interfaces/distroseries.py Text conflict in lib/lp/registry/model/distribution.py Text conflict in lib/lp/registry/model/product.py Text conflict in lib/lp/soyuz/browser/queue.py Text conflict in lib/lp/soyuz/browser/tests/test_queue.py Text conflict in lib/lp/soyuz/interfaces/archive.py Text conflict in lib/lp/soyuz/interfaces/files.py Text conflict in lib/lp/soyuz/model/packagesetsources.py Text conflict in lib/lp/soyuz/scripts/tests/test_queue.py Text conflict in lib/lp/soyuz/stories/webservice/xx-archive.txt Text conflict in lib/lp/soyuz/tests/test_packageupload.py Text conflict in lib/lp/testing/factory.py Text conflict in scripts/code-import-worker.py Text conflict in utilities/sourcedeps.cache Text conflict in versions.cfg |
To merge this branch: | bzr merge lp:~jelmer/launchpad/bzr-code-imports |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins | code | Pending | |
Gavin Panella | Pending | ||
Michael Hudson-Doyle | Pending | ||
Review via email:
|
This proposal supersedes a proposal from 2011-06-23.
This proposal has been superseded by a proposal from 2011-07-18.
Commit message
Description of the change
Add support for importing code from Bazaar branches.
At the moment mirrors of remote Bazaar branches are created with completely different infrastructure as the code imports. This is confusing for users (bug 611837) and duplicates a lot of code. Several features are only available for code imports (bug 362622, bug 193607, bug 193607, bug 371484) and vice versa. Having shared infrastructure would also make it easier to fix several open bugs that affect both code imports and code mirrors (bug 519159, bug 136939)
Code imports are a bit heavier than mirrors at the moment, as they run on a separate machine and require an extra copy of the branch that is imported.
This branch only adds backend support for Bazaar branches, it does not yet add a UI which can add code imports of this kind nor does it migrate any of the existing code mirrors.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gavin Panella (allenap) wrote : Posted in a previous version of this proposal | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal | # |
> I don't know the ins and outs of bzrlib or codehosting - for that I
> guess that's why you've asked Michael to review - but the rest looks
> good.
Thanks :)
I'm pretty confident about this code change (especially since nothing actually triggers the new code yet), but I'd like to get some feedback from more people (Michael, Jono?, Rob?) on to confirm this is the right direction to go in.
> + def test_partial(self):
> + # Skip tests of partial tests, as they are disabled for native
> imports
> + # at the moment.
> + return
>
> You could use TestCase.skip() here:
>
> def test_partial(self):
> self.skip("Disabled for native imports at the moment.")
I looked for things that raised SkipTest or TestSkipped and couldn't find any. I didn't know about TestCase.skip - thanks, fixed.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michael Hudson-Doyle (mwhudson) wrote : Posted in a previous version of this proposal | # |
Some random comments: it would have been nice to see the things you had to do wrt the bzr upgrade
The fact that you put this line in suggests that the tests aren't very well isolated:
Does something need to inherit from bzrlib's TestCase? It's all a complete tangle though.
Is simply prohibiting branch references the right thing to do? The branch puller goes to some lengths to support them safely -- being able to dump all that code would surely be very nice. Relatedly, and on thinking about it I think this is a bit more serious, I think you might need to be careful about stacking -- it's contrived but you might be able to construct a branch that was stacked on some DC-internal branch and have that be imported so you can grab it.
In terms of overall direction, I'm all for removing duplication. I think the UI will require some effort (e.g. do we want to count bzr mirrors as "imported branches" on the code.launchpad.net frontpage?) but engineering wise, this looks fine, modulo the above comments.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michael Hudson-Doyle (mwhudson) wrote : Posted in a previous version of this proposal | # |
"Some random comments: it would have been nice to see the things you had to do wrt the bzr upgrade " ... in a separate branch, I meant to say.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal | # |
On 06/24/2011 05:35 AM, Michael Hudson-Doyle wrote:
> Review: Approve
> Some random comments: it would have been nice to see the things you had to do wrt the bzr upgrade
This branch has two prerequisite branches, but I can only set one in
Launchpad. Hopefully the diff will get smaller when the update to the
newer bzr lands on lp:launchpad...
>
> The fact that you put this line in suggests that the tests aren't very well isolated:
>
> branch.
>
> Does something need to inherit from bzrlib's TestCase? It's all a complete tangle though.
Perhaps; bzr's TestCase does a lot though, and I'm kindof worried that
mixing it in will break other things. It should do more than just this
ad-hoc override though. Perhaps reset the global bzr configuration in setUp?
>
> Is simply prohibiting branch references the right thing to do? The branch puller goes to some lengths to support them safely -- being able to dump all that code would surely be very nice. Relatedly, and on thinking about it I think this is a bit more serious, I think you might need to be careful about stacking -- it's contrived but you might be able to construct a branch that was stacked on some DC-internal branch and have that be imported so you can grab it.
Stacking is a very good point, and one that I had not considered -
thanks. I should probably also have another, closer, look at the branch
puller to see what it does and why.
For branch references, the easiest thing to do for the moment seemed to
be to just refuse to mirror them. If code mirrors support branch
references at the moment, we should keep that support.
>
> In terms of overall direction, I'm all for removing duplication. I think the UI will require some effort (e.g. do we want to count bzr mirrors as "imported branches" on the code.launchpad.net frontpage?) but engineering wise, this looks fine, modulo the above comments.
Thanks for having a look at this. I'm only just getting into this code
and am not very familiar with it yet.
Cheers,
Jelmer
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michael Hudson-Doyle (mwhudson) wrote : Posted in a previous version of this proposal | # |
On Fri, 24 Jun 2011 19:55:45 +0200, Jelmer Vernooij <email address hidden> wrote:
> On 06/24/2011 05:35 AM, Michael Hudson-Doyle wrote:
> > Review: Approve
> > Some random comments: it would have been nice to see the things you had to do wrt the bzr upgrade
> This branch has two prerequisite branches, but I can only set one in
> Launchpad. Hopefully the diff will get smaller when the update to the
> newer bzr lands on lp:launchpad...
Ah! I guess you could have created a branch with both of the
prerequisites merged in, but that's getting pretty tedious...
> >
> > The fact that you put this line in suggests that the tests aren't very well isolated:
> >
> > branch.
> >
> > Does something need to inherit from bzrlib's TestCase? It's all a complete tangle though.
> Perhaps; bzr's TestCase does a lot though, and I'm kindof worried that
> mixing it in will break other things.
I'd be surprised if it broke other stuff on past experience, but you
might be right.
> It should do more than just this ad-hoc override though. Perhaps reset
> the global bzr configuration in setUp?
I guess what you want is a test fixture that just does the environment
isolation in bzrlib you can reuse here... but yes, doing something more
generic than just clearing create_signatures would be better, I think.
> >
> > Is simply prohibiting branch references the right thing to do? The branch puller goes to some lengths to support them safely -- being able to dump all that code would surely be very nice. Relatedly, and on thinking about it I think this is a bit more serious, I think you might need to be careful about stacking -- it's contrived but you might be able to construct a branch that was stacked on some DC-internal branch and have that be imported so you can grab it.
> Stacking is a very good point, and one that I had not considered -
> thanks. I should probably also have another, closer, look at the branch
> puller to see what it does and why.
For reasons I should probably be ashamed of, there seem to be two
implementations of 'safe opening' in Launchpad; one is around
lp.codehosting.
lp.codehosting.
(looking at worker.py makes me realize how much code we can delete once
this branch is done, never mind how much can be deleted when the puller
is gone completely).
> For branch references, the easiest thing to do for the moment seemed to
> be to just refuse to mirror them. If code mirrors support branch
> references at the moment, we should keep that support.
Yeah, they do.
> >
> > In terms of overall direction, I'm all for removing duplication. I think the UI will require some effort (e.g. do we want to count bzr mirrors as "imported branches" on the code.launchpad.net frontpage?) but engineering wise, this looks fine, modulo the above comments.
> Thanks for having a look at this. I'm only just getting into this code
> and am not very familiar with it yet.
You seem to be doing fine :)
Cheers,
mwh
Preview Diff
1 | === modified file 'database/schema/patch-2208-74-0.sql' |
2 | --- database/schema/patch-2208-74-0.sql 2011-06-27 10:38:18 +0000 |
3 | +++ database/schema/patch-2208-74-0.sql 2011-07-18 11:13:26 +0000 |
4 | @@ -1,3 +1,4 @@ |
5 | +<<<<<<< TREE |
6 | -- Copyright 2011 Canonical Ltd. This software is licensed under the |
7 | -- GNU Affero General Public License version 3 (see the file LICENSE). |
8 | |
9 | @@ -15,3 +16,22 @@ |
10 | |
11 | INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 74, 0); |
12 | |
13 | +======= |
14 | +-- Copyright 2011 Canonical Ltd. This software is licensed under the |
15 | +-- GNU Affero General Public License version 3 (see the file LICENSE). |
16 | + |
17 | +SET client_min_messages=ERROR; |
18 | + |
19 | +ALTER TABLE CodeImport DROP CONSTRAINT valid_vcs_details; |
20 | +ALTER TABLE CodeImport ADD CONSTRAINT "valid_vcs_details" CHECK ( |
21 | +CASE |
22 | + WHEN rcs_type = 1 THEN cvs_root IS NOT NULL AND cvs_root <> ''::text AND cvs_module IS NOT NULL AND cvs_module <> ''::text AND url IS NULL |
23 | + WHEN rcs_type = ANY (ARRAY[2, 3]) THEN cvs_root IS NULL AND cvs_module IS NULL AND url IS NOT NULL AND valid_absolute_url(url) |
24 | + WHEN rcs_type = ANY (ARRAY[4, 5, 6]) THEN cvs_root IS NULL AND cvs_module IS NULL AND url IS NOT NULL |
25 | + ELSE false |
26 | +END); |
27 | + |
28 | + |
29 | +INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 74, 0); |
30 | + |
31 | +>>>>>>> MERGE-SOURCE |
32 | |
33 | === modified file 'lib/canonical/config/schema-lazr.conf' |
34 | --- lib/canonical/config/schema-lazr.conf 2011-07-15 15:46:51 +0000 |
35 | +++ lib/canonical/config/schema-lazr.conf 2011-07-18 11:13:26 +0000 |
36 | @@ -504,6 +504,11 @@ |
37 | # datatype: integer |
38 | default_interval_cvs: 43200 |
39 | |
40 | +# The default value of the update interval of a code import from |
41 | +# Bazaar, in seconds. |
42 | +# datatype: integer |
43 | +default_interval_bzr: 21600 |
44 | + |
45 | # Where the tarballs of foreign branches are uploaded for storage. |
46 | # datatype: string |
47 | foreign_tree_store: sftp://hoover@escudero/srv/importd/sources/ |
48 | |
49 | === modified file 'lib/canonical/launchpad/daemons/tachandler.py' |
50 | --- lib/canonical/launchpad/daemons/tachandler.py 2011-06-21 17:13:47 +0000 |
51 | +++ lib/canonical/launchpad/daemons/tachandler.py 2011-07-18 11:13:26 +0000 |
52 | @@ -20,6 +20,7 @@ |
53 | from fixtures import Fixture |
54 | |
55 | from canonical.launchpad.daemons import readyservice |
56 | +<<<<<<< TREE |
57 | from lp.services.osutils import ( |
58 | get_pid_from_file, |
59 | kill_by_pidfile, |
60 | @@ -27,6 +28,14 @@ |
61 | two_stage_kill, |
62 | until_no_eintr, |
63 | ) |
64 | +======= |
65 | +from lp.services.osutils import ( |
66 | + get_pid_from_file, |
67 | + kill_by_pidfile, |
68 | + remove_if_exists, |
69 | + two_stage_kill, |
70 | + ) |
71 | +>>>>>>> MERGE-SOURCE |
72 | |
73 | |
74 | twistd_script = os.path.abspath(os.path.join( |
75 | |
76 | === modified file 'lib/canonical/launchpad/doc/vocabulary-json.txt' |
77 | --- lib/canonical/launchpad/doc/vocabulary-json.txt 2011-07-18 00:30:18 +0000 |
78 | +++ lib/canonical/launchpad/doc/vocabulary-json.txt 2011-07-18 11:13:26 +0000 |
79 | @@ -93,9 +93,14 @@ |
80 | "api_uri": "/~commercial-admins", |
81 | "css": "sprite team", |
82 | "link_css": "js-action", |
83 | +<<<<<<< TREE |
84 | "metadata": "team", |
85 | "title": "Commercial Subscription Admins", |
86 | "value": "commercial-admins" |
87 | +======= |
88 | + "title": "Commercial Subscription Admins", |
89 | + "value": "commercial-admins" |
90 | +>>>>>>> MERGE-SOURCE |
91 | } |
92 | ], |
93 | "total_size": 6 |
94 | @@ -114,9 +119,14 @@ |
95 | "api_uri": "/~launchpad-buildd-admins", |
96 | "css": "sprite team", |
97 | "link_css": "js-action", |
98 | +<<<<<<< TREE |
99 | "metadata": "team", |
100 | "title": "Launchpad Buildd Admins", |
101 | "value": "launchpad-buildd-admins" |
102 | +======= |
103 | + "title": "Launchpad Buildd Admins", |
104 | + "value": "launchpad-buildd-admins" |
105 | +>>>>>>> MERGE-SOURCE |
106 | } |
107 | ], |
108 | "total_size": 6 |
109 | |
110 | === modified file 'lib/lp/app/javascript/picker/person_picker.js' |
111 | --- lib/lp/app/javascript/picker/person_picker.js 2011-07-14 12:46:31 +0000 |
112 | +++ lib/lp/app/javascript/picker/person_picker.js 2011-07-18 11:13:26 +0000 |
113 | @@ -1,10 +1,81 @@ |
114 | /* Copyright 2011 Canonical Ltd. This software is licensed under the |
115 | * GNU Affero General Public License version 3 (see the file LICENSE). |
116 | * |
117 | +<<<<<<< TREE |
118 | * @namespace Y.lazr.person-picker |
119 | * @requires lazr.picker |
120 | */ |
121 | YUI.add('lazr.person-picker', function(Y) { |
122 | +======= |
123 | + * @namespace Y.lp.app.widgets |
124 | + * @requires Y.lazr.picker |
125 | + */ |
126 | +YUI.add('lp.app.widgets', function(Y) { |
127 | +var namespace = Y.namespace('lp.app.widgets'); |
128 | + |
129 | +/** |
130 | + * Extend the lazr-js Picker. |
131 | + */ |
132 | +var Picker = function() { |
133 | + Picker.superclass.constructor.apply(this, arguments); |
134 | +}; |
135 | + |
136 | +Y.extend(Picker, Y.lazr.Picker, { |
137 | + // We want to render alt title slightly differently. |
138 | + _renderTitleUI: function(data) { |
139 | + var li_title = Y.Node.create( |
140 | + '<span></span>').addClass(Y.lazr.Picker.C_RESULT_TITLE); |
141 | + if (data.title === undefined) { |
142 | + // Display an empty element if data is empty. |
143 | + return li_title; |
144 | + } |
145 | + var title = this._text_or_link( |
146 | + data.title, data.title_link, data.link_css); |
147 | + li_title.appendChild(title); |
148 | + if (data.alt_title) { |
149 | + var alt_link = null; |
150 | + if (data.alt_title_link) { |
151 | + alt_link =Y.Node.create('<a></a>') |
152 | + .addClass(data.link_css) |
153 | + .addClass('discreet'); |
154 | + alt_link.set('text', " Details...") |
155 | + .set('href', data.alt_title_link); |
156 | + Y.on('click', function(e) { |
157 | + e.halt(); |
158 | + window.open(data.alt_title_link); |
159 | + }, alt_link); |
160 | + } |
161 | + li_title.appendChild(' ('); |
162 | + li_title.appendChild(document.createTextNode(data.alt_title)); |
163 | + li_title.appendChild(')'); |
164 | + if (alt_link !== null) { |
165 | + li_title.appendChild(alt_link); |
166 | + } |
167 | + } |
168 | + return li_title; |
169 | + }, |
170 | + |
171 | + /** |
172 | + * Create the widget's HTML components. |
173 | + * <p> |
174 | + * This method is invoked after renderUI is invoked for the Widget class |
175 | + * using YUI's aop infrastructure. |
176 | + * </p> |
177 | + * |
178 | + * @method _renderUIPicker |
179 | + * @protected |
180 | + */ |
181 | + _renderUIPicker: function() { |
182 | + Picker.superclass._renderUIPicker.apply(this, arguments); |
183 | + var body = this._batches_box.get('parentNode'); |
184 | + body.removeChild(this._batches_box); |
185 | + this._results_box.insert(this._batches_box, 'after'); |
186 | + } |
187 | +}); |
188 | + |
189 | +Picker.NAME = 'picker'; |
190 | +namespace.Picker = Picker; |
191 | +>>>>>>> MERGE-SOURCE |
192 | |
193 | /* |
194 | * Extend the picker into the PersonPicker |
195 | |
196 | === modified file 'lib/lp/app/javascript/picker/picker_patcher.js' |
197 | --- lib/lp/app/javascript/picker/picker_patcher.js 2011-07-18 05:32:40 +0000 |
198 | +++ lib/lp/app/javascript/picker/picker_patcher.js 2011-07-18 11:13:26 +0000 |
199 | @@ -93,8 +93,12 @@ |
200 | if (link === null || !show_remove_button) { |
201 | remove_button.addClass('yui3-picker-hidden'); |
202 | } else { |
203 | +<<<<<<< TREE |
204 | remove_button.removeClass('yui3-picker-hidden'); |
205 | update_button_text(); |
206 | +======= |
207 | + remove_button.removeClass('yui3-picker-hidden'); |
208 | +>>>>>>> MERGE-SOURCE |
209 | } |
210 | } |
211 | |
212 | |
213 | === modified file 'lib/lp/app/javascript/picker/tests/test_personpicker.js' |
214 | --- lib/lp/app/javascript/picker/tests/test_personpicker.js 2011-07-15 02:20:36 +0000 |
215 | +++ lib/lp/app/javascript/picker/tests/test_personpicker.js 2011-07-18 11:13:26 +0000 |
216 | @@ -6,6 +6,7 @@ |
217 | 'lazr.picker', 'lazr.person-picker', 'lp.app.picker', |
218 | 'node-event-simulate', function(Y) { |
219 | |
220 | +<<<<<<< TREE |
221 | var Assert = Y.Assert; |
222 | |
223 | /* Helper function to clean up a dynamically added widget instance. */ |
224 | @@ -144,6 +145,78 @@ |
225 | picker.set('results', []); |
226 | } else { |
227 | picker.set('results', this.vocabulary); |
228 | +======= |
229 | + var suite = new Y.Test.Suite("lp.app.widgets.PersonPicker Tests"); |
230 | + |
231 | + suite.add(new Y.Test.Case({ |
232 | + name: 'personpicker', |
233 | + |
234 | + setUp: function() { |
235 | + window.LP = { |
236 | + links: {me: '/~no-one'}, |
237 | + cache: {} |
238 | + }; |
239 | + }, |
240 | + |
241 | + test_render: function () { |
242 | + var personpicker = new Y.lp.app.widgets.PersonPicker(); |
243 | + personpicker.render(); |
244 | + personpicker.show(); |
245 | + |
246 | + // The extra buttons section exists |
247 | + Y.Assert.isNotNull(Y.one('.extra-form-buttons')); |
248 | + Y.Assert.isNotUndefined(personpicker.assign_me_button); |
249 | + Y.Assert.isNotUndefined(personpicker.remove_button); |
250 | + }, |
251 | + |
252 | + test_search_field_focus: function () { |
253 | + var personpicker = new Y.lp.app.widgets.PersonPicker(); |
254 | + personpicker.render(); |
255 | + personpicker.hide(); |
256 | + |
257 | + var got_focus = false; |
258 | + personpicker._search_input.on('focus', function(e) { |
259 | + got_focus = true; |
260 | + }); |
261 | + personpicker.show(); |
262 | + Y.Assert.isTrue(got_focus, "search input did not get focus."); |
263 | + }, |
264 | + |
265 | + test_buttons: function () { |
266 | + var personpicker = new Y.lp.app.widgets.PersonPicker(); |
267 | + personpicker.render(); |
268 | + personpicker.show(); |
269 | + |
270 | + // Patch the picker so the assign_me and remove methods can be |
271 | + // tested. |
272 | + var data = null; |
273 | + personpicker.on('save', function (result) { |
274 | + data = result.value; |
275 | + }); |
276 | + var remove = Y.one('.yui-picker-remove-button'); |
277 | + remove.simulate('click'); |
278 | + Y.Assert.areEqual('', data); |
279 | + |
280 | + var assign_me = Y.one('.yui-picker-assign-me-button'); |
281 | + assign_me.simulate('click'); |
282 | + Y.Assert.areEqual('no-one', data); |
283 | + }, |
284 | + |
285 | + test_buttons_config: function () { |
286 | + cfg = { |
287 | + show_assign_me_button: false, |
288 | + show_remove_button: false |
289 | + }; |
290 | + |
291 | + personpicker = new Y.lp.app.widgets.PersonPicker(cfg); |
292 | + personpicker.render(); |
293 | + personpicker.show(); |
294 | + |
295 | + Y.Assert.isNotNull(Y.one('.extra-form-buttons')); |
296 | + Y.Assert.isUndefined(personpicker.remove_button); |
297 | + Y.Assert.isUndefined(personpicker.assign_me_button); |
298 | + |
299 | +>>>>>>> MERGE-SOURCE |
300 | } |
301 | }, |
302 | |
303 | |
304 | === modified file 'lib/lp/app/javascript/picker/tests/test_picker_patcher.js' |
305 | --- lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2011-07-18 05:32:40 +0000 |
306 | +++ lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2011-07-18 11:13:26 +0000 |
307 | @@ -231,6 +231,7 @@ |
308 | })); |
309 | |
310 | /* |
311 | +<<<<<<< TREE |
312 | * Test cases for assign and remove buttons. |
313 | */ |
314 | suite.add(new Y.Test.Case({ |
315 | @@ -471,6 +472,184 @@ |
316 | })); |
317 | |
318 | /* |
319 | +======= |
320 | + * Test cases for assign and remove buttons. |
321 | + */ |
322 | +suite.add(new Y.Test.Case({ |
323 | + |
324 | + name: 'picker_assign_remove_button', |
325 | + |
326 | + setUp: function() { |
327 | + var i; |
328 | + this.vocabulary = new Array(121); |
329 | + for (i = 0; i <5; i++) { |
330 | + this.vocabulary[i] = { |
331 | + "value": "value-" + i, |
332 | + "title": "title-" + i, |
333 | + "css": "sprite-person", |
334 | + "description": "description-" + i, |
335 | + "api_uri": "~/fred-" + i}; |
336 | + } |
337 | + this.ME = '/~me'; |
338 | + window.LP = { |
339 | + links: { |
340 | + me: this.ME |
341 | + }, |
342 | + cache: {} |
343 | + }; |
344 | + |
345 | + // We patch Launchpad client to return some fake data for the patch |
346 | + // operation. |
347 | + Y.lp.client.Launchpad = function() {}; |
348 | + Y.lp.client.Launchpad.prototype.patch = |
349 | + function(uri, representation, config, headers) { |
350 | + // our setup assumes success, so we just do the success |
351 | + // callback. |
352 | + var entry_repr = { |
353 | + 'lp_html': {'test_link': '<a href="/~me">Content</a>'} |
354 | + }; |
355 | + var result = new Y.lp.client.Entry( |
356 | + null, entry_repr, "a_self_link"); |
357 | + config.on.success(result); |
358 | + }; |
359 | + |
360 | + }, |
361 | + |
362 | + tearDown: function() { |
363 | + cleanup_widget(this.picker); |
364 | + delete window.LP; |
365 | + }, |
366 | + |
367 | + create_picker: function(show_assign_me, show_remove, field_value) { |
368 | + if (field_value !== undefined) { |
369 | + var data_box = Y.one('#picker_id .yui3-activator-data-box'); |
370 | + data_box.appendChild(Y.Node.create('<a>Content</a>')); |
371 | + data_box.one('a').set('href', field_value); |
372 | + } |
373 | + |
374 | + var config = { |
375 | + "step_title": "Choose someone", |
376 | + "header": "Pick Someone", |
377 | + "validate_callback": null, |
378 | + "show_search_box": true, |
379 | + "show_assign_me_button": show_assign_me, |
380 | + "show_remove_button": show_remove |
381 | + }; |
382 | + this.picker = Y.lp.app.picker.addPickerPatcher( |
383 | + this.vocabulary, |
384 | + "foo/bar", |
385 | + "test_link", |
386 | + "picker_id", |
387 | + config); |
388 | + }, |
389 | + |
390 | + _check_button_state: function(btn_class, is_visible) { |
391 | + var assign_me_button = Y.one(btn_class); |
392 | + Assert.isNotNull(assign_me_button); |
393 | + if (is_visible) { |
394 | + Assert.isFalse( |
395 | + assign_me_button.hasClass('yui3-picker-hidden'), |
396 | + btn_class + " should be visible but is hidden"); |
397 | + } else { |
398 | + Assert.isTrue( |
399 | + assign_me_button.hasClass('yui3-picker-hidden'), |
400 | + btn_class + " should be hidden but is visible"); |
401 | + } |
402 | + }, |
403 | + |
404 | + _check_assign_me_button_state: function(is_visible) { |
405 | + this._check_button_state('.yui-picker-assign-me-button', is_visible); |
406 | + }, |
407 | + |
408 | + _check_remove_button_state: function(is_visible) { |
409 | + this._check_button_state('.yui-picker-remove-button', is_visible); |
410 | + }, |
411 | + |
412 | + test_picker_has_assign_me_button: function() { |
413 | + // The assign me button is shown. |
414 | + this.create_picker(true, true); |
415 | + this.picker.render(); |
416 | + this._check_assign_me_button_state(true); |
417 | + }, |
418 | + |
419 | + test_picker_no_assign_me_button_unless_configured: function() { |
420 | + // The assign me button is only rendered if show_assign_me_button |
421 | + // config setting is true. |
422 | + this.create_picker(false, true); |
423 | + this.picker.render(); |
424 | + Assert.isNull(Y.one('.yui-picker-assign-me-button')); |
425 | + }, |
426 | + |
427 | + test_picker_no_assign_me_button_if_value_is_me: function() { |
428 | + // The assign me button is not shown if the picker is created for a |
429 | + // field where the value is "me". |
430 | + this.create_picker(true, true, this.ME); |
431 | + this.picker.render(); |
432 | + this._check_assign_me_button_state(false); |
433 | + }, |
434 | + |
435 | + test_picker_assign_me_button_hide_on_save: function() { |
436 | + // The assign me button is shown initially but hidden if the picker |
437 | + // saves a value equal to 'me'. |
438 | + this.create_picker(true, true); |
439 | + this._check_assign_me_button_state(true); |
440 | + this.picker.set('results', this.vocabulary); |
441 | + this.picker.render(); |
442 | + simulate( |
443 | + this.picker.get('boundingBox').one('.yui3-picker-results'), |
444 | + 'li:nth-child(1)', 'click'); |
445 | + this._check_assign_me_button_state(false); |
446 | + }, |
447 | + |
448 | + test_picker_no_remove_button_if_null_value: function() { |
449 | + // The remove button is not shown if the picker is created for a field |
450 | + // which has a null value. |
451 | + this.create_picker(true, true); |
452 | + this.picker.render(); |
453 | + this._check_remove_button_state(false); |
454 | + }, |
455 | + |
456 | + test_picker_has_remove_button_if_value: function() { |
457 | + // The remove button is shown if the picker is created for a field |
458 | + // which has a value. |
459 | + this.create_picker(true, true, this.ME); |
460 | + this.picker.render(); |
461 | + this._check_remove_button_state(true); |
462 | + }, |
463 | + |
464 | + test_picker_no_remove_button_unless_configured: function() { |
465 | + // The remove button is only rendered if show_remove_button setting is |
466 | + // true. |
467 | + this.create_picker(true, false, this.ME); |
468 | + this.picker.render(); |
469 | + Assert.isNull(Y.one('.yui-picker-remove-button')); |
470 | + }, |
471 | + |
472 | + test_picker_remove_button_clicked: function() { |
473 | + // The remove button is hidden once a picker value has been removed. |
474 | + // And the assign me button is shown. |
475 | + this.create_picker(true, true, this.ME); |
476 | + this.picker.render(); |
477 | + var remove = Y.one('.yui-picker-remove-button'); |
478 | + remove.simulate('click'); |
479 | + this._check_remove_button_state(false); |
480 | + this._check_assign_me_button_state(true); |
481 | + }, |
482 | + |
483 | + test_picker_assign_me_button_clicked: function() { |
484 | + // The assign me button is hidden once it is clicked. |
485 | + // And the remove button is shown. |
486 | + this.create_picker(true, true); |
487 | + this.picker.render(); |
488 | + var remove = Y.one('.yui-picker-assign-me-button'); |
489 | + remove.simulate('click'); |
490 | + this._check_remove_button_state(true); |
491 | + this._check_assign_me_button_state(false); |
492 | + } |
493 | +})); |
494 | + |
495 | +/* |
496 | +>>>>>>> MERGE-SOURCE |
497 | * Test cases for a picker with a large vocabulary. |
498 | */ |
499 | suite.add(new Y.Test.Case({ |
500 | |
501 | === modified file 'lib/lp/code/errors.py' |
502 | --- lib/lp/code/errors.py 2011-06-23 21:09:20 +0000 |
503 | +++ lib/lp/code/errors.py 2011-07-18 11:13:26 +0000 |
504 | @@ -182,6 +182,7 @@ |
505 | class BranchMergeProposalExists(InvalidBranchMergeProposal): |
506 | """Raised if there is already a matching BranchMergeProposal.""" |
507 | |
508 | +<<<<<<< TREE |
509 | def __init__(self, existing_proposal): |
510 | super(BranchMergeProposalExists, self).__init__( |
511 | 'There is already a branch merge proposal registered for ' |
512 | @@ -190,6 +191,8 @@ |
513 | existing_proposal.target_branch.displayname)) |
514 | self.existing_proposal = existing_proposal |
515 | |
516 | +======= |
517 | +>>>>>>> MERGE-SOURCE |
518 | |
519 | class InvalidNamespace(Exception): |
520 | """Raised when someone tries to lookup a namespace with a bad name. |
521 | |
522 | === modified file 'lib/lp/code/mail/codeimport.py' |
523 | --- lib/lp/code/mail/codeimport.py 2011-05-27 21:12:25 +0000 |
524 | +++ lib/lp/code/mail/codeimport.py 2011-07-18 11:13:26 +0000 |
525 | @@ -51,6 +51,7 @@ |
526 | RevisionControlSystems.BZR_SVN: 'subversion', |
527 | RevisionControlSystems.GIT: 'git', |
528 | RevisionControlSystems.HG: 'mercurial', |
529 | + RevisionControlSystems.BZR: 'bazaar', |
530 | } |
531 | body = get_email_template('new-code-import.txt') % { |
532 | 'person': code_import.registrant.displayname, |
533 | @@ -123,7 +124,8 @@ |
534 | elif code_import.rcs_type in (RevisionControlSystems.SVN, |
535 | RevisionControlSystems.BZR_SVN, |
536 | RevisionControlSystems.GIT, |
537 | - RevisionControlSystems.HG): |
538 | + RevisionControlSystems.HG, |
539 | + RevisionControlSystems.BZR): |
540 | if CodeImportEventDataType.OLD_URL in event_data: |
541 | old_url = event_data[CodeImportEventDataType.OLD_URL] |
542 | body.append( |
543 | |
544 | === modified file 'lib/lp/code/model/branchjob.py' |
545 | --- lib/lp/code/model/branchjob.py 2011-07-11 17:48:50 +0000 |
546 | +++ lib/lp/code/model/branchjob.py 2011-07-18 11:13:26 +0000 |
547 | @@ -107,11 +107,15 @@ |
548 | from lp.scripts.helpers import TransactionFreeOperation |
549 | from lp.services.job.interfaces.job import JobStatus |
550 | from lp.services.job.model.job import Job |
551 | +<<<<<<< TREE |
552 | from lp.services.job.runner import ( |
553 | BaseRunnableJob, |
554 | BaseRunnableJobSource, |
555 | ) |
556 | from lp.services.utils import RegisteredSubclass |
557 | +======= |
558 | +from lp.services.job.runner import BaseRunnableJob |
559 | +>>>>>>> MERGE-SOURCE |
560 | from lp.translations.interfaces.translationimportqueue import ( |
561 | ITranslationImportQueue, |
562 | ) |
563 | @@ -294,6 +298,18 @@ |
564 | raise SQLObjectNotFound( |
565 | 'No occurrence of %s has key %s' % (cls.__name__, key)) |
566 | |
567 | + @classmethod |
568 | + def get(cls, key): |
569 | + """Return the instance of this class whose key is supplied. |
570 | + |
571 | + :raises: SQLObjectNotFound |
572 | + """ |
573 | + instance = IStore(BranchJob).get(BranchJob, key) |
574 | + if instance is None or instance.job_type != cls.class_job_type: |
575 | + raise SQLObjectNotFound( |
576 | + 'No occurrence of %s has key %s' % (cls.__name__, key)) |
577 | + return cls(instance) |
578 | + |
579 | def getOopsVars(self): |
580 | """See `IRunnableJob`.""" |
581 | vars = BaseRunnableJob.getOopsVars(self) |
582 | |
583 | === modified file 'lib/lp/code/model/codeimport.py' |
584 | --- lib/lp/code/model/codeimport.py 2011-04-27 01:42:46 +0000 |
585 | +++ lib/lp/code/model/codeimport.py 2011-07-18 11:13:26 +0000 |
586 | @@ -116,6 +116,8 @@ |
587 | config.codeimport.default_interval_git, |
588 | RevisionControlSystems.HG: |
589 | config.codeimport.default_interval_hg, |
590 | + RevisionControlSystems.BZR: |
591 | + config.codeimport.default_interval_bzr, |
592 | } |
593 | seconds = default_interval_dict[self.rcs_type] |
594 | return timedelta(seconds=seconds) |
595 | @@ -133,7 +135,8 @@ |
596 | RevisionControlSystems.SVN, |
597 | RevisionControlSystems.GIT, |
598 | RevisionControlSystems.BZR_SVN, |
599 | - RevisionControlSystems.HG): |
600 | + RevisionControlSystems.HG, |
601 | + RevisionControlSystems.BZR): |
602 | return self.url |
603 | else: |
604 | raise AssertionError( |
605 | @@ -252,7 +255,8 @@ |
606 | elif rcs_type in (RevisionControlSystems.SVN, |
607 | RevisionControlSystems.BZR_SVN, |
608 | RevisionControlSystems.GIT, |
609 | - RevisionControlSystems.HG): |
610 | + RevisionControlSystems.HG, |
611 | + RevisionControlSystems.BZR): |
612 | assert cvs_root is None and cvs_module is None |
613 | assert url is not None |
614 | else: |
615 | @@ -262,7 +266,8 @@ |
616 | if review_status is None: |
617 | # Auto approve git and hg imports. |
618 | if rcs_type in ( |
619 | - RevisionControlSystems.GIT, RevisionControlSystems.HG): |
620 | + RevisionControlSystems.GIT, RevisionControlSystems.HG, |
621 | + RevisionControlSystems.BZR): |
622 | review_status = CodeImportReviewStatus.REVIEWED |
623 | else: |
624 | review_status = CodeImportReviewStatus.NEW |
625 | |
626 | === modified file 'lib/lp/code/model/codeimportevent.py' |
627 | --- lib/lp/code/model/codeimportevent.py 2010-10-17 22:51:50 +0000 |
628 | +++ lib/lp/code/model/codeimportevent.py 2011-07-18 11:13:26 +0000 |
629 | @@ -269,7 +269,8 @@ |
630 | if code_import.rcs_type in (RevisionControlSystems.SVN, |
631 | RevisionControlSystems.BZR_SVN, |
632 | RevisionControlSystems.GIT, |
633 | - RevisionControlSystems.HG): |
634 | + RevisionControlSystems.HG, |
635 | + RevisionControlSystems.BZR): |
636 | yield 'URL', code_import.url |
637 | elif code_import.rcs_type == RevisionControlSystems.CVS: |
638 | yield 'CVS_ROOT', code_import.cvs_root |
639 | |
640 | === modified file 'lib/lp/code/model/tests/test_codeimport.py' |
641 | --- lib/lp/code/model/tests/test_codeimport.py 2011-05-27 21:12:25 +0000 |
642 | +++ lib/lp/code/model/tests/test_codeimport.py 2011-07-18 11:13:26 +0000 |
643 | @@ -71,6 +71,20 @@ |
644 | # No job is created for the import. |
645 | self.assertIs(None, code_import.import_job) |
646 | |
647 | + def test_new_svn_import_svn_scheme(self): |
648 | + """A subversion import can use the svn:// scheme.""" |
649 | + code_import = CodeImportSet().new( |
650 | + registrant=self.factory.makePerson(), |
651 | + target=IBranchTarget(self.factory.makeProduct()), |
652 | + branch_name='imported', |
653 | + rcs_type=RevisionControlSystems.SVN, |
654 | + url=self.factory.getUniqueURL(scheme="svn")) |
655 | + self.assertEqual( |
656 | + CodeImportReviewStatus.NEW, |
657 | + code_import.review_status) |
658 | + # No job is created for the import. |
659 | + self.assertIs(None, code_import.import_job) |
660 | + |
661 | def test_reviewed_svn_import(self): |
662 | """A specific review status can be set for a new import.""" |
663 | code_import = CodeImportSet().new( |
664 | @@ -117,6 +131,21 @@ |
665 | # A job is created for the import. |
666 | self.assertIsNot(None, code_import.import_job) |
667 | |
668 | + def test_git_import_git_scheme(self): |
669 | + """A git import can have a git:// style URL.""" |
670 | + code_import = CodeImportSet().new( |
671 | + registrant=self.factory.makePerson(), |
672 | + target=IBranchTarget(self.factory.makeProduct()), |
673 | + branch_name='imported', |
674 | + rcs_type=RevisionControlSystems.GIT, |
675 | + url=self.factory.getUniqueURL(scheme="git"), |
676 | + review_status=None) |
677 | + self.assertEqual( |
678 | + CodeImportReviewStatus.REVIEWED, |
679 | + code_import.review_status) |
680 | + # A job is created for the import. |
681 | + self.assertIsNot(None, code_import.import_job) |
682 | + |
683 | def test_git_import_reviewed(self): |
684 | """A new git import is always reviewed by default.""" |
685 | code_import = CodeImportSet().new( |
686 | @@ -147,6 +176,21 @@ |
687 | # A job is created for the import. |
688 | self.assertIsNot(None, code_import.import_job) |
689 | |
690 | + def test_bzr_import_reviewed(self): |
691 | + """A new bzr import is always reviewed by default.""" |
692 | + code_import = CodeImportSet().new( |
693 | + registrant=self.factory.makePerson(), |
694 | + target=IBranchTarget(self.factory.makeProduct()), |
695 | + branch_name='mirrored', |
696 | + rcs_type=RevisionControlSystems.BZR, |
697 | + url=self.factory.getUniqueURL(), |
698 | + review_status=None) |
699 | + self.assertEqual( |
700 | + CodeImportReviewStatus.REVIEWED, |
701 | + code_import.review_status) |
702 | + # A job is created for the import. |
703 | + self.assertIsNot(None, code_import.import_job) |
704 | + |
705 | def test_junk_code_import_rejected(self): |
706 | """You are not allowed to create code imports targetting +junk.""" |
707 | registrant = self.factory.makePerson() |
708 | |
709 | === modified file 'lib/lp/codehosting/codeimport/tests/servers.py' |
710 | --- lib/lp/codehosting/codeimport/tests/servers.py 2011-06-02 10:48:54 +0000 |
711 | +++ lib/lp/codehosting/codeimport/tests/servers.py 2011-07-18 11:13:26 +0000 |
712 | @@ -4,6 +4,7 @@ |
713 | """Server classes that know how to create various kinds of foreign archive.""" |
714 | |
715 | __all__ = [ |
716 | + 'BzrServer', |
717 | 'CVSServer', |
718 | 'GitServer', |
719 | 'MercurialServer', |
720 | @@ -21,6 +22,8 @@ |
721 | import tempfile |
722 | import time |
723 | |
724 | +from bzrlib.bzrdir import BzrDir |
725 | +from bzrlib.branchbuilder import BranchBuilder |
726 | from bzrlib.tests.treeshape import build_tree_contents |
727 | from bzrlib.transport import Server |
728 | from bzrlib.urlutils import ( |
729 | @@ -246,3 +249,21 @@ |
730 | f.close() |
731 | repo[None].add([filename]) |
732 | repo.commit(text='<The commit message>', user='jane Foo <joe@foo.com>') |
733 | + |
734 | + |
735 | +class BzrServer(Server): |
736 | + |
737 | + def __init__(self, repo_url): |
738 | + super(BzrServer, self).__init__() |
739 | + self.repo_url = repo_url |
740 | + |
741 | + def makeRepo(self, tree_contents): |
742 | + branch = BzrDir.create_branch_convenience(self.repo_url) |
743 | + branch.get_config().set_user_option("create_signatures", "never") |
744 | + builder = BranchBuilder(branch=branch) |
745 | + actions = [('add', ('', 'tree-root', 'directory', None))] |
746 | + actions += [('add', (path, path+'-id', 'file', content)) for (path, |
747 | + content) in tree_contents] |
748 | + builder.build_snapshot(None, None, |
749 | + actions, committer='Joe Foo <joe@foo.com>', |
750 | + message=u'<The commit message>') |
751 | |
752 | === modified file 'lib/lp/codehosting/codeimport/tests/test_worker.py' |
753 | --- lib/lp/codehosting/codeimport/tests/test_worker.py 2011-06-21 05:38:15 +0000 |
754 | +++ lib/lp/codehosting/codeimport/tests/test_worker.py 2011-07-18 11:13:26 +0000 |
755 | @@ -17,6 +17,9 @@ |
756 | Branch, |
757 | BranchReferenceFormat, |
758 | ) |
759 | +from bzrlib.branchbuilder import ( |
760 | + BranchBuilder, |
761 | + ) |
762 | from bzrlib.bzrdir import ( |
763 | BzrDir, |
764 | BzrDirFormat, |
765 | @@ -24,12 +27,10 @@ |
766 | ) |
767 | from bzrlib.errors import ( |
768 | NoSuchFile, |
769 | - NotBranchError, |
770 | ) |
771 | from bzrlib.tests import TestCaseWithTransport |
772 | from bzrlib import trace |
773 | from bzrlib.transport import get_transport |
774 | -from bzrlib.upgrade import upgrade |
775 | from bzrlib.urlutils import ( |
776 | join as urljoin, |
777 | local_path_from_url, |
778 | @@ -49,6 +50,7 @@ |
779 | extract_tarball, |
780 | ) |
781 | from lp.codehosting.codeimport.tests.servers import ( |
782 | + BzrServer, |
783 | CVSServer, |
784 | GitServer, |
785 | MercurialServer, |
786 | @@ -56,6 +58,7 @@ |
787 | ) |
788 | from lp.codehosting.codeimport.worker import ( |
789 | BazaarBranchStore, |
790 | + BzrImportWorker, |
791 | BzrSvnImportWorker, |
792 | CodeImportWorkerExitCode, |
793 | CSCVSImportWorker, |
794 | @@ -1001,10 +1004,10 @@ |
795 | # import should be rejected. |
796 | args = {'rcstype': self.rcstype} |
797 | reference_url = self.createBranchReference() |
798 | - if self.rcstype in ('git', 'bzr-svn', 'hg'): |
799 | + if self.rcstype in ('git', 'bzr-svn', 'hg', 'bzr'): |
800 | args['url'] = reference_url |
801 | else: |
802 | - raise AssertionError("unexpected rcs_type %r" % self.rcs_type) |
803 | + raise AssertionError("unexpected rcs_type %r" % self.rcstype) |
804 | source_details = self.factory.makeCodeImportSourceDetails(**args) |
805 | worker = self.makeImportWorker(source_details) |
806 | self.assertEqual( |
807 | @@ -1162,5 +1165,51 @@ |
808 | self.bazaar_store, logging.getLogger()) |
809 | |
810 | |
811 | +class TestBzrImport(WorkerTest, TestActualImportMixin, |
812 | + PullingImportWorkerTests): |
813 | + |
814 | + rcstype = 'bzr' |
815 | + |
816 | + def setUp(self): |
817 | + super(TestBzrImport, self).setUp() |
818 | + self.setUpImport() |
819 | + |
820 | + def makeImportWorker(self, source_details): |
821 | + """Make a new `ImportWorker`.""" |
822 | + return BzrImportWorker( |
823 | + source_details, self.get_transport('import_data'), |
824 | + self.bazaar_store, logging.getLogger()) |
825 | + |
826 | + def makeForeignCommit(self, source_details): |
827 | + """Change the foreign tree, generating exactly one commit.""" |
828 | + branch = Branch.open(source_details.url) |
829 | + builder = BranchBuilder(branch=branch) |
830 | + builder.build_commit(message=self.factory.getUniqueString(), |
831 | + committer="Joe Random Hacker <joe@example.com>") |
832 | + self.foreign_commit_count += 1 |
833 | + |
834 | + def makeSourceDetails(self, branch_name, files): |
835 | + """Make Bzr `CodeImportSourceDetails` pointing at a real Bzr repo. |
836 | + """ |
837 | + repository_path = self.makeTemporaryDirectory() |
838 | + bzr_server = BzrServer(repository_path) |
839 | + bzr_server.start_server() |
840 | + self.addCleanup(bzr_server.stop_server) |
841 | + |
842 | + bzr_server.makeRepo(files) |
843 | + self.foreign_commit_count = 1 |
844 | + |
845 | + return self.factory.makeCodeImportSourceDetails( |
846 | + rcstype='bzr', url=repository_path) |
847 | + |
848 | + def test_partial(self): |
849 | + self.skip( |
850 | + "Partial fetching is not supported for native bzr branches " |
851 | + "at the moment.") |
852 | + |
853 | + def test_unsupported_feature(self): |
854 | + self.skip("All Bazaar features are supported by Bazaar.") |
855 | + |
856 | + |
857 | def test_suite(): |
858 | return unittest.TestLoader().loadTestsFromName(__name__) |
859 | |
860 | === modified file 'lib/lp/codehosting/codeimport/tests/test_workermonitor.py' |
861 | --- lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2011-06-16 23:43:04 +0000 |
862 | +++ lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2011-07-18 11:13:26 +0000 |
863 | @@ -50,6 +50,7 @@ |
864 | from lp.code.model.codeimportjob import CodeImportJob |
865 | from lp.codehosting import load_optional_plugin |
866 | from lp.codehosting.codeimport.tests.servers import ( |
867 | + BzrServer, |
868 | CVSServer, |
869 | GitServer, |
870 | MercurialServer, |
871 | @@ -682,6 +683,16 @@ |
872 | |
873 | return self.factory.makeCodeImport(hg_repo_url=self.repo_path) |
874 | |
875 | + def makeBzrCodeImport(self): |
876 | + """Make a `CodeImport` that points to a real Bazaar branch.""" |
877 | + self.bzr_server = BzrServer(self.repo_path) |
878 | + self.bzr_server.start_server() |
879 | + self.addCleanup(self.bzr_server.stop_server) |
880 | + |
881 | + self.bzr_server.makeRepo([('README', 'contents')]) |
882 | + self.foreign_commit_count = 1 |
883 | + return self.factory.makeCodeImport(bzr_branch_url=self.repo_path) |
884 | + |
885 | def getStartedJobForImport(self, code_import): |
886 | """Get a started `CodeImportJob` for `code_import`. |
887 | |
888 | @@ -782,6 +793,15 @@ |
889 | result = self.performImport(job_id) |
890 | return result.addCallback(self.assertImported, code_import_id) |
891 | |
892 | + def test_import_bzr(self): |
893 | + # Create a Bazaar CodeImport and import it. |
894 | + job = self.getStartedJobForImport(self.makeBzrCodeImport()) |
895 | + code_import_id = job.code_import.id |
896 | + job_id = job.id |
897 | + self.layer.txn.commit() |
898 | + result = self.performImport(job_id) |
899 | + return result.addCallback(self.assertImported, code_import_id) |
900 | + |
901 | # XXX 2010-03-24 MichaelHudson, bug=541526: This test fails intermittently |
902 | # in EC2. |
903 | def DISABLED_test_import_bzrsvn(self): |
904 | |
905 | === modified file 'lib/lp/codehosting/codeimport/worker.py' |
906 | --- lib/lp/codehosting/codeimport/worker.py 2011-06-21 05:38:15 +0000 |
907 | +++ lib/lp/codehosting/codeimport/worker.py 2011-07-18 11:13:26 +0000 |
908 | @@ -6,6 +6,7 @@ |
909 | __metaclass__ = type |
910 | __all__ = [ |
911 | 'BazaarBranchStore', |
912 | + 'BzrImportWorker', |
913 | 'BzrSvnImportWorker', |
914 | 'CSCVSImportWorker', |
915 | 'CodeImportSourceDetails', |
916 | @@ -190,9 +191,9 @@ |
917 | |
918 | :ivar branch_id: The id of the branch associated to this code import, used |
919 | for locating the existing import and the foreign tree. |
920 | - :ivar rcstype: 'svn' or 'cvs' as appropriate. |
921 | + :ivar rcstype: 'svn', 'cvs', 'hg', 'git', 'bzr-svn', 'bzr' as appropriate. |
922 | :ivar url: The branch URL if rcstype in ['svn', 'bzr-svn', |
923 | - 'git'], None otherwise. |
924 | + 'git', 'hg', 'bzr'], None otherwise. |
925 | :ivar cvs_root: The $CVSROOT if rcstype == 'cvs', None otherwise. |
926 | :ivar cvs_module: The CVS module if rcstype == 'cvs', None otherwise. |
927 | """ |
928 | @@ -210,7 +211,7 @@ |
929 | """Convert command line-style arguments to an instance.""" |
930 | branch_id = int(arguments.pop(0)) |
931 | rcstype = arguments.pop(0) |
932 | - if rcstype in ['svn', 'bzr-svn', 'git', 'hg']: |
933 | + if rcstype in ['svn', 'bzr-svn', 'git', 'hg', 'bzr']: |
934 | [url] = arguments |
935 | cvs_root = cvs_module = None |
936 | elif rcstype == 'cvs': |
937 | @@ -237,6 +238,8 @@ |
938 | return cls(branch_id, 'git', str(code_import.url)) |
939 | elif code_import.rcs_type == RevisionControlSystems.HG: |
940 | return cls(branch_id, 'hg', str(code_import.url)) |
941 | + elif code_import.rcs_type == RevisionControlSystems.BZR: |
942 | + return cls(branch_id, 'bzr', str(code_import.url)) |
943 | else: |
944 | raise AssertionError("Unknown rcstype %r." % code_import.rcs_type) |
945 | |
946 | @@ -244,7 +247,7 @@ |
947 | """Return a list of arguments suitable for passing to a child process. |
948 | """ |
949 | result = [str(self.branch_id), self.rcstype] |
950 | - if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg']: |
951 | + if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg', 'bzr']: |
952 | result.append(self.url) |
953 | elif self.rcstype == 'cvs': |
954 | result.append(self.cvs_root) |
955 | @@ -619,9 +622,19 @@ |
956 | except NotBranchError: |
957 | pass |
958 | else: |
959 | +<<<<<<< TREE |
960 | self._logger.info("No branch found at remote location.") |
961 | return CodeImportWorkerExitCode.FAILURE_INVALID |
962 | remote_branch = format.open(transport).open_branch() |
963 | +======= |
964 | + self._logger.info("No branch found at remote location.") |
965 | + return CodeImportWorkerExitCode.FAILURE_INVALID |
966 | + remote_dir = format.open(transport) |
967 | + if remote_dir.get_branch_reference() is not None: |
968 | + self._logger.info("Remote branch is a branch reference.") |
969 | + return CodeImportWorkerExitCode.FAILURE_INVALID |
970 | + remote_branch = remote_dir.open_branch() |
971 | +>>>>>>> MERGE-SOURCE |
972 | remote_branch_tip = remote_branch.last_revision() |
973 | inter_branch = InterBranch.get(remote_branch, bazaar_branch) |
974 | self._logger.info("Importing branch.") |
975 | @@ -822,3 +835,21 @@ |
976 | """See `PullingImportWorker.probers`.""" |
977 | from bzrlib.plugins.svn import SvnRemoteProber |
978 | return [SvnRemoteProber] |
979 | + |
980 | + |
981 | +class BzrImportWorker(PullingImportWorker): |
982 | + """An import worker for importing Bazaar branches.""" |
983 | + |
984 | + invalid_branch_exceptions = [] |
985 | + unsupported_feature_exceptions = [] |
986 | + |
987 | + def getRevisionLimit(self): |
988 | + """See `PullingImportWorker.getRevisionLimit`.""" |
989 | + # For now, just grab the whole branch at once |
990 | + return None |
991 | + |
992 | + @property |
993 | + def probers(self): |
994 | + """See `PullingImportWorker.probers`.""" |
995 | + from bzrlib.bzrdir import BzrProber, RemoteBzrProber |
996 | + return [BzrProber, RemoteBzrProber] |
997 | |
998 | === modified file 'lib/lp/registry/browser/productseries.py' |
999 | --- lib/lp/registry/browser/productseries.py 2011-05-27 21:12:25 +0000 |
1000 | +++ lib/lp/registry/browser/productseries.py 2011-07-18 11:13:26 +0000 |
1001 | @@ -827,15 +827,6 @@ |
1002 | )) |
1003 | |
1004 | |
1005 | -class RevisionControlSystemsExtended(RevisionControlSystems): |
1006 | - """External RCS plus Bazaar.""" |
1007 | - BZR = DBItem(99, """ |
1008 | - Bazaar |
1009 | - |
1010 | - External Bazaar branch. |
1011 | - """) |
1012 | - |
1013 | - |
1014 | class SetBranchForm(Interface): |
1015 | """The fields presented on the form for setting a branch.""" |
1016 | |
1017 | @@ -844,7 +835,7 @@ |
1018 | ['cvs_module']) |
1019 | |
1020 | rcs_type = Choice(title=_("Type of RCS"), |
1021 | - required=False, vocabulary=RevisionControlSystemsExtended, |
1022 | + required=False, vocabulary=RevisionControlSystems, |
1023 | description=_( |
1024 | "The version control system to import from. ")) |
1025 | |
1026 | @@ -908,7 +899,7 @@ |
1027 | @property |
1028 | def initial_values(self): |
1029 | return dict( |
1030 | - rcs_type=RevisionControlSystemsExtended.BZR, |
1031 | + rcs_type=RevisionControlSystems.BZR, |
1032 | branch_type=LINK_LP_BZR, |
1033 | branch_location=self.context.branch) |
1034 | |
1035 | @@ -989,7 +980,7 @@ |
1036 | self.setFieldError( |
1037 | 'rcs_type', |
1038 | 'You must specify the type of RCS for the remote host.') |
1039 | - elif rcs_type == RevisionControlSystemsExtended.CVS: |
1040 | + elif rcs_type == RevisionControlSystems.CVS: |
1041 | if 'cvs_module' not in data: |
1042 | self.setFieldError( |
1043 | 'cvs_module', |
1044 | @@ -1022,8 +1013,9 @@ |
1045 | # Extend the allowed schemes for the repository URL based on |
1046 | # rcs_type. |
1047 | extra_schemes = { |
1048 | - RevisionControlSystemsExtended.BZR_SVN: ['svn'], |
1049 | - RevisionControlSystemsExtended.GIT: ['git'], |
1050 | + RevisionControlSystems.BZR_SVN: ['svn'], |
1051 | + RevisionControlSystems.GIT: ['git'], |
1052 | + RevisionControlSystems.BZR: ['bzr'], |
1053 | } |
1054 | schemes.update(extra_schemes.get(rcs_type, [])) |
1055 | return schemes |
1056 | @@ -1050,7 +1042,7 @@ |
1057 | # The branch location is not required for validation. |
1058 | self._setRequired(['branch_location'], False) |
1059 | # The cvs_module is required if it is a CVS import. |
1060 | - if rcs_type == RevisionControlSystemsExtended.CVS: |
1061 | + if rcs_type == RevisionControlSystems.CVS: |
1062 | self._setRequired(['cvs_module'], True) |
1063 | else: |
1064 | raise AssertionError("Unknown branch type %s" % branch_type) |
1065 | @@ -1110,7 +1102,7 @@ |
1066 | # Either create an externally hosted bzr branch |
1067 | # (a.k.a. 'mirrored') or create a new code import. |
1068 | rcs_type = data.get('rcs_type') |
1069 | - if rcs_type == RevisionControlSystemsExtended.BZR: |
1070 | + if rcs_type == RevisionControlSystems.BZR: |
1071 | branch = self._createBzrBranch( |
1072 | BranchType.MIRRORED, branch_name, branch_owner, |
1073 | data['repo_url']) |
1074 | @@ -1123,7 +1115,7 @@ |
1075 | 'the series.') |
1076 | else: |
1077 | # We need to create an import request. |
1078 | - if rcs_type == RevisionControlSystemsExtended.CVS: |
1079 | + if rcs_type == RevisionControlSystems.CVS: |
1080 | cvs_root = data.get('repo_url') |
1081 | cvs_module = data.get('cvs_module') |
1082 | url = None |
1083 | |
1084 | === modified file 'lib/lp/registry/browser/tests/test_distroseries.py' |
1085 | --- lib/lp/registry/browser/tests/test_distroseries.py 2011-07-07 20:07:51 +0000 |
1086 | +++ lib/lp/registry/browser/tests/test_distroseries.py 2011-07-18 11:13:26 +0000 |
1087 | @@ -32,11 +32,16 @@ |
1088 | from canonical.config import config |
1089 | from canonical.database.constants import UTC_NOW |
1090 | from canonical.database.sqlbase import flush_database_caches |
1091 | +<<<<<<< TREE |
1092 | from canonical.launchpad.testing.pages import ( |
1093 | extract_text, |
1094 | find_tag_by_id, |
1095 | ) |
1096 | from canonical.launchpad.webapp.authorization import check_permission |
1097 | +======= |
1098 | +from canonical.launchpad.testing.pages import find_tag_by_id |
1099 | +from canonical.launchpad.webapp.authorization import check_permission |
1100 | +>>>>>>> MERGE-SOURCE |
1101 | from canonical.launchpad.webapp.batching import BatchNavigator |
1102 | from canonical.launchpad.webapp.interaction import get_current_principal |
1103 | from canonical.launchpad.webapp.interfaces import BrowserNotificationLevel |
1104 | @@ -829,6 +834,60 @@ |
1105 | check_permission('launchpad.Edit', view)) |
1106 | |
1107 | |
1108 | +class TestDistroSeriesInitializeViewAccess(TestCaseWithFactory): |
1109 | + """Test access to IDS.+initseries.""" |
1110 | + |
1111 | + layer = LaunchpadFunctionalLayer |
1112 | + |
1113 | + def setUp(self): |
1114 | + super(TestDistroSeriesInitializeViewAccess, |
1115 | + self).setUp('foo.bar@canonical.com') |
1116 | + set_derived_series_ui_feature_flag(self) |
1117 | + |
1118 | + def test_initseries_access_anon(self): |
1119 | + # Anonymous users cannot access +initseries. |
1120 | + distroseries = self.factory.makeDistroSeries() |
1121 | + view = create_initialized_view(distroseries, "+initseries") |
1122 | + login(ANONYMOUS) |
1123 | + |
1124 | + self.assertEqual( |
1125 | + False, |
1126 | + check_permission('launchpad.Edit', view)) |
1127 | + |
1128 | + def test_initseries_access_simpleuser(self): |
1129 | + # Unprivileged users cannot access +initseries. |
1130 | + distroseries = self.factory.makeDistroSeries() |
1131 | + view = create_initialized_view(distroseries, "+initseries") |
1132 | + login_person(self.factory.makePerson()) |
1133 | + |
1134 | + self.assertEqual( |
1135 | + False, |
1136 | + check_permission('launchpad.Edit', view)) |
1137 | + |
1138 | + def test_initseries_access_admin(self): |
1139 | + # Users with lp.Admin can access +initseries. |
1140 | + distroseries = self.factory.makeDistroSeries() |
1141 | + view = create_initialized_view(distroseries, "+initseries") |
1142 | + login_celebrity('admin') |
1143 | + |
1144 | + self.assertEqual( |
1145 | + True, |
1146 | + check_permission('launchpad.Edit', view)) |
1147 | + |
1148 | + def test_initseries_access_driver(self): |
1149 | + # Distroseries drivers can access +initseries. |
1150 | + distroseries = self.factory.makeDistroSeries() |
1151 | + view = create_initialized_view(distroseries, "+initseries") |
1152 | + driver = self.factory.makePerson() |
1153 | + with celebrity_logged_in('admin'): |
1154 | + distroseries.driver = driver |
1155 | + login_person(driver) |
1156 | + |
1157 | + self.assertEqual( |
1158 | + True, |
1159 | + check_permission('launchpad.Edit', view)) |
1160 | + |
1161 | + |
1162 | class DistroSeriesDifferenceMixin: |
1163 | """A helper class for testing differences pages""" |
1164 | |
1165 | |
1166 | === modified file 'lib/lp/registry/interfaces/distroseries.py' |
1167 | --- lib/lp/registry/interfaces/distroseries.py 2011-07-07 20:07:51 +0000 |
1168 | +++ lib/lp/registry/interfaces/distroseries.py 2011-07-18 11:13:26 +0000 |
1169 | @@ -1075,6 +1075,7 @@ |
1170 | released == None will do no filtering on status. |
1171 | """ |
1172 | |
1173 | +<<<<<<< TREE |
1174 | def priorReleasedSeries(self, distribution, prior_to_date): |
1175 | """Find distroseries for the supplied distro released before a |
1176 | certain date. |
1177 | @@ -1089,6 +1090,10 @@ |
1178 | |
1179 | |
1180 | @error_status(httplib.BAD_REQUEST) |
1181 | +======= |
1182 | + |
1183 | +@error_status(httplib.BAD_REQUEST) |
1184 | +>>>>>>> MERGE-SOURCE |
1185 | class DerivationError(Exception): |
1186 | """Raised when there is a problem deriving a distroseries.""" |
1187 | _message_prefix = "Error deriving distro series" |
1188 | |
1189 | === modified file 'lib/lp/registry/model/distribution.py' |
1190 | --- lib/lp/registry/model/distribution.py 2011-07-14 14:08:04 +0000 |
1191 | +++ lib/lp/registry/model/distribution.py 2011-07-18 11:13:26 +0000 |
1192 | @@ -655,6 +655,7 @@ |
1193 | """See `IBugTarget`.""" |
1194 | return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self)) |
1195 | |
1196 | +<<<<<<< TREE |
1197 | def getBranchTips(self, since=None): |
1198 | """See `IDistribution`.""" |
1199 | query = """ |
1200 | @@ -684,6 +685,17 @@ |
1201 | # to the end of the current record, removing Nones from the list. |
1202 | result[-1].append(filter(None, map(itemgetter(-1), group))) |
1203 | return result |
1204 | +======= |
1205 | + def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, |
1206 | + include_tags=None): |
1207 | + """See IBugTarget.""" |
1208 | + # Circular fail. |
1209 | + from lp.bugs.model.bugsummary import BugSummary |
1210 | + return get_bug_tags_open_count( |
1211 | + And(BugSummary.distribution_id == self.id, |
1212 | + BugSummary.sourcepackagename_id == None), |
1213 | + user, tag_limit=tag_limit, include_tags=include_tags) |
1214 | +>>>>>>> MERGE-SOURCE |
1215 | |
1216 | def getMirrorByName(self, name): |
1217 | """See `IDistribution`.""" |
1218 | @@ -1414,6 +1426,7 @@ |
1219 | # Note that in the source package case, we don't restrict |
1220 | # the search to the distribution release, making a best |
1221 | # effort to find a package. |
1222 | +<<<<<<< TREE |
1223 | publishing = IStore(SourcePackagePublishingHistory).find( |
1224 | SourcePackagePublishingHistory, |
1225 | # We use an extra query to get the IDs instead of an |
1226 | @@ -1431,6 +1444,20 @@ |
1227 | PackagePublishingStatus.PENDING) |
1228 | )).order_by( |
1229 | Desc(SourcePackagePublishingHistory.id)).first() |
1230 | +======= |
1231 | + publishing = IStore(SourcePackagePublishingHistory).find( |
1232 | + SourcePackagePublishingHistory, |
1233 | + SourcePackagePublishingHistory.archiveID.is_in( |
1234 | + self.all_distro_archive_ids), |
1235 | + SourcePackagePublishingHistory.sourcepackagereleaseID == |
1236 | + SourcePackageRelease.id, |
1237 | + SourcePackageRelease.sourcepackagename == sourcepackagename, |
1238 | + SourcePackagePublishingHistory.status.is_in( |
1239 | + (PackagePublishingStatus.PUBLISHED, |
1240 | + PackagePublishingStatus.PENDING) |
1241 | + )).order_by( |
1242 | + Desc(SourcePackagePublishingHistory.id)).first() |
1243 | +>>>>>>> MERGE-SOURCE |
1244 | if publishing is not None: |
1245 | return sourcepackagename |
1246 | |
1247 | @@ -1453,6 +1480,7 @@ |
1248 | # the sourcepackagename from that. |
1249 | bpph = IStore(BinaryPackagePublishingHistory).find( |
1250 | BinaryPackagePublishingHistory, |
1251 | +<<<<<<< TREE |
1252 | # See comment above for rationale for using an extra query |
1253 | # instead of an inner join. (Bottom line, it would time out |
1254 | # otherwise.) |
1255 | @@ -1460,6 +1488,12 @@ |
1256 | self.all_distro_archive_ids), |
1257 | BinaryPackagePublishingHistory.binarypackagereleaseID == |
1258 | BinaryPackageRelease.id, |
1259 | +======= |
1260 | + BinaryPackagePublishingHistory.archiveID.is_in( |
1261 | + self.all_distro_archive_ids), |
1262 | + BinaryPackagePublishingHistory.binarypackagereleaseID == |
1263 | + BinaryPackageRelease.id, |
1264 | +>>>>>>> MERGE-SOURCE |
1265 | BinaryPackageRelease.binarypackagename == binarypackagename, |
1266 | BinaryPackagePublishingHistory.dateremoved == None, |
1267 | ).order_by( |
1268 | |
1269 | === modified file 'lib/lp/registry/model/product.py' |
1270 | --- lib/lp/registry/model/product.py 2011-06-21 01:34:08 +0000 |
1271 | +++ lib/lp/registry/model/product.py 2011-07-18 11:13:26 +0000 |
1272 | @@ -800,6 +800,18 @@ |
1273 | """See `IBugTarget`.""" |
1274 | return get_bug_tags("BugTask.product = %s" % sqlvalues(self)) |
1275 | |
1276 | +<<<<<<< TREE |
1277 | +======= |
1278 | + def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, |
1279 | + include_tags=None): |
1280 | + """See IBugTarget.""" |
1281 | + # Circular fail. |
1282 | + from lp.bugs.model.bugsummary import BugSummary |
1283 | + return get_bug_tags_open_count( |
1284 | + BugSummary.product_id == self.id, |
1285 | + user, tag_limit=tag_limit, include_tags=include_tags) |
1286 | + |
1287 | +>>>>>>> MERGE-SOURCE |
1288 | series = SQLMultipleJoin('ProductSeries', joinColumn='product', |
1289 | orderBy='name') |
1290 | |
1291 | |
1292 | === modified file 'lib/lp/soyuz/browser/queue.py' |
1293 | --- lib/lp/soyuz/browser/queue.py 2011-06-29 07:25:18 +0000 |
1294 | +++ lib/lp/soyuz/browser/queue.py 2011-07-18 11:13:26 +0000 |
1295 | @@ -46,6 +46,7 @@ |
1296 | QueueInconsistentStateError, |
1297 | ) |
1298 | from lp.soyuz.interfaces.section import ISectionSet |
1299 | +from lp.soyuz.model.queue import PackageUploadSource |
1300 | |
1301 | |
1302 | QUEUE_SIZE = 30 |
1303 | @@ -197,10 +198,16 @@ |
1304 | CompletePackageUpload. This avoids many additional SQL queries |
1305 | in the +queue template. |
1306 | """ |
1307 | +<<<<<<< TREE |
1308 | # Avoid circular imports. |
1309 | from lp.soyuz.model.queue import PackageUploadSource |
1310 | from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease |
1311 | |
1312 | +======= |
1313 | + # Avoid circular imports. |
1314 | + from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease |
1315 | + |
1316 | +>>>>>>> MERGE-SOURCE |
1317 | uploads = list(self.batchnav.currentBatch()) |
1318 | |
1319 | if len(uploads) == 0: |
1320 | @@ -214,9 +221,14 @@ |
1321 | upload.status != PackageUploadStatus.DONE)] |
1322 | binary_file_set = getUtility(IBinaryPackageFileSet) |
1323 | binary_files = binary_file_set.getByPackageUploadIDs(upload_ids) |
1324 | +<<<<<<< TREE |
1325 | binary_file_set.loadLibraryFiles(binary_files) |
1326 | packageuploadsources = load_referencing( |
1327 | PackageUploadSource, uploads, ['packageuploadID']) |
1328 | +======= |
1329 | + packageuploadsources = load_referencing( |
1330 | + PackageUploadSource, uploads, ['packageuploadID']) |
1331 | +>>>>>>> MERGE-SOURCE |
1332 | source_file_set = getUtility(ISourcePackageReleaseFileSet) |
1333 | source_files = source_file_set.getByPackageUploadIDs(upload_ids) |
1334 | |
1335 | @@ -490,6 +502,12 @@ |
1336 | else: |
1337 | self.package_sets = [] |
1338 | |
1339 | + if self.contains_source: |
1340 | + self.package_sets = package_sets.get( |
1341 | + self.sourcepackagerelease.sourcepackagenameID, []) |
1342 | + else: |
1343 | + self.package_sets = [] |
1344 | + |
1345 | @property |
1346 | def pending_delayed_copy(self): |
1347 | """Whether the context is a delayed-copy pending processing.""" |
1348 | @@ -509,6 +527,7 @@ |
1349 | return self.context.changesfile |
1350 | |
1351 | @property |
1352 | +<<<<<<< TREE |
1353 | def display_package_sets(self): |
1354 | """Package sets, if any, for display on the +queue page.""" |
1355 | return ' '.join(sorted( |
1356 | @@ -592,3 +611,89 @@ |
1357 | (%s) |
1358 | </div> |
1359 | """ % (iconlist_id, '\n'.join(icons), link, self.displayarchs) |
1360 | +======= |
1361 | + def display_package_sets(self): |
1362 | + """Package sets, if any, for display on the +queue page.""" |
1363 | + if self.contains_source: |
1364 | + return ' '.join(sorted( |
1365 | + packageset.name for packageset in self.package_sets)) |
1366 | + else: |
1367 | + return "" |
1368 | + |
1369 | + @property |
1370 | + def display_component(self): |
1371 | + """Component name, if any, for display on the +queue page.""" |
1372 | + if self.contains_source: |
1373 | + return self.component_name.lower() |
1374 | + else: |
1375 | + return "" |
1376 | + |
1377 | + @property |
1378 | + def display_section(self): |
1379 | + """Section name, if any, for display on the +queue page.""" |
1380 | + if self.contains_source: |
1381 | + return self.section_name.lower() |
1382 | + else: |
1383 | + return "" |
1384 | + |
1385 | + @property |
1386 | + def display_priority(self): |
1387 | + """Priority name, if any, for display on the +queue page.""" |
1388 | + if self.contains_source: |
1389 | + return self.sourcepackagerelease.urgency.name.lower() |
1390 | + else: |
1391 | + return "" |
1392 | + |
1393 | + def composeIcon(self, alt, icon, title=None): |
1394 | + """Compose an icon for the package's icon list.""" |
1395 | + # These should really be sprites! |
1396 | + if title is None: |
1397 | + title = alt |
1398 | + return '<img alt="[%s]" src="/@@/%s" title="%s" />' % ( |
1399 | + cgi.escape(alt, quote=True), |
1400 | + icon, |
1401 | + cgi.escape(title, quote=True), |
1402 | + ) |
1403 | + |
1404 | + def composeIconList(self): |
1405 | + """List icons that should be shown for this upload.""" |
1406 | + ddtp = "Debian Description Translation Project Indexes" |
1407 | + potential_icons = [ |
1408 | + (self.contains_source, ("Source", 'package-source')), |
1409 | + (self.contains_build, ("Build", 'package-binary', "Binary")), |
1410 | + (self.contains_translation, ("Translation", 'translation-file')), |
1411 | + (self.contains_installer, ("Installer", 'ubuntu-icon')), |
1412 | + (self.contains_upgrader, ("Upgrader", 'ubuntu-icon')), |
1413 | + (self.contains_ddtp, (ddtp, 'ubuntu-icon')), |
1414 | + ] |
1415 | + return [ |
1416 | + self.composeIcon(*details) |
1417 | + for condition, details in potential_icons |
1418 | + if condition] |
1419 | + |
1420 | + def composeNameAndChangesLink(self): |
1421 | + """Compose HTML: upload name and link to changes file.""" |
1422 | + raw_displayname = self.displayname |
1423 | + displayname = cgi.escape(raw_displayname) |
1424 | + if self.pending_delayed_copy or self.changesfile is None: |
1425 | + return displayname |
1426 | + else: |
1427 | + return '<a href="%s" title="Changes file for %s">%s</a>' % ( |
1428 | + self.changesfile.http_url, |
1429 | + cgi.escape(self.displayname, quote=True), |
1430 | + displayname) |
1431 | + |
1432 | + @property |
1433 | + def icons_and_name(self): |
1434 | + """Icon list and name, linked to changes file if appropriate.""" |
1435 | + iconlist_id = "queue%d-iconlist" % self.id |
1436 | + icons = self.composeIconList() |
1437 | + link = self.composeNameAndChangesLink() |
1438 | + return """ |
1439 | + <div id="%s"> |
1440 | + %s |
1441 | + %s |
1442 | + (%s) |
1443 | + </div> |
1444 | + """ % (iconlist_id, '\n'.join(icons), link, self.displayarchs) |
1445 | +>>>>>>> MERGE-SOURCE |
1446 | |
1447 | === modified file 'lib/lp/soyuz/browser/tests/test_queue.py' |
1448 | --- lib/lp/soyuz/browser/tests/test_queue.py 2011-06-23 13:05:52 +0000 |
1449 | +++ lib/lp/soyuz/browser/tests/test_queue.py 2011-07-18 11:13:26 +0000 |
1450 | @@ -234,6 +234,7 @@ |
1451 | upload.distroseries.main_archive) |
1452 | with person_logged_in(queue_admin): |
1453 | view = self.makeView(upload.distroseries, queue_admin) |
1454 | +<<<<<<< TREE |
1455 | html_text = view() |
1456 | self.assertIn(upload.package_name, html_text) |
1457 | |
1458 | @@ -414,3 +415,168 @@ |
1459 | self.assertNotEqual(None, icons_and_name.find("a")) |
1460 | self.assertIn( |
1461 | complete_upload.displayarchs, ' '.join(icons_and_name.itertext())) |
1462 | +======= |
1463 | + html_text = view() |
1464 | + self.assertIn(upload.package_name, html_text) |
1465 | + |
1466 | + |
1467 | +class TestCompletePackageUpload(TestCaseWithFactory): |
1468 | + |
1469 | + layer = LaunchpadZopelessLayer |
1470 | + |
1471 | + def makeCompletePackageUpload(self, upload=None, build_upload_files=None, |
1472 | + source_upload_files=None, |
1473 | + package_sets=None): |
1474 | + if upload is None: |
1475 | + upload = self.factory.makeSourcePackageUpload() |
1476 | + if build_upload_files is None: |
1477 | + build_upload_files = {} |
1478 | + if source_upload_files is None: |
1479 | + source_upload_files = {} |
1480 | + if package_sets is None: |
1481 | + package_sets = {} |
1482 | + return CompletePackageUpload( |
1483 | + upload, build_upload_files, source_upload_files, package_sets) |
1484 | + |
1485 | + def mapPackageSets(self, upload, package_sets=None): |
1486 | + if package_sets is None: |
1487 | + package_sets = [self.factory.makePackageset( |
1488 | + distroseries=upload.distroseries)] |
1489 | + spn = upload.sourcepackagerelease.sourcepackagename |
1490 | + return {spn.id: package_sets} |
1491 | + |
1492 | + def test_display_package_sets_returns_source_upload_packagesets(self): |
1493 | + upload = self.factory.makeSourcePackageUpload() |
1494 | + package_sets = self.mapPackageSets(upload) |
1495 | + complete_upload = self.makeCompletePackageUpload( |
1496 | + upload, package_sets=package_sets) |
1497 | + self.assertEqual( |
1498 | + package_sets.values()[0][0].name, |
1499 | + complete_upload.display_package_sets) |
1500 | + |
1501 | + def test_display_package_sets_returns_empty_for_non_source_upload(self): |
1502 | + upload = self.factory.makeBuildPackageUpload() |
1503 | + complete_upload = self.makeCompletePackageUpload( |
1504 | + upload, package_sets=self.mapPackageSets(upload)) |
1505 | + self.assertEqual("", complete_upload.display_package_sets) |
1506 | + |
1507 | + def test_display_package_sets_sorts_by_name(self): |
1508 | + complete_upload = self.makeCompletePackageUpload() |
1509 | + distroseries = complete_upload.distroseries |
1510 | + complete_upload.package_sets = [ |
1511 | + self.factory.makePackageset(distroseries=distroseries, name=name) |
1512 | + for name in [u'ccc', u'aaa', u'bbb']] |
1513 | + self.assertEqual("aaa bbb ccc", complete_upload.display_package_sets) |
1514 | + |
1515 | + def test_display_component_returns_source_upload_component_name(self): |
1516 | + complete_upload = self.makeCompletePackageUpload() |
1517 | + self.assertEqual( |
1518 | + complete_upload.sourcepackagerelease.component.name.lower(), |
1519 | + complete_upload.display_component) |
1520 | + |
1521 | + def test_display_component_returns_empty_for_non_source_upload(self): |
1522 | + complete_upload = self.makeCompletePackageUpload( |
1523 | + self.factory.makeBuildPackageUpload()) |
1524 | + self.assertEqual('', complete_upload.display_component) |
1525 | + |
1526 | + def test_display_section_returns_source_upload_section_name(self): |
1527 | + complete_upload = self.makeCompletePackageUpload() |
1528 | + self.assertEqual( |
1529 | + complete_upload.sourcepackagerelease.section.name.lower(), |
1530 | + complete_upload.display_section) |
1531 | + |
1532 | + def test_display_section_returns_empty_for_non_source_upload(self): |
1533 | + complete_upload = self.makeCompletePackageUpload( |
1534 | + self.factory.makeBuildPackageUpload()) |
1535 | + self.assertEqual('', complete_upload.display_section) |
1536 | + |
1537 | + def test_display_priority_returns_source_upload_priority(self): |
1538 | + complete_upload = self.makeCompletePackageUpload() |
1539 | + self.assertEqual( |
1540 | + complete_upload.sourcepackagerelease.urgency.name.lower(), |
1541 | + complete_upload.display_priority) |
1542 | + |
1543 | + def test_display_priority_returns_empty_for_non_source_upload(self): |
1544 | + complete_upload = self.makeCompletePackageUpload( |
1545 | + self.factory.makeBuildPackageUpload()) |
1546 | + self.assertEqual('', complete_upload.display_priority) |
1547 | + |
1548 | + def test_composeIcon_produces_image_tag(self): |
1549 | + alt = self.factory.getUniqueString() |
1550 | + icon = self.factory.getUniqueString() + ".png" |
1551 | + title = self.factory.getUniqueString() |
1552 | + html_text = self.makeCompletePackageUpload().composeIcon( |
1553 | + alt, icon, title) |
1554 | + img = html.fromstring(html_text) |
1555 | + self.assertEqual("img", img.tag) |
1556 | + self.assertEqual("[%s]" % alt, img.get("alt")) |
1557 | + self.assertEqual("/@@/" + icon, img.get("src")) |
1558 | + self.assertEqual(title, img.get("title")) |
1559 | + |
1560 | + def test_composeIcon_title_defaults_to_alt_text(self): |
1561 | + alt = self.factory.getUniqueString() |
1562 | + icon = self.factory.getUniqueString() + ".png" |
1563 | + html_text = self.makeCompletePackageUpload().composeIcon(alt, icon) |
1564 | + img = html.fromstring(html_text) |
1565 | + self.assertEqual(alt, img.get("title")) |
1566 | + |
1567 | + def test_composeIcon_escapes_alt_and_title(self): |
1568 | + alt = 'alt"&' |
1569 | + icon = self.factory.getUniqueString() + ".png" |
1570 | + title = 'title"&' |
1571 | + html_text = self.makeCompletePackageUpload().composeIcon( |
1572 | + alt, icon, title) |
1573 | + img = html.fromstring(html_text) |
1574 | + self.assertEqual("[%s]" % alt, img.get("alt")) |
1575 | + self.assertEqual(title, img.get("title")) |
1576 | + |
1577 | + def test_composeIconList_produces_icons(self): |
1578 | + icons = self.makeCompletePackageUpload().composeIconList() |
1579 | + self.assertNotEqual([], icons) |
1580 | + self.assertEqual('img', html.fromstring(icons[0]).tag) |
1581 | + |
1582 | + def test_composeIconList_produces_icons_conditionally(self): |
1583 | + complete_upload = self.makeCompletePackageUpload() |
1584 | + base_count = len(complete_upload.composeIconList()) |
1585 | + complete_upload.contains_build = True |
1586 | + new_count = len(complete_upload.composeIconList()) |
1587 | + self.assertEqual(base_count + 1, new_count) |
1588 | + |
1589 | + def test_composeNameAndChangesLink_does_not_link_if_no_changes_file(self): |
1590 | + upload = self.factory.makeCopyJobPackageUpload() |
1591 | + complete_upload = self.makeCompletePackageUpload(upload) |
1592 | + self.assertEqual( |
1593 | + complete_upload.displayname, |
1594 | + complete_upload.composeNameAndChangesLink()) |
1595 | + |
1596 | + def test_composeNameAndChangesLink_links_to_changes_file(self): |
1597 | + complete_upload = self.makeCompletePackageUpload() |
1598 | + link = html.fromstring(complete_upload.composeNameAndChangesLink()) |
1599 | + self.assertEqual( |
1600 | + complete_upload.changesfile.http_url, link.get("href")) |
1601 | + |
1602 | + def test_composeNameAndChangesLink_escapes_nonlinked_display_name(self): |
1603 | + filename = 'name"&name' |
1604 | + upload = self.factory.makeCustomPackageUpload(filename=filename) |
1605 | + # Stop nameAndChangesLink from producing a link. |
1606 | + upload.changesfile = None |
1607 | + complete_upload = self.makeCompletePackageUpload(upload) |
1608 | + self.assertIn( |
1609 | + cgi.escape(filename), complete_upload.composeNameAndChangesLink()) |
1610 | + |
1611 | + def test_composeNameAndChangesLink_escapes_name_in_link(self): |
1612 | + filename = 'name"&name' |
1613 | + upload = self.factory.makeCustomPackageUpload(filename=filename) |
1614 | + complete_upload = self.makeCompletePackageUpload(upload) |
1615 | + link = html.fromstring(complete_upload.composeNameAndChangesLink()) |
1616 | + self.assertIn(filename, link.get("title")) |
1617 | + self.assertEqual(filename, link.text) |
1618 | + |
1619 | + def test_icons_and_name_composes_icons_and_link_and_archs(self): |
1620 | + complete_upload = self.makeCompletePackageUpload() |
1621 | + icons_and_name = html.fromstring(complete_upload.icons_and_name) |
1622 | + self.assertNotEqual(None, icons_and_name.find("img")) |
1623 | + self.assertNotEqual(None, icons_and_name.find("a")) |
1624 | + self.assertIn( |
1625 | + complete_upload.displayarchs, ' '.join(icons_and_name.itertext())) |
1626 | +>>>>>>> MERGE-SOURCE |
1627 | |
1628 | === modified file 'lib/lp/soyuz/interfaces/archive.py' |
1629 | --- lib/lp/soyuz/interfaces/archive.py 2011-06-24 21:21:36 +0000 |
1630 | +++ lib/lp/soyuz/interfaces/archive.py 2011-07-18 11:13:26 +0000 |
1631 | @@ -101,9 +101,16 @@ |
1632 | from lp.soyuz.enums import ArchivePurpose |
1633 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
1634 | from lp.soyuz.interfaces.component import IComponent |
1635 | - |
1636 | - |
1637 | -@error_status(httplib.BAD_REQUEST) |
1638 | +<<<<<<< TREE |
1639 | + |
1640 | + |
1641 | +@error_status(httplib.BAD_REQUEST) |
1642 | +======= |
1643 | +from lp.soyuz.interfaces.processor import IProcessorFamily |
1644 | + |
1645 | + |
1646 | +@error_status(httplib.BAD_REQUEST) |
1647 | +>>>>>>> MERGE-SOURCE |
1648 | class ArchiveDependencyError(Exception): |
1649 | """Raised when an `IArchiveDependency` does not fit the context archive. |
1650 | |
1651 | |
1652 | === modified file 'lib/lp/soyuz/interfaces/files.py' |
1653 | --- lib/lp/soyuz/interfaces/files.py 2011-06-29 11:40:03 +0000 |
1654 | +++ lib/lp/soyuz/interfaces/files.py 2011-07-18 11:13:26 +0000 |
1655 | @@ -22,8 +22,12 @@ |
1656 | ) |
1657 | |
1658 | from canonical.launchpad import _ |
1659 | +<<<<<<< TREE |
1660 | from canonical.launchpad.interfaces.librarian import ILibraryFileAlias |
1661 | from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease |
1662 | +======= |
1663 | +from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease |
1664 | +>>>>>>> MERGE-SOURCE |
1665 | |
1666 | |
1667 | class IBinaryPackageFile(Interface): |
1668 | |
1669 | === modified file 'lib/lp/soyuz/model/packagesetsources.py' |
1670 | --- lib/lp/soyuz/model/packagesetsources.py 2011-07-01 15:53:38 +0000 |
1671 | +++ lib/lp/soyuz/model/packagesetsources.py 2011-07-18 11:13:26 +0000 |
1672 | @@ -1,3 +1,4 @@ |
1673 | +<<<<<<< TREE |
1674 | # Copyright 2011 Canonical Ltd. This software is licensed under the |
1675 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1676 | |
1677 | @@ -43,3 +44,46 @@ |
1678 | def __init__(self, packageset, sourcepackagename): |
1679 | self.packageset = packageset |
1680 | self.sourcepackagename = sourcepackagename |
1681 | +======= |
1682 | +# Copyright 2011 Canonical Ltd. This software is licensed under the |
1683 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
1684 | + |
1685 | +"""The `PackagesetSources` linking table. |
1686 | + |
1687 | +This table associates `Packageset`s with `SourcePackageName`s. |
1688 | +""" |
1689 | + |
1690 | +__metaclass__ = type |
1691 | +__all__ = [ |
1692 | + 'PackagesetSources', |
1693 | + ] |
1694 | + |
1695 | +from storm.locals import ( |
1696 | + Int, |
1697 | + Reference, |
1698 | + Storm, |
1699 | + ) |
1700 | + |
1701 | + |
1702 | +class PackagesetSources(Storm): |
1703 | + """Linking table: which packages are in a package set?""" |
1704 | + # This table is largely managed from Packageset, but also directly |
1705 | + # accessed from other places. |
1706 | + |
1707 | + __storm_table__ = 'PackagesetSources' |
1708 | + |
1709 | + # There's a vestigial id as well, a holdover from the SQLObject |
1710 | + # days. Nobody seems to use it. The only key that matters is |
1711 | + # (packageset, sourcepackagename). |
1712 | + # XXX JeroenVermeulen 2011-06-22, bug=800677: Drop the id column. |
1713 | + __storm_primary__ = ( |
1714 | + 'packageset_id', |
1715 | + 'sourcepackagename_id', |
1716 | + ) |
1717 | + |
1718 | + packageset_id = Int(name='packageset') |
1719 | + packageset = Reference(packageset_id, 'Packageset.id') |
1720 | + sourcepackagename_id = Int(name='sourcepackagename') |
1721 | + sourcepackagename = Reference( |
1722 | + sourcepackagename_id, 'SourcePackageName.id') |
1723 | +>>>>>>> MERGE-SOURCE |
1724 | |
1725 | === modified file 'lib/lp/soyuz/scripts/tests/test_queue.py' |
1726 | --- lib/lp/soyuz/scripts/tests/test_queue.py 2011-07-07 16:05:03 +0000 |
1727 | +++ lib/lp/soyuz/scripts/tests/test_queue.py 2011-07-18 11:13:26 +0000 |
1728 | @@ -966,6 +966,7 @@ |
1729 | distro.name, distroseries.name, queue, terms, component.name, |
1730 | section.name, priority_name, display) |
1731 | |
1732 | +<<<<<<< TREE |
1733 | def makeQueueActionOverride(self, package_upload, component, section, |
1734 | distroseries=None): |
1735 | return self.makeQueueAction( |
1736 | @@ -980,6 +981,16 @@ |
1737 | """ |
1738 | return tuple(item.strip() for item in output_line.split('|')) |
1739 | |
1740 | +======= |
1741 | + def parseUploadSummaryLine(self, output_line): |
1742 | + """Parse an output line from `QueueAction.displayItem`. |
1743 | + |
1744 | + :param output_line: A line of output text from `displayItem`. |
1745 | + :return: A tuple of displayed items: (id, tag, name, version, age). |
1746 | + """ |
1747 | + return tuple(item.strip() for item in output_line.split('|')) |
1748 | + |
1749 | +>>>>>>> MERGE-SOURCE |
1750 | def test_display_actions_have_privileges_for_PackageCopyJob(self): |
1751 | # The methods that display uploads have privileges to work with |
1752 | # a PackageUpload that has a copy job. |
1753 | @@ -1031,6 +1042,7 @@ |
1754 | action = self.makeQueueAction(upload) |
1755 | |
1756 | action.displayItem(upload) |
1757 | +<<<<<<< TREE |
1758 | |
1759 | ((output, ), kwargs) = action.display.calls[0] |
1760 | (upload_id, tag, name, version, age) = self.parseUploadSummaryLine( |
1761 | @@ -1085,6 +1097,37 @@ |
1762 | def test_makeTag_returns_dashes_for_custom_upload(self): |
1763 | upload = self.factory.makeCustomPackageUpload() |
1764 | self.assertEqual('--', self.makeQueueAction(upload)._makeTag(upload)) |
1765 | +======= |
1766 | + |
1767 | + ((output, ), kwargs) = action.display.calls[0] |
1768 | + (upload_id, tag, name, version, age) = self.parseUploadSummaryLine( |
1769 | + output) |
1770 | + self.assertEqual(str(upload.id), upload_id) |
1771 | + self.assertEqual("X-", tag) |
1772 | + self.assertThat(upload.displayname, StartsWith(name)) |
1773 | + self.assertThat(upload.package_version, StartsWith(version)) |
1774 | + |
1775 | + def test_makeTag_returns_S_for_source_upload(self): |
1776 | + upload = self.factory.makeSourcePackageUpload() |
1777 | + self.assertEqual('S-', self.makeQueueAction(upload)._makeTag(upload)) |
1778 | + |
1779 | + def test_makeTag_returns_B_for_binary_upload(self): |
1780 | + upload = self.factory.makeBuildPackageUpload() |
1781 | + self.assertEqual('-B', self.makeQueueAction(upload)._makeTag(upload)) |
1782 | + |
1783 | + def test_makeTag_returns_SB_for_mixed_upload(self): |
1784 | + upload = self.factory.makeSourcePackageUpload() |
1785 | + upload.addBuild(self.factory.makeBinaryPackageBuild()) |
1786 | + self.assertEqual('SB', self.makeQueueAction(upload)._makeTag(upload)) |
1787 | + |
1788 | + def test_makeTag_returns_X_for_copy_job_upload(self): |
1789 | + upload = self.factory.makeCopyJobPackageUpload() |
1790 | + self.assertEqual('X-', self.makeQueueAction(upload)._makeTag(upload)) |
1791 | + |
1792 | + def test_makeTag_returns_dashes_for_custom_upload(self): |
1793 | + upload = self.factory.makeCustomPackageUpload() |
1794 | + self.assertEqual('--', self.makeQueueAction(upload)._makeTag(upload)) |
1795 | +>>>>>>> MERGE-SOURCE |
1796 | |
1797 | def test_displayInfo_displays_PackageUpload_with_source(self): |
1798 | # displayInfo can display a source package upload. |
1799 | |
1800 | === modified file 'lib/lp/soyuz/stories/webservice/xx-archive.txt' |
1801 | --- lib/lp/soyuz/stories/webservice/xx-archive.txt 2011-06-23 16:05:30 +0000 |
1802 | +++ lib/lp/soyuz/stories/webservice/xx-archive.txt 2011-07-18 11:13:26 +0000 |
1803 | @@ -17,6 +17,7 @@ |
1804 | >>> from lazr.restful.testing.webservice import pprint_entry |
1805 | >>> pprint_entry(cprov_archive) |
1806 | commercial: False |
1807 | +<<<<<<< TREE |
1808 | dependencies_collection_link: |
1809 | u'http://.../~cprov/+archive/ppa/dependencies' |
1810 | description: u'packages to help my friends.' |
1811 | @@ -38,6 +39,10 @@ |
1812 | >>> pprint_entry(cprov_archive_devel) |
1813 | commercial: False |
1814 | dependencies_collection_link: u'http://.../~cprov/+archive/ppa/dependencies' |
1815 | +======= |
1816 | + dependencies_collection_link: |
1817 | + u'http://.../~cprov/+archive/ppa/dependencies' |
1818 | +>>>>>>> MERGE-SOURCE |
1819 | description: u'packages to help my friends.' |
1820 | displayname: u'PPA for Celso Providelo' |
1821 | distribution_link: u'http://.../ubuntu' |
1822 | |
1823 | === modified file 'lib/lp/soyuz/tests/test_packageupload.py' |
1824 | --- lib/lp/soyuz/tests/test_packageupload.py 2011-07-11 08:31:46 +0000 |
1825 | +++ lib/lp/soyuz/tests/test_packageupload.py 2011-07-18 11:13:26 +0000 |
1826 | @@ -816,6 +816,7 @@ |
1827 | [upload], |
1828 | upload_set.getAll( |
1829 | distroseries, name=spn.name, version=upload.displayversion)) |
1830 | +<<<<<<< TREE |
1831 | |
1832 | def test_getAll_orders_in_reverse_historical_order(self): |
1833 | # The results from getAll are returned in order of creation, |
1834 | @@ -838,3 +839,5 @@ |
1835 | self.assertEqual( |
1836 | list(reversed(ordered_uploads)), |
1837 | list(getUtility(IPackageUploadSet).getAll(series))) |
1838 | +======= |
1839 | +>>>>>>> MERGE-SOURCE |
1840 | |
1841 | === modified file 'lib/lp/testing/factory.py' |
1842 | --- lib/lp/testing/factory.py 2011-07-13 20:55:34 +0000 |
1843 | +++ lib/lp/testing/factory.py 2011-07-18 11:13:26 +0000 |
1844 | @@ -474,7 +474,7 @@ |
1845 | branch_id = self.getUniqueInteger() |
1846 | if rcstype is None: |
1847 | rcstype = 'svn' |
1848 | - if rcstype in ['svn', 'bzr-svn', 'hg']: |
1849 | + if rcstype in ['svn', 'bzr-svn', 'hg', 'bzr']: |
1850 | assert cvs_root is cvs_module is None |
1851 | if url is None: |
1852 | url = self.getUniqueURL() |
1853 | @@ -2100,7 +2100,8 @@ |
1854 | |
1855 | def makeCodeImport(self, svn_branch_url=None, cvs_root=None, |
1856 | cvs_module=None, target=None, branch_name=None, |
1857 | - git_repo_url=None, hg_repo_url=None, registrant=None, |
1858 | + git_repo_url=None, hg_repo_url=None, |
1859 | + bzr_branch_url=None, registrant=None, |
1860 | rcs_type=None, review_status=None): |
1861 | """Create and return a new, arbitrary code import. |
1862 | |
1863 | @@ -2109,7 +2110,7 @@ |
1864 | unique URL. |
1865 | """ |
1866 | if (svn_branch_url is cvs_root is cvs_module is git_repo_url is |
1867 | - hg_repo_url is None): |
1868 | + hg_repo_url is bzr_branch_url is None): |
1869 | svn_branch_url = self.getUniqueURL() |
1870 | |
1871 | if target is None: |
1872 | @@ -2140,6 +2141,11 @@ |
1873 | registrant, target, branch_name, |
1874 | rcs_type=RevisionControlSystems.HG, |
1875 | url=hg_repo_url) |
1876 | + elif bzr_branch_url is not None: |
1877 | + code_import = code_import_set.new( |
1878 | + registrant, target, branch_name, |
1879 | + rcs_type=RevisionControlSystems.BZR, |
1880 | + url=bzr_branch_url) |
1881 | else: |
1882 | assert rcs_type in (None, RevisionControlSystems.CVS) |
1883 | code_import = code_import_set.new( |
1884 | @@ -3485,6 +3491,7 @@ |
1885 | distribution=distribution) |
1886 | |
1887 | if archive is None: |
1888 | +<<<<<<< TREE |
1889 | archive = distroseries.main_archive |
1890 | |
1891 | if (sourcepackagename is None or |
1892 | @@ -3494,6 +3501,19 @@ |
1893 | |
1894 | if (component is None or isinstance(component, basestring)): |
1895 | component = self.makeComponent(component) |
1896 | +======= |
1897 | + archive = self.makeArchive( |
1898 | + distribution=distroseries.distribution, |
1899 | + purpose=ArchivePurpose.PRIMARY) |
1900 | + |
1901 | + if (sourcepackagename is None or |
1902 | + isinstance(sourcepackagename, basestring)): |
1903 | + sourcepackagename = self.getOrMakeSourcePackageName( |
1904 | + sourcepackagename) |
1905 | + |
1906 | + if component is None: |
1907 | + component = self.makeComponent() |
1908 | +>>>>>>> MERGE-SOURCE |
1909 | |
1910 | if urgency is None: |
1911 | urgency = self.getAnySourcePackageUrgency() |
1912 | @@ -3567,6 +3587,7 @@ |
1913 | to determine archive. |
1914 | :param source_package_release: The SourcePackageRelease this binary |
1915 | build uses as its source. |
1916 | +<<<<<<< TREE |
1917 | :param sourcepackagename: when source_package_release is None, the |
1918 | sourcepackagename from which the build will come. |
1919 | :param distroarchseries: The DistroArchSeries to use. Defaults to the |
1920 | @@ -3574,6 +3595,12 @@ |
1921 | :param archive: The Archive to use. Defaults to the one from the |
1922 | source_package_release, or the distro arch series main archive |
1923 | otherwise. |
1924 | +======= |
1925 | + :param sourcepackagename: when source_package_release is None, the |
1926 | + sourcepackagename from which the build will come. |
1927 | + :param distroarchseries: The DistroArchSeries to use. |
1928 | + :param archive: The Archive to use. |
1929 | +>>>>>>> MERGE-SOURCE |
1930 | :param builder: An optional builder to assign. |
1931 | :param status: The BuildStatus for the build. |
1932 | """ |
1933 | |
1934 | === modified file 'scripts/code-import-worker.py' |
1935 | --- scripts/code-import-worker.py 2011-06-16 23:43:04 +0000 |
1936 | +++ scripts/code-import-worker.py 2011-07-18 11:13:26 +0000 |
1937 | @@ -26,8 +26,9 @@ |
1938 | from canonical.config import config |
1939 | from lp.codehosting import load_optional_plugin |
1940 | from lp.codehosting.codeimport.worker import ( |
1941 | - BzrSvnImportWorker, CSCVSImportWorker, CodeImportSourceDetails, |
1942 | - GitImportWorker, HgImportWorker, get_default_bazaar_branch_store) |
1943 | + BzrImportWorker, BzrSvnImportWorker, CSCVSImportWorker, |
1944 | + CodeImportSourceDetails, GitImportWorker, HgImportWorker, |
1945 | + get_default_bazaar_branch_store) |
1946 | from canonical.launchpad import scripts |
1947 | |
1948 | |
1949 | @@ -65,8 +66,17 @@ |
1950 | elif source_details.rcstype == 'hg': |
1951 | load_optional_plugin('hg') |
1952 | import_worker_cls = HgImportWorker |
1953 | - elif source_details.rcstype in ['cvs', 'svn']: |
1954 | - import_worker_cls = CSCVSImportWorker |
1955 | +<<<<<<< TREE |
1956 | + elif source_details.rcstype in ['cvs', 'svn']: |
1957 | + import_worker_cls = CSCVSImportWorker |
1958 | +======= |
1959 | + elif source_details.rcstype == 'bzr': |
1960 | + load_optional_plugin('loom') |
1961 | + load_optional_plugin('weave_fmt') |
1962 | + import_worker_cls = BzrImportWorker |
1963 | + elif source_details.rcstype in ['cvs', 'svn']: |
1964 | + import_worker_cls = CSCVSImportWorker |
1965 | +>>>>>>> MERGE-SOURCE |
1966 | else: |
1967 | raise AssertionError( |
1968 | 'unknown rcstype %r' % source_details.rcstype) |
1969 | |
1970 | === modified file 'utilities/sourcedeps.cache' |
1971 | --- utilities/sourcedeps.cache 2011-06-29 14:14:20 +0000 |
1972 | +++ utilities/sourcedeps.cache 2011-07-18 11:13:26 +0000 |
1973 | @@ -1,8 +1,4 @@ |
1974 | { |
1975 | - "bzr-builder": [ |
1976 | - 68, |
1977 | - "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg" |
1978 | - ], |
1979 | "testresources": [ |
1980 | 16, |
1981 | "robertc@robertcollins.net-20050911111209-ee5da49011cf936a" |
1982 | @@ -31,14 +27,18 @@ |
1983 | 24, |
1984 | "launchpad@pqm.canonical.com-20100601182722-wo7h2fh0fvyw3aaq" |
1985 | ], |
1986 | - "lpreview": [ |
1987 | - 23, |
1988 | - "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8" |
1989 | - ], |
1990 | "bzr-git": [ |
1991 | 259, |
1992 | "launchpad@pqm.canonical.com-20110601140035-gl5merbechngjw5s" |
1993 | ], |
1994 | + "loggerhead": [ |
1995 | + 445, |
1996 | + "john@arbash-meinel.com-20110325141442-536j4be3x0c464zy" |
1997 | + ], |
1998 | + "bzr-builder": [ |
1999 | + 68, |
2000 | + "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg" |
2001 | + ], |
2002 | "bzr-loom": [ |
2003 | 49, |
2004 | "launchpad@pqm.canonical.com-20110601122412-54vo3k8yae9i2zve" |
2005 | @@ -47,9 +47,15 @@ |
2006 | 4, |
2007 | "sinzui-20090526164636-1swugzupwvjgomo4" |
2008 | ], |
2009 | +<<<<<<< TREE |
2010 | "loggerhead": [ |
2011 | 452, |
2012 | "andrew.bennetts@canonical.com-20110628173100-owrifrnckvoi60af" |
2013 | +======= |
2014 | + "lpreview": [ |
2015 | + 23, |
2016 | + "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8" |
2017 | +>>>>>>> MERGE-SOURCE |
2018 | ], |
2019 | "difftacular": [ |
2020 | 6, |
2021 | |
2022 | === modified file 'versions.cfg' |
2023 | --- versions.cfg 2011-07-18 11:13:00 +0000 |
2024 | +++ versions.cfg 2011-07-18 11:13:26 +0000 |
2025 | @@ -78,8 +78,12 @@ |
2026 | soupmatchers = 0.1r53 |
2027 | sourcecodegen = 0.6.9 |
2028 | storm = 0.18.0.99-lpwithnodatetime-r393 |
2029 | +<<<<<<< TREE |
2030 | testresources = 0.2.4-r58 |
2031 | testtools = 0.9.12-r194 |
2032 | +======= |
2033 | +testtools = 0.9.11 |
2034 | +>>>>>>> MERGE-SOURCE |
2035 | transaction = 1.0.0 |
2036 | txamqp = 0.4 |
2037 | Twisted = 11.0.0 |
I don't know the ins and outs of bzrlib or codehosting - for that I
guess that's why you've asked Michael to review - but the rest looks
good.
[1]
+ def test_partial(self):
+ # Skip tests of partial tests, as they are disabled for native imports
+ # at the moment.
+ return
You could use TestCase.skip() here:
def test_partial(self):
self.skip( "Disabled for native imports at the moment.")