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

Proposed by Jelmer Vernooij
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
Reviewer Review Type Date Requested Status
Robert Collins code Pending
Gavin Panella Pending
Michael Hudson-Doyle Pending
Review via email: mp+68231@code.launchpad.net

This proposal supersedes a proposal from 2011-06-23.

This proposal has been superseded by a proposal from 2011-07-18.

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.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) 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.

[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.")

review: Approve
Revision history for this message
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.

Revision history for this message
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:

        branch.get_config().set_user_option("create_signatures", "never")

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.

review: Approve
Revision history for this message
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.

Revision history for this message
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.get_config().set_user_option("create_signatures", "never")
>
> 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

Revision history for this message
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.get_config().set_user_option("create_signatures", "never")
> >
> > 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.bzrutils.safe_open and the other around
lp.codehosting.puller.worker.BranchMirrorer.

(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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/patch-2208-74-0.sql'
--- database/schema/patch-2208-74-0.sql 2011-06-27 10:38:18 +0000
+++ database/schema/patch-2208-74-0.sql 2011-07-18 11:13:26 +0000
@@ -1,3 +1,4 @@
1<<<<<<< TREE
1-- Copyright 2011 Canonical Ltd. This software is licensed under the2-- Copyright 2011 Canonical Ltd. This software is licensed under the
2-- GNU Affero General Public License version 3 (see the file LICENSE).3-- GNU Affero General Public License version 3 (see the file LICENSE).
34
@@ -15,3 +16,22 @@
1516
16INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 74, 0);17INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 74, 0);
1718
19=======
20-- Copyright 2011 Canonical Ltd. This software is licensed under the
21-- GNU Affero General Public License version 3 (see the file LICENSE).
22
23SET client_min_messages=ERROR;
24
25ALTER TABLE CodeImport DROP CONSTRAINT valid_vcs_details;
26ALTER TABLE CodeImport ADD CONSTRAINT "valid_vcs_details" CHECK (
27CASE
28 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
29 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)
30 WHEN rcs_type = ANY (ARRAY[4, 5, 6]) THEN cvs_root IS NULL AND cvs_module IS NULL AND url IS NOT NULL
31 ELSE false
32END);
33
34
35INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 74, 0);
36
37>>>>>>> MERGE-SOURCE
1838
=== modified file 'lib/canonical/config/schema-lazr.conf'
--- lib/canonical/config/schema-lazr.conf 2011-07-15 15:46:51 +0000
+++ lib/canonical/config/schema-lazr.conf 2011-07-18 11:13:26 +0000
@@ -504,6 +504,11 @@
504# datatype: integer504# datatype: integer
505default_interval_cvs: 43200505default_interval_cvs: 43200
506506
507# The default value of the update interval of a code import from
508# Bazaar, in seconds.
509# datatype: integer
510default_interval_bzr: 21600
511
507# Where the tarballs of foreign branches are uploaded for storage.512# Where the tarballs of foreign branches are uploaded for storage.
508# datatype: string513# datatype: string
509foreign_tree_store: sftp://hoover@escudero/srv/importd/sources/514foreign_tree_store: sftp://hoover@escudero/srv/importd/sources/
510515
=== modified file 'lib/canonical/launchpad/daemons/tachandler.py'
--- lib/canonical/launchpad/daemons/tachandler.py 2011-06-21 17:13:47 +0000
+++ lib/canonical/launchpad/daemons/tachandler.py 2011-07-18 11:13:26 +0000
@@ -20,6 +20,7 @@
20from fixtures import Fixture20from fixtures import Fixture
2121
22from canonical.launchpad.daemons import readyservice22from canonical.launchpad.daemons import readyservice
23<<<<<<< TREE
23from lp.services.osutils import (24from lp.services.osutils import (
24 get_pid_from_file,25 get_pid_from_file,
25 kill_by_pidfile,26 kill_by_pidfile,
@@ -27,6 +28,14 @@
27 two_stage_kill,28 two_stage_kill,
28 until_no_eintr,29 until_no_eintr,
29 )30 )
31=======
32from lp.services.osutils import (
33 get_pid_from_file,
34 kill_by_pidfile,
35 remove_if_exists,
36 two_stage_kill,
37 )
38>>>>>>> MERGE-SOURCE
3039
3140
32twistd_script = os.path.abspath(os.path.join(41twistd_script = os.path.abspath(os.path.join(
3342
=== modified file 'lib/canonical/launchpad/doc/vocabulary-json.txt'
--- lib/canonical/launchpad/doc/vocabulary-json.txt 2011-07-18 00:30:18 +0000
+++ lib/canonical/launchpad/doc/vocabulary-json.txt 2011-07-18 11:13:26 +0000
@@ -93,9 +93,14 @@
93 "api_uri": "/~commercial-admins",93 "api_uri": "/~commercial-admins",
94 "css": "sprite team",94 "css": "sprite team",
95 "link_css": "js-action",95 "link_css": "js-action",
96<<<<<<< TREE
96 "metadata": "team",97 "metadata": "team",
97 "title": "Commercial Subscription Admins",98 "title": "Commercial Subscription Admins",
98 "value": "commercial-admins"99 "value": "commercial-admins"
100=======
101 "title": "Commercial Subscription Admins",
102 "value": "commercial-admins"
103>>>>>>> MERGE-SOURCE
99 }104 }
100 ],105 ],
101 "total_size": 6106 "total_size": 6
@@ -114,9 +119,14 @@
114 "api_uri": "/~launchpad-buildd-admins",119 "api_uri": "/~launchpad-buildd-admins",
115 "css": "sprite team",120 "css": "sprite team",
116 "link_css": "js-action",121 "link_css": "js-action",
122<<<<<<< TREE
117 "metadata": "team",123 "metadata": "team",
118 "title": "Launchpad Buildd Admins",124 "title": "Launchpad Buildd Admins",
119 "value": "launchpad-buildd-admins"125 "value": "launchpad-buildd-admins"
126=======
127 "title": "Launchpad Buildd Admins",
128 "value": "launchpad-buildd-admins"
129>>>>>>> MERGE-SOURCE
120 }130 }
121 ],131 ],
122 "total_size": 6132 "total_size": 6
123133
=== modified file 'lib/lp/app/javascript/picker/person_picker.js'
--- lib/lp/app/javascript/picker/person_picker.js 2011-07-14 12:46:31 +0000
+++ lib/lp/app/javascript/picker/person_picker.js 2011-07-18 11:13:26 +0000
@@ -1,10 +1,81 @@
1/* Copyright 2011 Canonical Ltd. This software is licensed under the1/* Copyright 2011 Canonical Ltd. This software is licensed under the
2 * GNU Affero General Public License version 3 (see the file LICENSE).2 * GNU Affero General Public License version 3 (see the file LICENSE).
3 *3 *
4<<<<<<< TREE
4 * @namespace Y.lazr.person-picker5 * @namespace Y.lazr.person-picker
5 * @requires lazr.picker6 * @requires lazr.picker
6 */7 */
7YUI.add('lazr.person-picker', function(Y) {8YUI.add('lazr.person-picker', function(Y) {
9=======
10 * @namespace Y.lp.app.widgets
11 * @requires Y.lazr.picker
12 */
13YUI.add('lp.app.widgets', function(Y) {
14var namespace = Y.namespace('lp.app.widgets');
15
16/**
17 * Extend the lazr-js Picker.
18 */
19var Picker = function() {
20 Picker.superclass.constructor.apply(this, arguments);
21};
22
23Y.extend(Picker, Y.lazr.Picker, {
24 // We want to render alt title slightly differently.
25 _renderTitleUI: function(data) {
26 var li_title = Y.Node.create(
27 '<span></span>').addClass(Y.lazr.Picker.C_RESULT_TITLE);
28 if (data.title === undefined) {
29 // Display an empty element if data is empty.
30 return li_title;
31 }
32 var title = this._text_or_link(
33 data.title, data.title_link, data.link_css);
34 li_title.appendChild(title);
35 if (data.alt_title) {
36 var alt_link = null;
37 if (data.alt_title_link) {
38 alt_link =Y.Node.create('<a></a>')
39 .addClass(data.link_css)
40 .addClass('discreet');
41 alt_link.set('text', " Details...")
42 .set('href', data.alt_title_link);
43 Y.on('click', function(e) {
44 e.halt();
45 window.open(data.alt_title_link);
46 }, alt_link);
47 }
48 li_title.appendChild('&nbsp;(');
49 li_title.appendChild(document.createTextNode(data.alt_title));
50 li_title.appendChild(')');
51 if (alt_link !== null) {
52 li_title.appendChild(alt_link);
53 }
54 }
55 return li_title;
56 },
57
58 /**
59 * Create the widget's HTML components.
60 * <p>
61 * This method is invoked after renderUI is invoked for the Widget class
62 * using YUI's aop infrastructure.
63 * </p>
64 *
65 * @method _renderUIPicker
66 * @protected
67 */
68 _renderUIPicker: function() {
69 Picker.superclass._renderUIPicker.apply(this, arguments);
70 var body = this._batches_box.get('parentNode');
71 body.removeChild(this._batches_box);
72 this._results_box.insert(this._batches_box, 'after');
73 }
74});
75
76Picker.NAME = 'picker';
77namespace.Picker = Picker;
78>>>>>>> MERGE-SOURCE
879
9/*80/*
10 * Extend the picker into the PersonPicker81 * Extend the picker into the PersonPicker
1182
=== modified file 'lib/lp/app/javascript/picker/picker_patcher.js'
--- lib/lp/app/javascript/picker/picker_patcher.js 2011-07-18 05:32:40 +0000
+++ lib/lp/app/javascript/picker/picker_patcher.js 2011-07-18 11:13:26 +0000
@@ -93,8 +93,12 @@
93 if (link === null || !show_remove_button) {93 if (link === null || !show_remove_button) {
94 remove_button.addClass('yui3-picker-hidden');94 remove_button.addClass('yui3-picker-hidden');
95 } else {95 } else {
96<<<<<<< TREE
96 remove_button.removeClass('yui3-picker-hidden');97 remove_button.removeClass('yui3-picker-hidden');
97 update_button_text();98 update_button_text();
99=======
100 remove_button.removeClass('yui3-picker-hidden');
101>>>>>>> MERGE-SOURCE
98 }102 }
99 }103 }
100104
101105
=== modified file 'lib/lp/app/javascript/picker/tests/test_personpicker.js'
--- lib/lp/app/javascript/picker/tests/test_personpicker.js 2011-07-15 02:20:36 +0000
+++ lib/lp/app/javascript/picker/tests/test_personpicker.js 2011-07-18 11:13:26 +0000
@@ -6,6 +6,7 @@
6 'lazr.picker', 'lazr.person-picker', 'lp.app.picker',6 'lazr.picker', 'lazr.person-picker', 'lp.app.picker',
7 'node-event-simulate', function(Y) {7 'node-event-simulate', function(Y) {
88
9<<<<<<< TREE
9var Assert = Y.Assert;10var Assert = Y.Assert;
1011
11/* Helper function to clean up a dynamically added widget instance. */12/* Helper function to clean up a dynamically added widget instance. */
@@ -144,6 +145,78 @@
144 picker.set('results', []);145 picker.set('results', []);
145 } else {146 } else {
146 picker.set('results', this.vocabulary);147 picker.set('results', this.vocabulary);
148=======
149 var suite = new Y.Test.Suite("lp.app.widgets.PersonPicker Tests");
150
151 suite.add(new Y.Test.Case({
152 name: 'personpicker',
153
154 setUp: function() {
155 window.LP = {
156 links: {me: '/~no-one'},
157 cache: {}
158 };
159 },
160
161 test_render: function () {
162 var personpicker = new Y.lp.app.widgets.PersonPicker();
163 personpicker.render();
164 personpicker.show();
165
166 // The extra buttons section exists
167 Y.Assert.isNotNull(Y.one('.extra-form-buttons'));
168 Y.Assert.isNotUndefined(personpicker.assign_me_button);
169 Y.Assert.isNotUndefined(personpicker.remove_button);
170 },
171
172 test_search_field_focus: function () {
173 var personpicker = new Y.lp.app.widgets.PersonPicker();
174 personpicker.render();
175 personpicker.hide();
176
177 var got_focus = false;
178 personpicker._search_input.on('focus', function(e) {
179 got_focus = true;
180 });
181 personpicker.show();
182 Y.Assert.isTrue(got_focus, "search input did not get focus.");
183 },
184
185 test_buttons: function () {
186 var personpicker = new Y.lp.app.widgets.PersonPicker();
187 personpicker.render();
188 personpicker.show();
189
190 // Patch the picker so the assign_me and remove methods can be
191 // tested.
192 var data = null;
193 personpicker.on('save', function (result) {
194 data = result.value;
195 });
196 var remove = Y.one('.yui-picker-remove-button');
197 remove.simulate('click');
198 Y.Assert.areEqual('', data);
199
200 var assign_me = Y.one('.yui-picker-assign-me-button');
201 assign_me.simulate('click');
202 Y.Assert.areEqual('no-one', data);
203 },
204
205 test_buttons_config: function () {
206 cfg = {
207 show_assign_me_button: false,
208 show_remove_button: false
209 };
210
211 personpicker = new Y.lp.app.widgets.PersonPicker(cfg);
212 personpicker.render();
213 personpicker.show();
214
215 Y.Assert.isNotNull(Y.one('.extra-form-buttons'));
216 Y.Assert.isUndefined(personpicker.remove_button);
217 Y.Assert.isUndefined(personpicker.assign_me_button);
218
219>>>>>>> MERGE-SOURCE
147 }220 }
148 },221 },
149222
150223
=== modified file 'lib/lp/app/javascript/picker/tests/test_picker_patcher.js'
--- lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2011-07-18 05:32:40 +0000
+++ lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2011-07-18 11:13:26 +0000
@@ -231,6 +231,7 @@
231}));231}));
232232
233/*233/*
234<<<<<<< TREE
234 * Test cases for assign and remove buttons.235 * Test cases for assign and remove buttons.
235 */236 */
236suite.add(new Y.Test.Case({237suite.add(new Y.Test.Case({
@@ -471,6 +472,184 @@
471}));472}));
472473
473/*474/*
475=======
476 * Test cases for assign and remove buttons.
477 */
478suite.add(new Y.Test.Case({
479
480 name: 'picker_assign_remove_button',
481
482 setUp: function() {
483 var i;
484 this.vocabulary = new Array(121);
485 for (i = 0; i <5; i++) {
486 this.vocabulary[i] = {
487 "value": "value-" + i,
488 "title": "title-" + i,
489 "css": "sprite-person",
490 "description": "description-" + i,
491 "api_uri": "~/fred-" + i};
492 }
493 this.ME = '/~me';
494 window.LP = {
495 links: {
496 me: this.ME
497 },
498 cache: {}
499 };
500
501 // We patch Launchpad client to return some fake data for the patch
502 // operation.
503 Y.lp.client.Launchpad = function() {};
504 Y.lp.client.Launchpad.prototype.patch =
505 function(uri, representation, config, headers) {
506 // our setup assumes success, so we just do the success
507 // callback.
508 var entry_repr = {
509 'lp_html': {'test_link': '<a href="/~me">Content</a>'}
510 };
511 var result = new Y.lp.client.Entry(
512 null, entry_repr, "a_self_link");
513 config.on.success(result);
514 };
515
516 },
517
518 tearDown: function() {
519 cleanup_widget(this.picker);
520 delete window.LP;
521 },
522
523 create_picker: function(show_assign_me, show_remove, field_value) {
524 if (field_value !== undefined) {
525 var data_box = Y.one('#picker_id .yui3-activator-data-box');
526 data_box.appendChild(Y.Node.create('<a>Content</a>'));
527 data_box.one('a').set('href', field_value);
528 }
529
530 var config = {
531 "step_title": "Choose someone",
532 "header": "Pick Someone",
533 "validate_callback": null,
534 "show_search_box": true,
535 "show_assign_me_button": show_assign_me,
536 "show_remove_button": show_remove
537 };
538 this.picker = Y.lp.app.picker.addPickerPatcher(
539 this.vocabulary,
540 "foo/bar",
541 "test_link",
542 "picker_id",
543 config);
544 },
545
546 _check_button_state: function(btn_class, is_visible) {
547 var assign_me_button = Y.one(btn_class);
548 Assert.isNotNull(assign_me_button);
549 if (is_visible) {
550 Assert.isFalse(
551 assign_me_button.hasClass('yui3-picker-hidden'),
552 btn_class + " should be visible but is hidden");
553 } else {
554 Assert.isTrue(
555 assign_me_button.hasClass('yui3-picker-hidden'),
556 btn_class + " should be hidden but is visible");
557 }
558 },
559
560 _check_assign_me_button_state: function(is_visible) {
561 this._check_button_state('.yui-picker-assign-me-button', is_visible);
562 },
563
564 _check_remove_button_state: function(is_visible) {
565 this._check_button_state('.yui-picker-remove-button', is_visible);
566 },
567
568 test_picker_has_assign_me_button: function() {
569 // The assign me button is shown.
570 this.create_picker(true, true);
571 this.picker.render();
572 this._check_assign_me_button_state(true);
573 },
574
575 test_picker_no_assign_me_button_unless_configured: function() {
576 // The assign me button is only rendered if show_assign_me_button
577 // config setting is true.
578 this.create_picker(false, true);
579 this.picker.render();
580 Assert.isNull(Y.one('.yui-picker-assign-me-button'));
581 },
582
583 test_picker_no_assign_me_button_if_value_is_me: function() {
584 // The assign me button is not shown if the picker is created for a
585 // field where the value is "me".
586 this.create_picker(true, true, this.ME);
587 this.picker.render();
588 this._check_assign_me_button_state(false);
589 },
590
591 test_picker_assign_me_button_hide_on_save: function() {
592 // The assign me button is shown initially but hidden if the picker
593 // saves a value equal to 'me'.
594 this.create_picker(true, true);
595 this._check_assign_me_button_state(true);
596 this.picker.set('results', this.vocabulary);
597 this.picker.render();
598 simulate(
599 this.picker.get('boundingBox').one('.yui3-picker-results'),
600 'li:nth-child(1)', 'click');
601 this._check_assign_me_button_state(false);
602 },
603
604 test_picker_no_remove_button_if_null_value: function() {
605 // The remove button is not shown if the picker is created for a field
606 // which has a null value.
607 this.create_picker(true, true);
608 this.picker.render();
609 this._check_remove_button_state(false);
610 },
611
612 test_picker_has_remove_button_if_value: function() {
613 // The remove button is shown if the picker is created for a field
614 // which has a value.
615 this.create_picker(true, true, this.ME);
616 this.picker.render();
617 this._check_remove_button_state(true);
618 },
619
620 test_picker_no_remove_button_unless_configured: function() {
621 // The remove button is only rendered if show_remove_button setting is
622 // true.
623 this.create_picker(true, false, this.ME);
624 this.picker.render();
625 Assert.isNull(Y.one('.yui-picker-remove-button'));
626 },
627
628 test_picker_remove_button_clicked: function() {
629 // The remove button is hidden once a picker value has been removed.
630 // And the assign me button is shown.
631 this.create_picker(true, true, this.ME);
632 this.picker.render();
633 var remove = Y.one('.yui-picker-remove-button');
634 remove.simulate('click');
635 this._check_remove_button_state(false);
636 this._check_assign_me_button_state(true);
637 },
638
639 test_picker_assign_me_button_clicked: function() {
640 // The assign me button is hidden once it is clicked.
641 // And the remove button is shown.
642 this.create_picker(true, true);
643 this.picker.render();
644 var remove = Y.one('.yui-picker-assign-me-button');
645 remove.simulate('click');
646 this._check_remove_button_state(true);
647 this._check_assign_me_button_state(false);
648 }
649}));
650
651/*
652>>>>>>> MERGE-SOURCE
474 * Test cases for a picker with a large vocabulary.653 * Test cases for a picker with a large vocabulary.
475 */654 */
476suite.add(new Y.Test.Case({655suite.add(new Y.Test.Case({
477656
=== modified file 'lib/lp/code/errors.py'
--- lib/lp/code/errors.py 2011-06-23 21:09:20 +0000
+++ lib/lp/code/errors.py 2011-07-18 11:13:26 +0000
@@ -182,6 +182,7 @@
182class BranchMergeProposalExists(InvalidBranchMergeProposal):182class BranchMergeProposalExists(InvalidBranchMergeProposal):
183 """Raised if there is already a matching BranchMergeProposal."""183 """Raised if there is already a matching BranchMergeProposal."""
184184
185<<<<<<< TREE
185 def __init__(self, existing_proposal):186 def __init__(self, existing_proposal):
186 super(BranchMergeProposalExists, self).__init__(187 super(BranchMergeProposalExists, self).__init__(
187 'There is already a branch merge proposal registered for '188 'There is already a branch merge proposal registered for '
@@ -190,6 +191,8 @@
190 existing_proposal.target_branch.displayname))191 existing_proposal.target_branch.displayname))
191 self.existing_proposal = existing_proposal192 self.existing_proposal = existing_proposal
192193
194=======
195>>>>>>> MERGE-SOURCE
193196
194class InvalidNamespace(Exception):197class InvalidNamespace(Exception):
195 """Raised when someone tries to lookup a namespace with a bad name.198 """Raised when someone tries to lookup a namespace with a bad name.
196199
=== modified file 'lib/lp/code/mail/codeimport.py'
--- lib/lp/code/mail/codeimport.py 2011-05-27 21:12:25 +0000
+++ lib/lp/code/mail/codeimport.py 2011-07-18 11:13:26 +0000
@@ -51,6 +51,7 @@
51 RevisionControlSystems.BZR_SVN: 'subversion',51 RevisionControlSystems.BZR_SVN: 'subversion',
52 RevisionControlSystems.GIT: 'git',52 RevisionControlSystems.GIT: 'git',
53 RevisionControlSystems.HG: 'mercurial',53 RevisionControlSystems.HG: 'mercurial',
54 RevisionControlSystems.BZR: 'bazaar',
54 }55 }
55 body = get_email_template('new-code-import.txt') % {56 body = get_email_template('new-code-import.txt') % {
56 'person': code_import.registrant.displayname,57 'person': code_import.registrant.displayname,
@@ -123,7 +124,8 @@
123 elif code_import.rcs_type in (RevisionControlSystems.SVN,124 elif code_import.rcs_type in (RevisionControlSystems.SVN,
124 RevisionControlSystems.BZR_SVN,125 RevisionControlSystems.BZR_SVN,
125 RevisionControlSystems.GIT,126 RevisionControlSystems.GIT,
126 RevisionControlSystems.HG):127 RevisionControlSystems.HG,
128 RevisionControlSystems.BZR):
127 if CodeImportEventDataType.OLD_URL in event_data:129 if CodeImportEventDataType.OLD_URL in event_data:
128 old_url = event_data[CodeImportEventDataType.OLD_URL]130 old_url = event_data[CodeImportEventDataType.OLD_URL]
129 body.append(131 body.append(
130132
=== modified file 'lib/lp/code/model/branchjob.py'
--- lib/lp/code/model/branchjob.py 2011-07-11 17:48:50 +0000
+++ lib/lp/code/model/branchjob.py 2011-07-18 11:13:26 +0000
@@ -107,11 +107,15 @@
107from lp.scripts.helpers import TransactionFreeOperation107from lp.scripts.helpers import TransactionFreeOperation
108from lp.services.job.interfaces.job import JobStatus108from lp.services.job.interfaces.job import JobStatus
109from lp.services.job.model.job import Job109from lp.services.job.model.job import Job
110<<<<<<< TREE
110from lp.services.job.runner import (111from lp.services.job.runner import (
111 BaseRunnableJob,112 BaseRunnableJob,
112 BaseRunnableJobSource,113 BaseRunnableJobSource,
113 )114 )
114from lp.services.utils import RegisteredSubclass115from lp.services.utils import RegisteredSubclass
116=======
117from lp.services.job.runner import BaseRunnableJob
118>>>>>>> MERGE-SOURCE
115from lp.translations.interfaces.translationimportqueue import (119from lp.translations.interfaces.translationimportqueue import (
116 ITranslationImportQueue,120 ITranslationImportQueue,
117 )121 )
@@ -294,6 +298,18 @@
294 raise SQLObjectNotFound(298 raise SQLObjectNotFound(
295 'No occurrence of %s has key %s' % (cls.__name__, key))299 'No occurrence of %s has key %s' % (cls.__name__, key))
296300
301 @classmethod
302 def get(cls, key):
303 """Return the instance of this class whose key is supplied.
304
305 :raises: SQLObjectNotFound
306 """
307 instance = IStore(BranchJob).get(BranchJob, key)
308 if instance is None or instance.job_type != cls.class_job_type:
309 raise SQLObjectNotFound(
310 'No occurrence of %s has key %s' % (cls.__name__, key))
311 return cls(instance)
312
297 def getOopsVars(self):313 def getOopsVars(self):
298 """See `IRunnableJob`."""314 """See `IRunnableJob`."""
299 vars = BaseRunnableJob.getOopsVars(self)315 vars = BaseRunnableJob.getOopsVars(self)
300316
=== modified file 'lib/lp/code/model/codeimport.py'
--- lib/lp/code/model/codeimport.py 2011-04-27 01:42:46 +0000
+++ lib/lp/code/model/codeimport.py 2011-07-18 11:13:26 +0000
@@ -116,6 +116,8 @@
116 config.codeimport.default_interval_git,116 config.codeimport.default_interval_git,
117 RevisionControlSystems.HG:117 RevisionControlSystems.HG:
118 config.codeimport.default_interval_hg,118 config.codeimport.default_interval_hg,
119 RevisionControlSystems.BZR:
120 config.codeimport.default_interval_bzr,
119 }121 }
120 seconds = default_interval_dict[self.rcs_type]122 seconds = default_interval_dict[self.rcs_type]
121 return timedelta(seconds=seconds)123 return timedelta(seconds=seconds)
@@ -133,7 +135,8 @@
133 RevisionControlSystems.SVN,135 RevisionControlSystems.SVN,
134 RevisionControlSystems.GIT,136 RevisionControlSystems.GIT,
135 RevisionControlSystems.BZR_SVN,137 RevisionControlSystems.BZR_SVN,
136 RevisionControlSystems.HG):138 RevisionControlSystems.HG,
139 RevisionControlSystems.BZR):
137 return self.url140 return self.url
138 else:141 else:
139 raise AssertionError(142 raise AssertionError(
@@ -252,7 +255,8 @@
252 elif rcs_type in (RevisionControlSystems.SVN,255 elif rcs_type in (RevisionControlSystems.SVN,
253 RevisionControlSystems.BZR_SVN,256 RevisionControlSystems.BZR_SVN,
254 RevisionControlSystems.GIT,257 RevisionControlSystems.GIT,
255 RevisionControlSystems.HG):258 RevisionControlSystems.HG,
259 RevisionControlSystems.BZR):
256 assert cvs_root is None and cvs_module is None260 assert cvs_root is None and cvs_module is None
257 assert url is not None261 assert url is not None
258 else:262 else:
@@ -262,7 +266,8 @@
262 if review_status is None:266 if review_status is None:
263 # Auto approve git and hg imports.267 # Auto approve git and hg imports.
264 if rcs_type in (268 if rcs_type in (
265 RevisionControlSystems.GIT, RevisionControlSystems.HG):269 RevisionControlSystems.GIT, RevisionControlSystems.HG,
270 RevisionControlSystems.BZR):
266 review_status = CodeImportReviewStatus.REVIEWED271 review_status = CodeImportReviewStatus.REVIEWED
267 else:272 else:
268 review_status = CodeImportReviewStatus.NEW273 review_status = CodeImportReviewStatus.NEW
269274
=== modified file 'lib/lp/code/model/codeimportevent.py'
--- lib/lp/code/model/codeimportevent.py 2010-10-17 22:51:50 +0000
+++ lib/lp/code/model/codeimportevent.py 2011-07-18 11:13:26 +0000
@@ -269,7 +269,8 @@
269 if code_import.rcs_type in (RevisionControlSystems.SVN,269 if code_import.rcs_type in (RevisionControlSystems.SVN,
270 RevisionControlSystems.BZR_SVN,270 RevisionControlSystems.BZR_SVN,
271 RevisionControlSystems.GIT,271 RevisionControlSystems.GIT,
272 RevisionControlSystems.HG):272 RevisionControlSystems.HG,
273 RevisionControlSystems.BZR):
273 yield 'URL', code_import.url274 yield 'URL', code_import.url
274 elif code_import.rcs_type == RevisionControlSystems.CVS:275 elif code_import.rcs_type == RevisionControlSystems.CVS:
275 yield 'CVS_ROOT', code_import.cvs_root276 yield 'CVS_ROOT', code_import.cvs_root
276277
=== modified file 'lib/lp/code/model/tests/test_codeimport.py'
--- lib/lp/code/model/tests/test_codeimport.py 2011-05-27 21:12:25 +0000
+++ lib/lp/code/model/tests/test_codeimport.py 2011-07-18 11:13:26 +0000
@@ -71,6 +71,20 @@
71 # No job is created for the import.71 # No job is created for the import.
72 self.assertIs(None, code_import.import_job)72 self.assertIs(None, code_import.import_job)
7373
74 def test_new_svn_import_svn_scheme(self):
75 """A subversion import can use the svn:// scheme."""
76 code_import = CodeImportSet().new(
77 registrant=self.factory.makePerson(),
78 target=IBranchTarget(self.factory.makeProduct()),
79 branch_name='imported',
80 rcs_type=RevisionControlSystems.SVN,
81 url=self.factory.getUniqueURL(scheme="svn"))
82 self.assertEqual(
83 CodeImportReviewStatus.NEW,
84 code_import.review_status)
85 # No job is created for the import.
86 self.assertIs(None, code_import.import_job)
87
74 def test_reviewed_svn_import(self):88 def test_reviewed_svn_import(self):
75 """A specific review status can be set for a new import."""89 """A specific review status can be set for a new import."""
76 code_import = CodeImportSet().new(90 code_import = CodeImportSet().new(
@@ -117,6 +131,21 @@
117 # A job is created for the import.131 # A job is created for the import.
118 self.assertIsNot(None, code_import.import_job)132 self.assertIsNot(None, code_import.import_job)
119133
134 def test_git_import_git_scheme(self):
135 """A git import can have a git:// style URL."""
136 code_import = CodeImportSet().new(
137 registrant=self.factory.makePerson(),
138 target=IBranchTarget(self.factory.makeProduct()),
139 branch_name='imported',
140 rcs_type=RevisionControlSystems.GIT,
141 url=self.factory.getUniqueURL(scheme="git"),
142 review_status=None)
143 self.assertEqual(
144 CodeImportReviewStatus.REVIEWED,
145 code_import.review_status)
146 # A job is created for the import.
147 self.assertIsNot(None, code_import.import_job)
148
120 def test_git_import_reviewed(self):149 def test_git_import_reviewed(self):
121 """A new git import is always reviewed by default."""150 """A new git import is always reviewed by default."""
122 code_import = CodeImportSet().new(151 code_import = CodeImportSet().new(
@@ -147,6 +176,21 @@
147 # A job is created for the import.176 # A job is created for the import.
148 self.assertIsNot(None, code_import.import_job)177 self.assertIsNot(None, code_import.import_job)
149178
179 def test_bzr_import_reviewed(self):
180 """A new bzr import is always reviewed by default."""
181 code_import = CodeImportSet().new(
182 registrant=self.factory.makePerson(),
183 target=IBranchTarget(self.factory.makeProduct()),
184 branch_name='mirrored',
185 rcs_type=RevisionControlSystems.BZR,
186 url=self.factory.getUniqueURL(),
187 review_status=None)
188 self.assertEqual(
189 CodeImportReviewStatus.REVIEWED,
190 code_import.review_status)
191 # A job is created for the import.
192 self.assertIsNot(None, code_import.import_job)
193
150 def test_junk_code_import_rejected(self):194 def test_junk_code_import_rejected(self):
151 """You are not allowed to create code imports targetting +junk."""195 """You are not allowed to create code imports targetting +junk."""
152 registrant = self.factory.makePerson()196 registrant = self.factory.makePerson()
153197
=== modified file 'lib/lp/codehosting/codeimport/tests/servers.py'
--- lib/lp/codehosting/codeimport/tests/servers.py 2011-06-02 10:48:54 +0000
+++ lib/lp/codehosting/codeimport/tests/servers.py 2011-07-18 11:13:26 +0000
@@ -4,6 +4,7 @@
4"""Server classes that know how to create various kinds of foreign archive."""4"""Server classes that know how to create various kinds of foreign archive."""
55
6__all__ = [6__all__ = [
7 'BzrServer',
7 'CVSServer',8 'CVSServer',
8 'GitServer',9 'GitServer',
9 'MercurialServer',10 'MercurialServer',
@@ -21,6 +22,8 @@
21import tempfile22import tempfile
22import time23import time
2324
25from bzrlib.bzrdir import BzrDir
26from bzrlib.branchbuilder import BranchBuilder
24from bzrlib.tests.treeshape import build_tree_contents27from bzrlib.tests.treeshape import build_tree_contents
25from bzrlib.transport import Server28from bzrlib.transport import Server
26from bzrlib.urlutils import (29from bzrlib.urlutils import (
@@ -246,3 +249,21 @@
246 f.close()249 f.close()
247 repo[None].add([filename])250 repo[None].add([filename])
248 repo.commit(text='<The commit message>', user='jane Foo <joe@foo.com>')251 repo.commit(text='<The commit message>', user='jane Foo <joe@foo.com>')
252
253
254class BzrServer(Server):
255
256 def __init__(self, repo_url):
257 super(BzrServer, self).__init__()
258 self.repo_url = repo_url
259
260 def makeRepo(self, tree_contents):
261 branch = BzrDir.create_branch_convenience(self.repo_url)
262 branch.get_config().set_user_option("create_signatures", "never")
263 builder = BranchBuilder(branch=branch)
264 actions = [('add', ('', 'tree-root', 'directory', None))]
265 actions += [('add', (path, path+'-id', 'file', content)) for (path,
266 content) in tree_contents]
267 builder.build_snapshot(None, None,
268 actions, committer='Joe Foo <joe@foo.com>',
269 message=u'<The commit message>')
249270
=== modified file 'lib/lp/codehosting/codeimport/tests/test_worker.py'
--- lib/lp/codehosting/codeimport/tests/test_worker.py 2011-06-21 05:38:15 +0000
+++ lib/lp/codehosting/codeimport/tests/test_worker.py 2011-07-18 11:13:26 +0000
@@ -17,6 +17,9 @@
17 Branch,17 Branch,
18 BranchReferenceFormat,18 BranchReferenceFormat,
19 )19 )
20from bzrlib.branchbuilder import (
21 BranchBuilder,
22 )
20from bzrlib.bzrdir import (23from bzrlib.bzrdir import (
21 BzrDir,24 BzrDir,
22 BzrDirFormat,25 BzrDirFormat,
@@ -24,12 +27,10 @@
24 )27 )
25from bzrlib.errors import (28from bzrlib.errors import (
26 NoSuchFile,29 NoSuchFile,
27 NotBranchError,
28 )30 )
29from bzrlib.tests import TestCaseWithTransport31from bzrlib.tests import TestCaseWithTransport
30from bzrlib import trace32from bzrlib import trace
31from bzrlib.transport import get_transport33from bzrlib.transport import get_transport
32from bzrlib.upgrade import upgrade
33from bzrlib.urlutils import (34from bzrlib.urlutils import (
34 join as urljoin,35 join as urljoin,
35 local_path_from_url,36 local_path_from_url,
@@ -49,6 +50,7 @@
49 extract_tarball,50 extract_tarball,
50 )51 )
51from lp.codehosting.codeimport.tests.servers import (52from lp.codehosting.codeimport.tests.servers import (
53 BzrServer,
52 CVSServer,54 CVSServer,
53 GitServer,55 GitServer,
54 MercurialServer,56 MercurialServer,
@@ -56,6 +58,7 @@
56 )58 )
57from lp.codehosting.codeimport.worker import (59from lp.codehosting.codeimport.worker import (
58 BazaarBranchStore,60 BazaarBranchStore,
61 BzrImportWorker,
59 BzrSvnImportWorker,62 BzrSvnImportWorker,
60 CodeImportWorkerExitCode,63 CodeImportWorkerExitCode,
61 CSCVSImportWorker,64 CSCVSImportWorker,
@@ -1001,10 +1004,10 @@
1001 # import should be rejected.1004 # import should be rejected.
1002 args = {'rcstype': self.rcstype}1005 args = {'rcstype': self.rcstype}
1003 reference_url = self.createBranchReference()1006 reference_url = self.createBranchReference()
1004 if self.rcstype in ('git', 'bzr-svn', 'hg'):1007 if self.rcstype in ('git', 'bzr-svn', 'hg', 'bzr'):
1005 args['url'] = reference_url1008 args['url'] = reference_url
1006 else:1009 else:
1007 raise AssertionError("unexpected rcs_type %r" % self.rcs_type)1010 raise AssertionError("unexpected rcs_type %r" % self.rcstype)
1008 source_details = self.factory.makeCodeImportSourceDetails(**args)1011 source_details = self.factory.makeCodeImportSourceDetails(**args)
1009 worker = self.makeImportWorker(source_details)1012 worker = self.makeImportWorker(source_details)
1010 self.assertEqual(1013 self.assertEqual(
@@ -1162,5 +1165,51 @@
1162 self.bazaar_store, logging.getLogger())1165 self.bazaar_store, logging.getLogger())
11631166
11641167
1168class TestBzrImport(WorkerTest, TestActualImportMixin,
1169 PullingImportWorkerTests):
1170
1171 rcstype = 'bzr'
1172
1173 def setUp(self):
1174 super(TestBzrImport, self).setUp()
1175 self.setUpImport()
1176
1177 def makeImportWorker(self, source_details):
1178 """Make a new `ImportWorker`."""
1179 return BzrImportWorker(
1180 source_details, self.get_transport('import_data'),
1181 self.bazaar_store, logging.getLogger())
1182
1183 def makeForeignCommit(self, source_details):
1184 """Change the foreign tree, generating exactly one commit."""
1185 branch = Branch.open(source_details.url)
1186 builder = BranchBuilder(branch=branch)
1187 builder.build_commit(message=self.factory.getUniqueString(),
1188 committer="Joe Random Hacker <joe@example.com>")
1189 self.foreign_commit_count += 1
1190
1191 def makeSourceDetails(self, branch_name, files):
1192 """Make Bzr `CodeImportSourceDetails` pointing at a real Bzr repo.
1193 """
1194 repository_path = self.makeTemporaryDirectory()
1195 bzr_server = BzrServer(repository_path)
1196 bzr_server.start_server()
1197 self.addCleanup(bzr_server.stop_server)
1198
1199 bzr_server.makeRepo(files)
1200 self.foreign_commit_count = 1
1201
1202 return self.factory.makeCodeImportSourceDetails(
1203 rcstype='bzr', url=repository_path)
1204
1205 def test_partial(self):
1206 self.skip(
1207 "Partial fetching is not supported for native bzr branches "
1208 "at the moment.")
1209
1210 def test_unsupported_feature(self):
1211 self.skip("All Bazaar features are supported by Bazaar.")
1212
1213
1165def test_suite():1214def test_suite():
1166 return unittest.TestLoader().loadTestsFromName(__name__)1215 return unittest.TestLoader().loadTestsFromName(__name__)
11671216
=== modified file 'lib/lp/codehosting/codeimport/tests/test_workermonitor.py'
--- lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2011-06-16 23:43:04 +0000
+++ lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2011-07-18 11:13:26 +0000
@@ -50,6 +50,7 @@
50from lp.code.model.codeimportjob import CodeImportJob50from lp.code.model.codeimportjob import CodeImportJob
51from lp.codehosting import load_optional_plugin51from lp.codehosting import load_optional_plugin
52from lp.codehosting.codeimport.tests.servers import (52from lp.codehosting.codeimport.tests.servers import (
53 BzrServer,
53 CVSServer,54 CVSServer,
54 GitServer,55 GitServer,
55 MercurialServer,56 MercurialServer,
@@ -682,6 +683,16 @@
682683
683 return self.factory.makeCodeImport(hg_repo_url=self.repo_path)684 return self.factory.makeCodeImport(hg_repo_url=self.repo_path)
684685
686 def makeBzrCodeImport(self):
687 """Make a `CodeImport` that points to a real Bazaar branch."""
688 self.bzr_server = BzrServer(self.repo_path)
689 self.bzr_server.start_server()
690 self.addCleanup(self.bzr_server.stop_server)
691
692 self.bzr_server.makeRepo([('README', 'contents')])
693 self.foreign_commit_count = 1
694 return self.factory.makeCodeImport(bzr_branch_url=self.repo_path)
695
685 def getStartedJobForImport(self, code_import):696 def getStartedJobForImport(self, code_import):
686 """Get a started `CodeImportJob` for `code_import`.697 """Get a started `CodeImportJob` for `code_import`.
687698
@@ -782,6 +793,15 @@
782 result = self.performImport(job_id)793 result = self.performImport(job_id)
783 return result.addCallback(self.assertImported, code_import_id)794 return result.addCallback(self.assertImported, code_import_id)
784795
796 def test_import_bzr(self):
797 # Create a Bazaar CodeImport and import it.
798 job = self.getStartedJobForImport(self.makeBzrCodeImport())
799 code_import_id = job.code_import.id
800 job_id = job.id
801 self.layer.txn.commit()
802 result = self.performImport(job_id)
803 return result.addCallback(self.assertImported, code_import_id)
804
785 # XXX 2010-03-24 MichaelHudson, bug=541526: This test fails intermittently805 # XXX 2010-03-24 MichaelHudson, bug=541526: This test fails intermittently
786 # in EC2.806 # in EC2.
787 def DISABLED_test_import_bzrsvn(self):807 def DISABLED_test_import_bzrsvn(self):
788808
=== modified file 'lib/lp/codehosting/codeimport/worker.py'
--- lib/lp/codehosting/codeimport/worker.py 2011-06-21 05:38:15 +0000
+++ lib/lp/codehosting/codeimport/worker.py 2011-07-18 11:13:26 +0000
@@ -6,6 +6,7 @@
6__metaclass__ = type6__metaclass__ = type
7__all__ = [7__all__ = [
8 'BazaarBranchStore',8 'BazaarBranchStore',
9 'BzrImportWorker',
9 'BzrSvnImportWorker',10 'BzrSvnImportWorker',
10 'CSCVSImportWorker',11 'CSCVSImportWorker',
11 'CodeImportSourceDetails',12 'CodeImportSourceDetails',
@@ -190,9 +191,9 @@
190191
191 :ivar branch_id: The id of the branch associated to this code import, used192 :ivar branch_id: The id of the branch associated to this code import, used
192 for locating the existing import and the foreign tree.193 for locating the existing import and the foreign tree.
193 :ivar rcstype: 'svn' or 'cvs' as appropriate.194 :ivar rcstype: 'svn', 'cvs', 'hg', 'git', 'bzr-svn', 'bzr' as appropriate.
194 :ivar url: The branch URL if rcstype in ['svn', 'bzr-svn',195 :ivar url: The branch URL if rcstype in ['svn', 'bzr-svn',
195 'git'], None otherwise.196 'git', 'hg', 'bzr'], None otherwise.
196 :ivar cvs_root: The $CVSROOT if rcstype == 'cvs', None otherwise.197 :ivar cvs_root: The $CVSROOT if rcstype == 'cvs', None otherwise.
197 :ivar cvs_module: The CVS module if rcstype == 'cvs', None otherwise.198 :ivar cvs_module: The CVS module if rcstype == 'cvs', None otherwise.
198 """199 """
@@ -210,7 +211,7 @@
210 """Convert command line-style arguments to an instance."""211 """Convert command line-style arguments to an instance."""
211 branch_id = int(arguments.pop(0))212 branch_id = int(arguments.pop(0))
212 rcstype = arguments.pop(0)213 rcstype = arguments.pop(0)
213 if rcstype in ['svn', 'bzr-svn', 'git', 'hg']:214 if rcstype in ['svn', 'bzr-svn', 'git', 'hg', 'bzr']:
214 [url] = arguments215 [url] = arguments
215 cvs_root = cvs_module = None216 cvs_root = cvs_module = None
216 elif rcstype == 'cvs':217 elif rcstype == 'cvs':
@@ -237,6 +238,8 @@
237 return cls(branch_id, 'git', str(code_import.url))238 return cls(branch_id, 'git', str(code_import.url))
238 elif code_import.rcs_type == RevisionControlSystems.HG:239 elif code_import.rcs_type == RevisionControlSystems.HG:
239 return cls(branch_id, 'hg', str(code_import.url))240 return cls(branch_id, 'hg', str(code_import.url))
241 elif code_import.rcs_type == RevisionControlSystems.BZR:
242 return cls(branch_id, 'bzr', str(code_import.url))
240 else:243 else:
241 raise AssertionError("Unknown rcstype %r." % code_import.rcs_type)244 raise AssertionError("Unknown rcstype %r." % code_import.rcs_type)
242245
@@ -244,7 +247,7 @@
244 """Return a list of arguments suitable for passing to a child process.247 """Return a list of arguments suitable for passing to a child process.
245 """248 """
246 result = [str(self.branch_id), self.rcstype]249 result = [str(self.branch_id), self.rcstype]
247 if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg']:250 if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg', 'bzr']:
248 result.append(self.url)251 result.append(self.url)
249 elif self.rcstype == 'cvs':252 elif self.rcstype == 'cvs':
250 result.append(self.cvs_root)253 result.append(self.cvs_root)
@@ -619,9 +622,19 @@
619 except NotBranchError:622 except NotBranchError:
620 pass623 pass
621 else:624 else:
625<<<<<<< TREE
622 self._logger.info("No branch found at remote location.")626 self._logger.info("No branch found at remote location.")
623 return CodeImportWorkerExitCode.FAILURE_INVALID627 return CodeImportWorkerExitCode.FAILURE_INVALID
624 remote_branch = format.open(transport).open_branch()628 remote_branch = format.open(transport).open_branch()
629=======
630 self._logger.info("No branch found at remote location.")
631 return CodeImportWorkerExitCode.FAILURE_INVALID
632 remote_dir = format.open(transport)
633 if remote_dir.get_branch_reference() is not None:
634 self._logger.info("Remote branch is a branch reference.")
635 return CodeImportWorkerExitCode.FAILURE_INVALID
636 remote_branch = remote_dir.open_branch()
637>>>>>>> MERGE-SOURCE
625 remote_branch_tip = remote_branch.last_revision()638 remote_branch_tip = remote_branch.last_revision()
626 inter_branch = InterBranch.get(remote_branch, bazaar_branch)639 inter_branch = InterBranch.get(remote_branch, bazaar_branch)
627 self._logger.info("Importing branch.")640 self._logger.info("Importing branch.")
@@ -822,3 +835,21 @@
822 """See `PullingImportWorker.probers`."""835 """See `PullingImportWorker.probers`."""
823 from bzrlib.plugins.svn import SvnRemoteProber836 from bzrlib.plugins.svn import SvnRemoteProber
824 return [SvnRemoteProber]837 return [SvnRemoteProber]
838
839
840class BzrImportWorker(PullingImportWorker):
841 """An import worker for importing Bazaar branches."""
842
843 invalid_branch_exceptions = []
844 unsupported_feature_exceptions = []
845
846 def getRevisionLimit(self):
847 """See `PullingImportWorker.getRevisionLimit`."""
848 # For now, just grab the whole branch at once
849 return None
850
851 @property
852 def probers(self):
853 """See `PullingImportWorker.probers`."""
854 from bzrlib.bzrdir import BzrProber, RemoteBzrProber
855 return [BzrProber, RemoteBzrProber]
825856
=== modified file 'lib/lp/registry/browser/productseries.py'
--- lib/lp/registry/browser/productseries.py 2011-05-27 21:12:25 +0000
+++ lib/lp/registry/browser/productseries.py 2011-07-18 11:13:26 +0000
@@ -827,15 +827,6 @@
827 ))827 ))
828828
829829
830class RevisionControlSystemsExtended(RevisionControlSystems):
831 """External RCS plus Bazaar."""
832 BZR = DBItem(99, """
833 Bazaar
834
835 External Bazaar branch.
836 """)
837
838
839class SetBranchForm(Interface):830class SetBranchForm(Interface):
840 """The fields presented on the form for setting a branch."""831 """The fields presented on the form for setting a branch."""
841832
@@ -844,7 +835,7 @@
844 ['cvs_module'])835 ['cvs_module'])
845836
846 rcs_type = Choice(title=_("Type of RCS"),837 rcs_type = Choice(title=_("Type of RCS"),
847 required=False, vocabulary=RevisionControlSystemsExtended,838 required=False, vocabulary=RevisionControlSystems,
848 description=_(839 description=_(
849 "The version control system to import from. "))840 "The version control system to import from. "))
850841
@@ -908,7 +899,7 @@
908 @property899 @property
909 def initial_values(self):900 def initial_values(self):
910 return dict(901 return dict(
911 rcs_type=RevisionControlSystemsExtended.BZR,902 rcs_type=RevisionControlSystems.BZR,
912 branch_type=LINK_LP_BZR,903 branch_type=LINK_LP_BZR,
913 branch_location=self.context.branch)904 branch_location=self.context.branch)
914905
@@ -989,7 +980,7 @@
989 self.setFieldError(980 self.setFieldError(
990 'rcs_type',981 'rcs_type',
991 'You must specify the type of RCS for the remote host.')982 'You must specify the type of RCS for the remote host.')
992 elif rcs_type == RevisionControlSystemsExtended.CVS:983 elif rcs_type == RevisionControlSystems.CVS:
993 if 'cvs_module' not in data:984 if 'cvs_module' not in data:
994 self.setFieldError(985 self.setFieldError(
995 'cvs_module',986 'cvs_module',
@@ -1022,8 +1013,9 @@
1022 # Extend the allowed schemes for the repository URL based on1013 # Extend the allowed schemes for the repository URL based on
1023 # rcs_type.1014 # rcs_type.
1024 extra_schemes = {1015 extra_schemes = {
1025 RevisionControlSystemsExtended.BZR_SVN: ['svn'],1016 RevisionControlSystems.BZR_SVN: ['svn'],
1026 RevisionControlSystemsExtended.GIT: ['git'],1017 RevisionControlSystems.GIT: ['git'],
1018 RevisionControlSystems.BZR: ['bzr'],
1027 }1019 }
1028 schemes.update(extra_schemes.get(rcs_type, []))1020 schemes.update(extra_schemes.get(rcs_type, []))
1029 return schemes1021 return schemes
@@ -1050,7 +1042,7 @@
1050 # The branch location is not required for validation.1042 # The branch location is not required for validation.
1051 self._setRequired(['branch_location'], False)1043 self._setRequired(['branch_location'], False)
1052 # The cvs_module is required if it is a CVS import.1044 # The cvs_module is required if it is a CVS import.
1053 if rcs_type == RevisionControlSystemsExtended.CVS:1045 if rcs_type == RevisionControlSystems.CVS:
1054 self._setRequired(['cvs_module'], True)1046 self._setRequired(['cvs_module'], True)
1055 else:1047 else:
1056 raise AssertionError("Unknown branch type %s" % branch_type)1048 raise AssertionError("Unknown branch type %s" % branch_type)
@@ -1110,7 +1102,7 @@
1110 # Either create an externally hosted bzr branch1102 # Either create an externally hosted bzr branch
1111 # (a.k.a. 'mirrored') or create a new code import.1103 # (a.k.a. 'mirrored') or create a new code import.
1112 rcs_type = data.get('rcs_type')1104 rcs_type = data.get('rcs_type')
1113 if rcs_type == RevisionControlSystemsExtended.BZR:1105 if rcs_type == RevisionControlSystems.BZR:
1114 branch = self._createBzrBranch(1106 branch = self._createBzrBranch(
1115 BranchType.MIRRORED, branch_name, branch_owner,1107 BranchType.MIRRORED, branch_name, branch_owner,
1116 data['repo_url'])1108 data['repo_url'])
@@ -1123,7 +1115,7 @@
1123 'the series.')1115 'the series.')
1124 else:1116 else:
1125 # We need to create an import request.1117 # We need to create an import request.
1126 if rcs_type == RevisionControlSystemsExtended.CVS:1118 if rcs_type == RevisionControlSystems.CVS:
1127 cvs_root = data.get('repo_url')1119 cvs_root = data.get('repo_url')
1128 cvs_module = data.get('cvs_module')1120 cvs_module = data.get('cvs_module')
1129 url = None1121 url = None
11301122
=== modified file 'lib/lp/registry/browser/tests/test_distroseries.py'
--- lib/lp/registry/browser/tests/test_distroseries.py 2011-07-07 20:07:51 +0000
+++ lib/lp/registry/browser/tests/test_distroseries.py 2011-07-18 11:13:26 +0000
@@ -32,11 +32,16 @@
32from canonical.config import config32from canonical.config import config
33from canonical.database.constants import UTC_NOW33from canonical.database.constants import UTC_NOW
34from canonical.database.sqlbase import flush_database_caches34from canonical.database.sqlbase import flush_database_caches
35<<<<<<< TREE
35from canonical.launchpad.testing.pages import (36from canonical.launchpad.testing.pages import (
36 extract_text,37 extract_text,
37 find_tag_by_id,38 find_tag_by_id,
38 )39 )
39from canonical.launchpad.webapp.authorization import check_permission40from canonical.launchpad.webapp.authorization import check_permission
41=======
42from canonical.launchpad.testing.pages import find_tag_by_id
43from canonical.launchpad.webapp.authorization import check_permission
44>>>>>>> MERGE-SOURCE
40from canonical.launchpad.webapp.batching import BatchNavigator45from canonical.launchpad.webapp.batching import BatchNavigator
41from canonical.launchpad.webapp.interaction import get_current_principal46from canonical.launchpad.webapp.interaction import get_current_principal
42from canonical.launchpad.webapp.interfaces import BrowserNotificationLevel47from canonical.launchpad.webapp.interfaces import BrowserNotificationLevel
@@ -829,6 +834,60 @@
829 check_permission('launchpad.Edit', view))834 check_permission('launchpad.Edit', view))
830835
831836
837class TestDistroSeriesInitializeViewAccess(TestCaseWithFactory):
838 """Test access to IDS.+initseries."""
839
840 layer = LaunchpadFunctionalLayer
841
842 def setUp(self):
843 super(TestDistroSeriesInitializeViewAccess,
844 self).setUp('foo.bar@canonical.com')
845 set_derived_series_ui_feature_flag(self)
846
847 def test_initseries_access_anon(self):
848 # Anonymous users cannot access +initseries.
849 distroseries = self.factory.makeDistroSeries()
850 view = create_initialized_view(distroseries, "+initseries")
851 login(ANONYMOUS)
852
853 self.assertEqual(
854 False,
855 check_permission('launchpad.Edit', view))
856
857 def test_initseries_access_simpleuser(self):
858 # Unprivileged users cannot access +initseries.
859 distroseries = self.factory.makeDistroSeries()
860 view = create_initialized_view(distroseries, "+initseries")
861 login_person(self.factory.makePerson())
862
863 self.assertEqual(
864 False,
865 check_permission('launchpad.Edit', view))
866
867 def test_initseries_access_admin(self):
868 # Users with lp.Admin can access +initseries.
869 distroseries = self.factory.makeDistroSeries()
870 view = create_initialized_view(distroseries, "+initseries")
871 login_celebrity('admin')
872
873 self.assertEqual(
874 True,
875 check_permission('launchpad.Edit', view))
876
877 def test_initseries_access_driver(self):
878 # Distroseries drivers can access +initseries.
879 distroseries = self.factory.makeDistroSeries()
880 view = create_initialized_view(distroseries, "+initseries")
881 driver = self.factory.makePerson()
882 with celebrity_logged_in('admin'):
883 distroseries.driver = driver
884 login_person(driver)
885
886 self.assertEqual(
887 True,
888 check_permission('launchpad.Edit', view))
889
890
832class DistroSeriesDifferenceMixin:891class DistroSeriesDifferenceMixin:
833 """A helper class for testing differences pages"""892 """A helper class for testing differences pages"""
834893
835894
=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py 2011-07-07 20:07:51 +0000
+++ lib/lp/registry/interfaces/distroseries.py 2011-07-18 11:13:26 +0000
@@ -1075,6 +1075,7 @@
1075 released == None will do no filtering on status.1075 released == None will do no filtering on status.
1076 """1076 """
10771077
1078<<<<<<< TREE
1078 def priorReleasedSeries(self, distribution, prior_to_date):1079 def priorReleasedSeries(self, distribution, prior_to_date):
1079 """Find distroseries for the supplied distro released before a1080 """Find distroseries for the supplied distro released before a
1080 certain date.1081 certain date.
@@ -1089,6 +1090,10 @@
10891090
10901091
1091@error_status(httplib.BAD_REQUEST)1092@error_status(httplib.BAD_REQUEST)
1093=======
1094
1095@error_status(httplib.BAD_REQUEST)
1096>>>>>>> MERGE-SOURCE
1092class DerivationError(Exception):1097class DerivationError(Exception):
1093 """Raised when there is a problem deriving a distroseries."""1098 """Raised when there is a problem deriving a distroseries."""
1094 _message_prefix = "Error deriving distro series"1099 _message_prefix = "Error deriving distro series"
10951100
=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py 2011-07-14 14:08:04 +0000
+++ lib/lp/registry/model/distribution.py 2011-07-18 11:13:26 +0000
@@ -655,6 +655,7 @@
655 """See `IBugTarget`."""655 """See `IBugTarget`."""
656 return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self))656 return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self))
657657
658<<<<<<< TREE
658 def getBranchTips(self, since=None):659 def getBranchTips(self, since=None):
659 """See `IDistribution`."""660 """See `IDistribution`."""
660 query = """661 query = """
@@ -684,6 +685,17 @@
684 # to the end of the current record, removing Nones from the list.685 # to the end of the current record, removing Nones from the list.
685 result[-1].append(filter(None, map(itemgetter(-1), group)))686 result[-1].append(filter(None, map(itemgetter(-1), group)))
686 return result687 return result
688=======
689 def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0,
690 include_tags=None):
691 """See IBugTarget."""
692 # Circular fail.
693 from lp.bugs.model.bugsummary import BugSummary
694 return get_bug_tags_open_count(
695 And(BugSummary.distribution_id == self.id,
696 BugSummary.sourcepackagename_id == None),
697 user, tag_limit=tag_limit, include_tags=include_tags)
698>>>>>>> MERGE-SOURCE
687699
688 def getMirrorByName(self, name):700 def getMirrorByName(self, name):
689 """See `IDistribution`."""701 """See `IDistribution`."""
@@ -1414,6 +1426,7 @@
1414 # Note that in the source package case, we don't restrict1426 # Note that in the source package case, we don't restrict
1415 # the search to the distribution release, making a best1427 # the search to the distribution release, making a best
1416 # effort to find a package.1428 # effort to find a package.
1429<<<<<<< TREE
1417 publishing = IStore(SourcePackagePublishingHistory).find(1430 publishing = IStore(SourcePackagePublishingHistory).find(
1418 SourcePackagePublishingHistory,1431 SourcePackagePublishingHistory,
1419 # We use an extra query to get the IDs instead of an1432 # We use an extra query to get the IDs instead of an
@@ -1431,6 +1444,20 @@
1431 PackagePublishingStatus.PENDING)1444 PackagePublishingStatus.PENDING)
1432 )).order_by(1445 )).order_by(
1433 Desc(SourcePackagePublishingHistory.id)).first()1446 Desc(SourcePackagePublishingHistory.id)).first()
1447=======
1448 publishing = IStore(SourcePackagePublishingHistory).find(
1449 SourcePackagePublishingHistory,
1450 SourcePackagePublishingHistory.archiveID.is_in(
1451 self.all_distro_archive_ids),
1452 SourcePackagePublishingHistory.sourcepackagereleaseID ==
1453 SourcePackageRelease.id,
1454 SourcePackageRelease.sourcepackagename == sourcepackagename,
1455 SourcePackagePublishingHistory.status.is_in(
1456 (PackagePublishingStatus.PUBLISHED,
1457 PackagePublishingStatus.PENDING)
1458 )).order_by(
1459 Desc(SourcePackagePublishingHistory.id)).first()
1460>>>>>>> MERGE-SOURCE
1434 if publishing is not None:1461 if publishing is not None:
1435 return sourcepackagename1462 return sourcepackagename
14361463
@@ -1453,6 +1480,7 @@
1453 # the sourcepackagename from that.1480 # the sourcepackagename from that.
1454 bpph = IStore(BinaryPackagePublishingHistory).find(1481 bpph = IStore(BinaryPackagePublishingHistory).find(
1455 BinaryPackagePublishingHistory,1482 BinaryPackagePublishingHistory,
1483<<<<<<< TREE
1456 # See comment above for rationale for using an extra query1484 # See comment above for rationale for using an extra query
1457 # instead of an inner join. (Bottom line, it would time out1485 # instead of an inner join. (Bottom line, it would time out
1458 # otherwise.)1486 # otherwise.)
@@ -1460,6 +1488,12 @@
1460 self.all_distro_archive_ids),1488 self.all_distro_archive_ids),
1461 BinaryPackagePublishingHistory.binarypackagereleaseID ==1489 BinaryPackagePublishingHistory.binarypackagereleaseID ==
1462 BinaryPackageRelease.id,1490 BinaryPackageRelease.id,
1491=======
1492 BinaryPackagePublishingHistory.archiveID.is_in(
1493 self.all_distro_archive_ids),
1494 BinaryPackagePublishingHistory.binarypackagereleaseID ==
1495 BinaryPackageRelease.id,
1496>>>>>>> MERGE-SOURCE
1463 BinaryPackageRelease.binarypackagename == binarypackagename,1497 BinaryPackageRelease.binarypackagename == binarypackagename,
1464 BinaryPackagePublishingHistory.dateremoved == None,1498 BinaryPackagePublishingHistory.dateremoved == None,
1465 ).order_by(1499 ).order_by(
14661500
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2011-06-21 01:34:08 +0000
+++ lib/lp/registry/model/product.py 2011-07-18 11:13:26 +0000
@@ -800,6 +800,18 @@
800 """See `IBugTarget`."""800 """See `IBugTarget`."""
801 return get_bug_tags("BugTask.product = %s" % sqlvalues(self))801 return get_bug_tags("BugTask.product = %s" % sqlvalues(self))
802802
803<<<<<<< TREE
804=======
805 def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0,
806 include_tags=None):
807 """See IBugTarget."""
808 # Circular fail.
809 from lp.bugs.model.bugsummary import BugSummary
810 return get_bug_tags_open_count(
811 BugSummary.product_id == self.id,
812 user, tag_limit=tag_limit, include_tags=include_tags)
813
814>>>>>>> MERGE-SOURCE
803 series = SQLMultipleJoin('ProductSeries', joinColumn='product',815 series = SQLMultipleJoin('ProductSeries', joinColumn='product',
804 orderBy='name')816 orderBy='name')
805817
806818
=== modified file 'lib/lp/soyuz/browser/queue.py'
--- lib/lp/soyuz/browser/queue.py 2011-06-29 07:25:18 +0000
+++ lib/lp/soyuz/browser/queue.py 2011-07-18 11:13:26 +0000
@@ -46,6 +46,7 @@
46 QueueInconsistentStateError,46 QueueInconsistentStateError,
47 )47 )
48from lp.soyuz.interfaces.section import ISectionSet48from lp.soyuz.interfaces.section import ISectionSet
49from lp.soyuz.model.queue import PackageUploadSource
4950
5051
51QUEUE_SIZE = 3052QUEUE_SIZE = 30
@@ -197,10 +198,16 @@
197 CompletePackageUpload. This avoids many additional SQL queries198 CompletePackageUpload. This avoids many additional SQL queries
198 in the +queue template.199 in the +queue template.
199 """200 """
201<<<<<<< TREE
200 # Avoid circular imports.202 # Avoid circular imports.
201 from lp.soyuz.model.queue import PackageUploadSource203 from lp.soyuz.model.queue import PackageUploadSource
202 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease204 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
203205
206=======
207 # Avoid circular imports.
208 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
209
210>>>>>>> MERGE-SOURCE
204 uploads = list(self.batchnav.currentBatch())211 uploads = list(self.batchnav.currentBatch())
205212
206 if len(uploads) == 0:213 if len(uploads) == 0:
@@ -214,9 +221,14 @@
214 upload.status != PackageUploadStatus.DONE)]221 upload.status != PackageUploadStatus.DONE)]
215 binary_file_set = getUtility(IBinaryPackageFileSet)222 binary_file_set = getUtility(IBinaryPackageFileSet)
216 binary_files = binary_file_set.getByPackageUploadIDs(upload_ids)223 binary_files = binary_file_set.getByPackageUploadIDs(upload_ids)
224<<<<<<< TREE
217 binary_file_set.loadLibraryFiles(binary_files)225 binary_file_set.loadLibraryFiles(binary_files)
218 packageuploadsources = load_referencing(226 packageuploadsources = load_referencing(
219 PackageUploadSource, uploads, ['packageuploadID'])227 PackageUploadSource, uploads, ['packageuploadID'])
228=======
229 packageuploadsources = load_referencing(
230 PackageUploadSource, uploads, ['packageuploadID'])
231>>>>>>> MERGE-SOURCE
220 source_file_set = getUtility(ISourcePackageReleaseFileSet)232 source_file_set = getUtility(ISourcePackageReleaseFileSet)
221 source_files = source_file_set.getByPackageUploadIDs(upload_ids)233 source_files = source_file_set.getByPackageUploadIDs(upload_ids)
222234
@@ -490,6 +502,12 @@
490 else:502 else:
491 self.package_sets = []503 self.package_sets = []
492504
505 if self.contains_source:
506 self.package_sets = package_sets.get(
507 self.sourcepackagerelease.sourcepackagenameID, [])
508 else:
509 self.package_sets = []
510
493 @property511 @property
494 def pending_delayed_copy(self):512 def pending_delayed_copy(self):
495 """Whether the context is a delayed-copy pending processing."""513 """Whether the context is a delayed-copy pending processing."""
@@ -509,6 +527,7 @@
509 return self.context.changesfile527 return self.context.changesfile
510528
511 @property529 @property
530<<<<<<< TREE
512 def display_package_sets(self):531 def display_package_sets(self):
513 """Package sets, if any, for display on the +queue page."""532 """Package sets, if any, for display on the +queue page."""
514 return ' '.join(sorted(533 return ' '.join(sorted(
@@ -592,3 +611,89 @@
592 (%s)611 (%s)
593 </div>612 </div>
594 """ % (iconlist_id, '\n'.join(icons), link, self.displayarchs)613 """ % (iconlist_id, '\n'.join(icons), link, self.displayarchs)
614=======
615 def display_package_sets(self):
616 """Package sets, if any, for display on the +queue page."""
617 if self.contains_source:
618 return ' '.join(sorted(
619 packageset.name for packageset in self.package_sets))
620 else:
621 return ""
622
623 @property
624 def display_component(self):
625 """Component name, if any, for display on the +queue page."""
626 if self.contains_source:
627 return self.component_name.lower()
628 else:
629 return ""
630
631 @property
632 def display_section(self):
633 """Section name, if any, for display on the +queue page."""
634 if self.contains_source:
635 return self.section_name.lower()
636 else:
637 return ""
638
639 @property
640 def display_priority(self):
641 """Priority name, if any, for display on the +queue page."""
642 if self.contains_source:
643 return self.sourcepackagerelease.urgency.name.lower()
644 else:
645 return ""
646
647 def composeIcon(self, alt, icon, title=None):
648 """Compose an icon for the package's icon list."""
649 # These should really be sprites!
650 if title is None:
651 title = alt
652 return '<img alt="[%s]" src="/@@/%s" title="%s" />' % (
653 cgi.escape(alt, quote=True),
654 icon,
655 cgi.escape(title, quote=True),
656 )
657
658 def composeIconList(self):
659 """List icons that should be shown for this upload."""
660 ddtp = "Debian Description Translation Project Indexes"
661 potential_icons = [
662 (self.contains_source, ("Source", 'package-source')),
663 (self.contains_build, ("Build", 'package-binary', "Binary")),
664 (self.contains_translation, ("Translation", 'translation-file')),
665 (self.contains_installer, ("Installer", 'ubuntu-icon')),
666 (self.contains_upgrader, ("Upgrader", 'ubuntu-icon')),
667 (self.contains_ddtp, (ddtp, 'ubuntu-icon')),
668 ]
669 return [
670 self.composeIcon(*details)
671 for condition, details in potential_icons
672 if condition]
673
674 def composeNameAndChangesLink(self):
675 """Compose HTML: upload name and link to changes file."""
676 raw_displayname = self.displayname
677 displayname = cgi.escape(raw_displayname)
678 if self.pending_delayed_copy or self.changesfile is None:
679 return displayname
680 else:
681 return '<a href="%s" title="Changes file for %s">%s</a>' % (
682 self.changesfile.http_url,
683 cgi.escape(self.displayname, quote=True),
684 displayname)
685
686 @property
687 def icons_and_name(self):
688 """Icon list and name, linked to changes file if appropriate."""
689 iconlist_id = "queue%d-iconlist" % self.id
690 icons = self.composeIconList()
691 link = self.composeNameAndChangesLink()
692 return """
693 <div id="%s">
694 %s
695 %s
696 (%s)
697 </div>
698 """ % (iconlist_id, '\n'.join(icons), link, self.displayarchs)
699>>>>>>> MERGE-SOURCE
595700
=== modified file 'lib/lp/soyuz/browser/tests/test_queue.py'
--- lib/lp/soyuz/browser/tests/test_queue.py 2011-06-23 13:05:52 +0000
+++ lib/lp/soyuz/browser/tests/test_queue.py 2011-07-18 11:13:26 +0000
@@ -234,6 +234,7 @@
234 upload.distroseries.main_archive)234 upload.distroseries.main_archive)
235 with person_logged_in(queue_admin):235 with person_logged_in(queue_admin):
236 view = self.makeView(upload.distroseries, queue_admin)236 view = self.makeView(upload.distroseries, queue_admin)
237<<<<<<< TREE
237 html_text = view()238 html_text = view()
238 self.assertIn(upload.package_name, html_text)239 self.assertIn(upload.package_name, html_text)
239240
@@ -414,3 +415,168 @@
414 self.assertNotEqual(None, icons_and_name.find("a"))415 self.assertNotEqual(None, icons_and_name.find("a"))
415 self.assertIn(416 self.assertIn(
416 complete_upload.displayarchs, ' '.join(icons_and_name.itertext()))417 complete_upload.displayarchs, ' '.join(icons_and_name.itertext()))
418=======
419 html_text = view()
420 self.assertIn(upload.package_name, html_text)
421
422
423class TestCompletePackageUpload(TestCaseWithFactory):
424
425 layer = LaunchpadZopelessLayer
426
427 def makeCompletePackageUpload(self, upload=None, build_upload_files=None,
428 source_upload_files=None,
429 package_sets=None):
430 if upload is None:
431 upload = self.factory.makeSourcePackageUpload()
432 if build_upload_files is None:
433 build_upload_files = {}
434 if source_upload_files is None:
435 source_upload_files = {}
436 if package_sets is None:
437 package_sets = {}
438 return CompletePackageUpload(
439 upload, build_upload_files, source_upload_files, package_sets)
440
441 def mapPackageSets(self, upload, package_sets=None):
442 if package_sets is None:
443 package_sets = [self.factory.makePackageset(
444 distroseries=upload.distroseries)]
445 spn = upload.sourcepackagerelease.sourcepackagename
446 return {spn.id: package_sets}
447
448 def test_display_package_sets_returns_source_upload_packagesets(self):
449 upload = self.factory.makeSourcePackageUpload()
450 package_sets = self.mapPackageSets(upload)
451 complete_upload = self.makeCompletePackageUpload(
452 upload, package_sets=package_sets)
453 self.assertEqual(
454 package_sets.values()[0][0].name,
455 complete_upload.display_package_sets)
456
457 def test_display_package_sets_returns_empty_for_non_source_upload(self):
458 upload = self.factory.makeBuildPackageUpload()
459 complete_upload = self.makeCompletePackageUpload(
460 upload, package_sets=self.mapPackageSets(upload))
461 self.assertEqual("", complete_upload.display_package_sets)
462
463 def test_display_package_sets_sorts_by_name(self):
464 complete_upload = self.makeCompletePackageUpload()
465 distroseries = complete_upload.distroseries
466 complete_upload.package_sets = [
467 self.factory.makePackageset(distroseries=distroseries, name=name)
468 for name in [u'ccc', u'aaa', u'bbb']]
469 self.assertEqual("aaa bbb ccc", complete_upload.display_package_sets)
470
471 def test_display_component_returns_source_upload_component_name(self):
472 complete_upload = self.makeCompletePackageUpload()
473 self.assertEqual(
474 complete_upload.sourcepackagerelease.component.name.lower(),
475 complete_upload.display_component)
476
477 def test_display_component_returns_empty_for_non_source_upload(self):
478 complete_upload = self.makeCompletePackageUpload(
479 self.factory.makeBuildPackageUpload())
480 self.assertEqual('', complete_upload.display_component)
481
482 def test_display_section_returns_source_upload_section_name(self):
483 complete_upload = self.makeCompletePackageUpload()
484 self.assertEqual(
485 complete_upload.sourcepackagerelease.section.name.lower(),
486 complete_upload.display_section)
487
488 def test_display_section_returns_empty_for_non_source_upload(self):
489 complete_upload = self.makeCompletePackageUpload(
490 self.factory.makeBuildPackageUpload())
491 self.assertEqual('', complete_upload.display_section)
492
493 def test_display_priority_returns_source_upload_priority(self):
494 complete_upload = self.makeCompletePackageUpload()
495 self.assertEqual(
496 complete_upload.sourcepackagerelease.urgency.name.lower(),
497 complete_upload.display_priority)
498
499 def test_display_priority_returns_empty_for_non_source_upload(self):
500 complete_upload = self.makeCompletePackageUpload(
501 self.factory.makeBuildPackageUpload())
502 self.assertEqual('', complete_upload.display_priority)
503
504 def test_composeIcon_produces_image_tag(self):
505 alt = self.factory.getUniqueString()
506 icon = self.factory.getUniqueString() + ".png"
507 title = self.factory.getUniqueString()
508 html_text = self.makeCompletePackageUpload().composeIcon(
509 alt, icon, title)
510 img = html.fromstring(html_text)
511 self.assertEqual("img", img.tag)
512 self.assertEqual("[%s]" % alt, img.get("alt"))
513 self.assertEqual("/@@/" + icon, img.get("src"))
514 self.assertEqual(title, img.get("title"))
515
516 def test_composeIcon_title_defaults_to_alt_text(self):
517 alt = self.factory.getUniqueString()
518 icon = self.factory.getUniqueString() + ".png"
519 html_text = self.makeCompletePackageUpload().composeIcon(alt, icon)
520 img = html.fromstring(html_text)
521 self.assertEqual(alt, img.get("title"))
522
523 def test_composeIcon_escapes_alt_and_title(self):
524 alt = 'alt"&'
525 icon = self.factory.getUniqueString() + ".png"
526 title = 'title"&'
527 html_text = self.makeCompletePackageUpload().composeIcon(
528 alt, icon, title)
529 img = html.fromstring(html_text)
530 self.assertEqual("[%s]" % alt, img.get("alt"))
531 self.assertEqual(title, img.get("title"))
532
533 def test_composeIconList_produces_icons(self):
534 icons = self.makeCompletePackageUpload().composeIconList()
535 self.assertNotEqual([], icons)
536 self.assertEqual('img', html.fromstring(icons[0]).tag)
537
538 def test_composeIconList_produces_icons_conditionally(self):
539 complete_upload = self.makeCompletePackageUpload()
540 base_count = len(complete_upload.composeIconList())
541 complete_upload.contains_build = True
542 new_count = len(complete_upload.composeIconList())
543 self.assertEqual(base_count + 1, new_count)
544
545 def test_composeNameAndChangesLink_does_not_link_if_no_changes_file(self):
546 upload = self.factory.makeCopyJobPackageUpload()
547 complete_upload = self.makeCompletePackageUpload(upload)
548 self.assertEqual(
549 complete_upload.displayname,
550 complete_upload.composeNameAndChangesLink())
551
552 def test_composeNameAndChangesLink_links_to_changes_file(self):
553 complete_upload = self.makeCompletePackageUpload()
554 link = html.fromstring(complete_upload.composeNameAndChangesLink())
555 self.assertEqual(
556 complete_upload.changesfile.http_url, link.get("href"))
557
558 def test_composeNameAndChangesLink_escapes_nonlinked_display_name(self):
559 filename = 'name"&name'
560 upload = self.factory.makeCustomPackageUpload(filename=filename)
561 # Stop nameAndChangesLink from producing a link.
562 upload.changesfile = None
563 complete_upload = self.makeCompletePackageUpload(upload)
564 self.assertIn(
565 cgi.escape(filename), complete_upload.composeNameAndChangesLink())
566
567 def test_composeNameAndChangesLink_escapes_name_in_link(self):
568 filename = 'name"&name'
569 upload = self.factory.makeCustomPackageUpload(filename=filename)
570 complete_upload = self.makeCompletePackageUpload(upload)
571 link = html.fromstring(complete_upload.composeNameAndChangesLink())
572 self.assertIn(filename, link.get("title"))
573 self.assertEqual(filename, link.text)
574
575 def test_icons_and_name_composes_icons_and_link_and_archs(self):
576 complete_upload = self.makeCompletePackageUpload()
577 icons_and_name = html.fromstring(complete_upload.icons_and_name)
578 self.assertNotEqual(None, icons_and_name.find("img"))
579 self.assertNotEqual(None, icons_and_name.find("a"))
580 self.assertIn(
581 complete_upload.displayarchs, ' '.join(icons_and_name.itertext()))
582>>>>>>> MERGE-SOURCE
417583
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2011-06-24 21:21:36 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2011-07-18 11:13:26 +0000
@@ -101,9 +101,16 @@
101from lp.soyuz.enums import ArchivePurpose101from lp.soyuz.enums import ArchivePurpose
102from lp.soyuz.interfaces.buildrecords import IHasBuildRecords102from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
103from lp.soyuz.interfaces.component import IComponent103from lp.soyuz.interfaces.component import IComponent
104104<<<<<<< TREE
105105
106@error_status(httplib.BAD_REQUEST)106
107@error_status(httplib.BAD_REQUEST)
108=======
109from lp.soyuz.interfaces.processor import IProcessorFamily
110
111
112@error_status(httplib.BAD_REQUEST)
113>>>>>>> MERGE-SOURCE
107class ArchiveDependencyError(Exception):114class ArchiveDependencyError(Exception):
108 """Raised when an `IArchiveDependency` does not fit the context archive.115 """Raised when an `IArchiveDependency` does not fit the context archive.
109116
110117
=== modified file 'lib/lp/soyuz/interfaces/files.py'
--- lib/lp/soyuz/interfaces/files.py 2011-06-29 11:40:03 +0000
+++ lib/lp/soyuz/interfaces/files.py 2011-07-18 11:13:26 +0000
@@ -22,8 +22,12 @@
22 )22 )
2323
24from canonical.launchpad import _24from canonical.launchpad import _
25<<<<<<< TREE
25from canonical.launchpad.interfaces.librarian import ILibraryFileAlias26from canonical.launchpad.interfaces.librarian import ILibraryFileAlias
26from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease27from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
28=======
29from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
30>>>>>>> MERGE-SOURCE
2731
2832
29class IBinaryPackageFile(Interface):33class IBinaryPackageFile(Interface):
3034
=== modified file 'lib/lp/soyuz/model/packagesetsources.py'
--- lib/lp/soyuz/model/packagesetsources.py 2011-07-01 15:53:38 +0000
+++ lib/lp/soyuz/model/packagesetsources.py 2011-07-18 11:13:26 +0000
@@ -1,3 +1,4 @@
1<<<<<<< TREE
1# Copyright 2011 Canonical Ltd. This software is licensed under the2# Copyright 2011 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).3# GNU Affero General Public License version 3 (see the file LICENSE).
34
@@ -43,3 +44,46 @@
43 def __init__(self, packageset, sourcepackagename):44 def __init__(self, packageset, sourcepackagename):
44 self.packageset = packageset45 self.packageset = packageset
45 self.sourcepackagename = sourcepackagename46 self.sourcepackagename = sourcepackagename
47=======
48# Copyright 2011 Canonical Ltd. This software is licensed under the
49# GNU Affero General Public License version 3 (see the file LICENSE).
50
51"""The `PackagesetSources` linking table.
52
53This table associates `Packageset`s with `SourcePackageName`s.
54"""
55
56__metaclass__ = type
57__all__ = [
58 'PackagesetSources',
59 ]
60
61from storm.locals import (
62 Int,
63 Reference,
64 Storm,
65 )
66
67
68class PackagesetSources(Storm):
69 """Linking table: which packages are in a package set?"""
70 # This table is largely managed from Packageset, but also directly
71 # accessed from other places.
72
73 __storm_table__ = 'PackagesetSources'
74
75 # There's a vestigial id as well, a holdover from the SQLObject
76 # days. Nobody seems to use it. The only key that matters is
77 # (packageset, sourcepackagename).
78 # XXX JeroenVermeulen 2011-06-22, bug=800677: Drop the id column.
79 __storm_primary__ = (
80 'packageset_id',
81 'sourcepackagename_id',
82 )
83
84 packageset_id = Int(name='packageset')
85 packageset = Reference(packageset_id, 'Packageset.id')
86 sourcepackagename_id = Int(name='sourcepackagename')
87 sourcepackagename = Reference(
88 sourcepackagename_id, 'SourcePackageName.id')
89>>>>>>> MERGE-SOURCE
4690
=== modified file 'lib/lp/soyuz/scripts/tests/test_queue.py'
--- lib/lp/soyuz/scripts/tests/test_queue.py 2011-07-07 16:05:03 +0000
+++ lib/lp/soyuz/scripts/tests/test_queue.py 2011-07-18 11:13:26 +0000
@@ -966,6 +966,7 @@
966 distro.name, distroseries.name, queue, terms, component.name,966 distro.name, distroseries.name, queue, terms, component.name,
967 section.name, priority_name, display)967 section.name, priority_name, display)
968968
969<<<<<<< TREE
969 def makeQueueActionOverride(self, package_upload, component, section,970 def makeQueueActionOverride(self, package_upload, component, section,
970 distroseries=None):971 distroseries=None):
971 return self.makeQueueAction(972 return self.makeQueueAction(
@@ -980,6 +981,16 @@
980 """981 """
981 return tuple(item.strip() for item in output_line.split('|'))982 return tuple(item.strip() for item in output_line.split('|'))
982983
984=======
985 def parseUploadSummaryLine(self, output_line):
986 """Parse an output line from `QueueAction.displayItem`.
987
988 :param output_line: A line of output text from `displayItem`.
989 :return: A tuple of displayed items: (id, tag, name, version, age).
990 """
991 return tuple(item.strip() for item in output_line.split('|'))
992
993>>>>>>> MERGE-SOURCE
983 def test_display_actions_have_privileges_for_PackageCopyJob(self):994 def test_display_actions_have_privileges_for_PackageCopyJob(self):
984 # The methods that display uploads have privileges to work with995 # The methods that display uploads have privileges to work with
985 # a PackageUpload that has a copy job.996 # a PackageUpload that has a copy job.
@@ -1031,6 +1042,7 @@
1031 action = self.makeQueueAction(upload)1042 action = self.makeQueueAction(upload)
10321043
1033 action.displayItem(upload)1044 action.displayItem(upload)
1045<<<<<<< TREE
10341046
1035 ((output, ), kwargs) = action.display.calls[0]1047 ((output, ), kwargs) = action.display.calls[0]
1036 (upload_id, tag, name, version, age) = self.parseUploadSummaryLine(1048 (upload_id, tag, name, version, age) = self.parseUploadSummaryLine(
@@ -1085,6 +1097,37 @@
1085 def test_makeTag_returns_dashes_for_custom_upload(self):1097 def test_makeTag_returns_dashes_for_custom_upload(self):
1086 upload = self.factory.makeCustomPackageUpload()1098 upload = self.factory.makeCustomPackageUpload()
1087 self.assertEqual('--', self.makeQueueAction(upload)._makeTag(upload))1099 self.assertEqual('--', self.makeQueueAction(upload)._makeTag(upload))
1100=======
1101
1102 ((output, ), kwargs) = action.display.calls[0]
1103 (upload_id, tag, name, version, age) = self.parseUploadSummaryLine(
1104 output)
1105 self.assertEqual(str(upload.id), upload_id)
1106 self.assertEqual("X-", tag)
1107 self.assertThat(upload.displayname, StartsWith(name))
1108 self.assertThat(upload.package_version, StartsWith(version))
1109
1110 def test_makeTag_returns_S_for_source_upload(self):
1111 upload = self.factory.makeSourcePackageUpload()
1112 self.assertEqual('S-', self.makeQueueAction(upload)._makeTag(upload))
1113
1114 def test_makeTag_returns_B_for_binary_upload(self):
1115 upload = self.factory.makeBuildPackageUpload()
1116 self.assertEqual('-B', self.makeQueueAction(upload)._makeTag(upload))
1117
1118 def test_makeTag_returns_SB_for_mixed_upload(self):
1119 upload = self.factory.makeSourcePackageUpload()
1120 upload.addBuild(self.factory.makeBinaryPackageBuild())
1121 self.assertEqual('SB', self.makeQueueAction(upload)._makeTag(upload))
1122
1123 def test_makeTag_returns_X_for_copy_job_upload(self):
1124 upload = self.factory.makeCopyJobPackageUpload()
1125 self.assertEqual('X-', self.makeQueueAction(upload)._makeTag(upload))
1126
1127 def test_makeTag_returns_dashes_for_custom_upload(self):
1128 upload = self.factory.makeCustomPackageUpload()
1129 self.assertEqual('--', self.makeQueueAction(upload)._makeTag(upload))
1130>>>>>>> MERGE-SOURCE
10881131
1089 def test_displayInfo_displays_PackageUpload_with_source(self):1132 def test_displayInfo_displays_PackageUpload_with_source(self):
1090 # displayInfo can display a source package upload.1133 # displayInfo can display a source package upload.
10911134
=== modified file 'lib/lp/soyuz/stories/webservice/xx-archive.txt'
--- lib/lp/soyuz/stories/webservice/xx-archive.txt 2011-06-23 16:05:30 +0000
+++ lib/lp/soyuz/stories/webservice/xx-archive.txt 2011-07-18 11:13:26 +0000
@@ -17,6 +17,7 @@
17 >>> from lazr.restful.testing.webservice import pprint_entry17 >>> from lazr.restful.testing.webservice import pprint_entry
18 >>> pprint_entry(cprov_archive)18 >>> pprint_entry(cprov_archive)
19 commercial: False19 commercial: False
20<<<<<<< TREE
20 dependencies_collection_link:21 dependencies_collection_link:
21 u'http://.../~cprov/+archive/ppa/dependencies'22 u'http://.../~cprov/+archive/ppa/dependencies'
22 description: u'packages to help my friends.'23 description: u'packages to help my friends.'
@@ -38,6 +39,10 @@
38 >>> pprint_entry(cprov_archive_devel)39 >>> pprint_entry(cprov_archive_devel)
39 commercial: False40 commercial: False
40 dependencies_collection_link: u'http://.../~cprov/+archive/ppa/dependencies'41 dependencies_collection_link: u'http://.../~cprov/+archive/ppa/dependencies'
42=======
43 dependencies_collection_link:
44 u'http://.../~cprov/+archive/ppa/dependencies'
45>>>>>>> MERGE-SOURCE
41 description: u'packages to help my friends.'46 description: u'packages to help my friends.'
42 displayname: u'PPA for Celso Providelo'47 displayname: u'PPA for Celso Providelo'
43 distribution_link: u'http://.../ubuntu'48 distribution_link: u'http://.../ubuntu'
4449
=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
--- lib/lp/soyuz/tests/test_packageupload.py 2011-07-11 08:31:46 +0000
+++ lib/lp/soyuz/tests/test_packageupload.py 2011-07-18 11:13:26 +0000
@@ -816,6 +816,7 @@
816 [upload],816 [upload],
817 upload_set.getAll(817 upload_set.getAll(
818 distroseries, name=spn.name, version=upload.displayversion))818 distroseries, name=spn.name, version=upload.displayversion))
819<<<<<<< TREE
819820
820 def test_getAll_orders_in_reverse_historical_order(self):821 def test_getAll_orders_in_reverse_historical_order(self):
821 # The results from getAll are returned in order of creation,822 # The results from getAll are returned in order of creation,
@@ -838,3 +839,5 @@
838 self.assertEqual(839 self.assertEqual(
839 list(reversed(ordered_uploads)),840 list(reversed(ordered_uploads)),
840 list(getUtility(IPackageUploadSet).getAll(series)))841 list(getUtility(IPackageUploadSet).getAll(series)))
842=======
843>>>>>>> MERGE-SOURCE
841844
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2011-07-13 20:55:34 +0000
+++ lib/lp/testing/factory.py 2011-07-18 11:13:26 +0000
@@ -474,7 +474,7 @@
474 branch_id = self.getUniqueInteger()474 branch_id = self.getUniqueInteger()
475 if rcstype is None:475 if rcstype is None:
476 rcstype = 'svn'476 rcstype = 'svn'
477 if rcstype in ['svn', 'bzr-svn', 'hg']:477 if rcstype in ['svn', 'bzr-svn', 'hg', 'bzr']:
478 assert cvs_root is cvs_module is None478 assert cvs_root is cvs_module is None
479 if url is None:479 if url is None:
480 url = self.getUniqueURL()480 url = self.getUniqueURL()
@@ -2100,7 +2100,8 @@
21002100
2101 def makeCodeImport(self, svn_branch_url=None, cvs_root=None,2101 def makeCodeImport(self, svn_branch_url=None, cvs_root=None,
2102 cvs_module=None, target=None, branch_name=None,2102 cvs_module=None, target=None, branch_name=None,
2103 git_repo_url=None, hg_repo_url=None, registrant=None,2103 git_repo_url=None, hg_repo_url=None,
2104 bzr_branch_url=None, registrant=None,
2104 rcs_type=None, review_status=None):2105 rcs_type=None, review_status=None):
2105 """Create and return a new, arbitrary code import.2106 """Create and return a new, arbitrary code import.
21062107
@@ -2109,7 +2110,7 @@
2109 unique URL.2110 unique URL.
2110 """2111 """
2111 if (svn_branch_url is cvs_root is cvs_module is git_repo_url is2112 if (svn_branch_url is cvs_root is cvs_module is git_repo_url is
2112 hg_repo_url is None):2113 hg_repo_url is bzr_branch_url is None):
2113 svn_branch_url = self.getUniqueURL()2114 svn_branch_url = self.getUniqueURL()
21142115
2115 if target is None:2116 if target is None:
@@ -2140,6 +2141,11 @@
2140 registrant, target, branch_name,2141 registrant, target, branch_name,
2141 rcs_type=RevisionControlSystems.HG,2142 rcs_type=RevisionControlSystems.HG,
2142 url=hg_repo_url)2143 url=hg_repo_url)
2144 elif bzr_branch_url is not None:
2145 code_import = code_import_set.new(
2146 registrant, target, branch_name,
2147 rcs_type=RevisionControlSystems.BZR,
2148 url=bzr_branch_url)
2143 else:2149 else:
2144 assert rcs_type in (None, RevisionControlSystems.CVS)2150 assert rcs_type in (None, RevisionControlSystems.CVS)
2145 code_import = code_import_set.new(2151 code_import = code_import_set.new(
@@ -3485,6 +3491,7 @@
3485 distribution=distribution)3491 distribution=distribution)
34863492
3487 if archive is None:3493 if archive is None:
3494<<<<<<< TREE
3488 archive = distroseries.main_archive3495 archive = distroseries.main_archive
34893496
3490 if (sourcepackagename is None or3497 if (sourcepackagename is None or
@@ -3494,6 +3501,19 @@
34943501
3495 if (component is None or isinstance(component, basestring)):3502 if (component is None or isinstance(component, basestring)):
3496 component = self.makeComponent(component)3503 component = self.makeComponent(component)
3504=======
3505 archive = self.makeArchive(
3506 distribution=distroseries.distribution,
3507 purpose=ArchivePurpose.PRIMARY)
3508
3509 if (sourcepackagename is None or
3510 isinstance(sourcepackagename, basestring)):
3511 sourcepackagename = self.getOrMakeSourcePackageName(
3512 sourcepackagename)
3513
3514 if component is None:
3515 component = self.makeComponent()
3516>>>>>>> MERGE-SOURCE
34973517
3498 if urgency is None:3518 if urgency is None:
3499 urgency = self.getAnySourcePackageUrgency()3519 urgency = self.getAnySourcePackageUrgency()
@@ -3567,6 +3587,7 @@
3567 to determine archive.3587 to determine archive.
3568 :param source_package_release: The SourcePackageRelease this binary3588 :param source_package_release: The SourcePackageRelease this binary
3569 build uses as its source.3589 build uses as its source.
3590<<<<<<< TREE
3570 :param sourcepackagename: when source_package_release is None, the3591 :param sourcepackagename: when source_package_release is None, the
3571 sourcepackagename from which the build will come.3592 sourcepackagename from which the build will come.
3572 :param distroarchseries: The DistroArchSeries to use. Defaults to the3593 :param distroarchseries: The DistroArchSeries to use. Defaults to the
@@ -3574,6 +3595,12 @@
3574 :param archive: The Archive to use. Defaults to the one from the3595 :param archive: The Archive to use. Defaults to the one from the
3575 source_package_release, or the distro arch series main archive3596 source_package_release, or the distro arch series main archive
3576 otherwise.3597 otherwise.
3598=======
3599 :param sourcepackagename: when source_package_release is None, the
3600 sourcepackagename from which the build will come.
3601 :param distroarchseries: The DistroArchSeries to use.
3602 :param archive: The Archive to use.
3603>>>>>>> MERGE-SOURCE
3577 :param builder: An optional builder to assign.3604 :param builder: An optional builder to assign.
3578 :param status: The BuildStatus for the build.3605 :param status: The BuildStatus for the build.
3579 """3606 """
35803607
=== modified file 'scripts/code-import-worker.py'
--- scripts/code-import-worker.py 2011-06-16 23:43:04 +0000
+++ scripts/code-import-worker.py 2011-07-18 11:13:26 +0000
@@ -26,8 +26,9 @@
26from canonical.config import config26from canonical.config import config
27from lp.codehosting import load_optional_plugin27from lp.codehosting import load_optional_plugin
28from lp.codehosting.codeimport.worker import (28from lp.codehosting.codeimport.worker import (
29 BzrSvnImportWorker, CSCVSImportWorker, CodeImportSourceDetails,29 BzrImportWorker, BzrSvnImportWorker, CSCVSImportWorker,
30 GitImportWorker, HgImportWorker, get_default_bazaar_branch_store)30 CodeImportSourceDetails, GitImportWorker, HgImportWorker,
31 get_default_bazaar_branch_store)
31from canonical.launchpad import scripts32from canonical.launchpad import scripts
3233
3334
@@ -65,8 +66,17 @@
65 elif source_details.rcstype == 'hg':66 elif source_details.rcstype == 'hg':
66 load_optional_plugin('hg')67 load_optional_plugin('hg')
67 import_worker_cls = HgImportWorker68 import_worker_cls = HgImportWorker
68 elif source_details.rcstype in ['cvs', 'svn']:69<<<<<<< TREE
69 import_worker_cls = CSCVSImportWorker70 elif source_details.rcstype in ['cvs', 'svn']:
71 import_worker_cls = CSCVSImportWorker
72=======
73 elif source_details.rcstype == 'bzr':
74 load_optional_plugin('loom')
75 load_optional_plugin('weave_fmt')
76 import_worker_cls = BzrImportWorker
77 elif source_details.rcstype in ['cvs', 'svn']:
78 import_worker_cls = CSCVSImportWorker
79>>>>>>> MERGE-SOURCE
70 else:80 else:
71 raise AssertionError(81 raise AssertionError(
72 'unknown rcstype %r' % source_details.rcstype)82 'unknown rcstype %r' % source_details.rcstype)
7383
=== modified file 'utilities/sourcedeps.cache'
--- utilities/sourcedeps.cache 2011-06-29 14:14:20 +0000
+++ utilities/sourcedeps.cache 2011-07-18 11:13:26 +0000
@@ -1,8 +1,4 @@
1{1{
2 "bzr-builder": [
3 68,
4 "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg"
5 ],
6 "testresources": [2 "testresources": [
7 16, 3 16,
8 "robertc@robertcollins.net-20050911111209-ee5da49011cf936a"4 "robertc@robertcollins.net-20050911111209-ee5da49011cf936a"
@@ -31,14 +27,18 @@
31 24, 27 24,
32 "launchpad@pqm.canonical.com-20100601182722-wo7h2fh0fvyw3aaq"28 "launchpad@pqm.canonical.com-20100601182722-wo7h2fh0fvyw3aaq"
33 ], 29 ],
34 "lpreview": [
35 23,
36 "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8"
37 ],
38 "bzr-git": [30 "bzr-git": [
39 259, 31 259,
40 "launchpad@pqm.canonical.com-20110601140035-gl5merbechngjw5s"32 "launchpad@pqm.canonical.com-20110601140035-gl5merbechngjw5s"
41 ], 33 ],
34 "loggerhead": [
35 445,
36 "john@arbash-meinel.com-20110325141442-536j4be3x0c464zy"
37 ],
38 "bzr-builder": [
39 68,
40 "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg"
41 ],
42 "bzr-loom": [42 "bzr-loom": [
43 49, 43 49,
44 "launchpad@pqm.canonical.com-20110601122412-54vo3k8yae9i2zve"44 "launchpad@pqm.canonical.com-20110601122412-54vo3k8yae9i2zve"
@@ -47,9 +47,15 @@
47 4, 47 4,
48 "sinzui-20090526164636-1swugzupwvjgomo4"48 "sinzui-20090526164636-1swugzupwvjgomo4"
49 ], 49 ],
50<<<<<<< TREE
50 "loggerhead": [51 "loggerhead": [
51 452, 52 452,
52 "andrew.bennetts@canonical.com-20110628173100-owrifrnckvoi60af"53 "andrew.bennetts@canonical.com-20110628173100-owrifrnckvoi60af"
54=======
55 "lpreview": [
56 23,
57 "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8"
58>>>>>>> MERGE-SOURCE
53 ], 59 ],
54 "difftacular": [60 "difftacular": [
55 6, 61 6,
5662
=== modified file 'versions.cfg'
--- versions.cfg 2011-07-18 11:13:00 +0000
+++ versions.cfg 2011-07-18 11:13:26 +0000
@@ -78,8 +78,12 @@
78soupmatchers = 0.1r5378soupmatchers = 0.1r53
79sourcecodegen = 0.6.979sourcecodegen = 0.6.9
80storm = 0.18.0.99-lpwithnodatetime-r39380storm = 0.18.0.99-lpwithnodatetime-r393
81<<<<<<< TREE
81testresources = 0.2.4-r5882testresources = 0.2.4-r58
82testtools = 0.9.12-r19483testtools = 0.9.12-r194
84=======
85testtools = 0.9.11
86>>>>>>> MERGE-SOURCE
83transaction = 1.0.087transaction = 1.0.0
84txamqp = 0.488txamqp = 0.4
85Twisted = 11.0.089Twisted = 11.0.0

Subscribers

People subscribed via source and target branches

to status/vote changes: