Merge lp:~jelmer/launchpad/bzr-code-imports into lp:launchpad
- bzr-code-imports
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Jelmer Vernooij |
Approved revision: | no longer in the source branch. |
Merged at revision: | 13811 |
Proposed branch: | lp:~jelmer/launchpad/bzr-code-imports |
Merge into: | lp:launchpad |
Prerequisite: | lp:~jelmer/launchpad/code-import-use-safe-open |
Diff against target: |
716 lines (+288/-39) 13 files modified
lib/canonical/config/schema-lazr.conf (+5/-0) lib/lp/code/mail/codeimport.py (+3/-1) 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 (+55/-0) lib/lp/codehosting/codeimport/tests/test_worker.py (+85/-2) lib/lp/codehosting/codeimport/tests/test_workermonitor.py (+22/-0) lib/lp/codehosting/codeimport/worker.py (+36/-6) lib/lp/registry/browser/productseries.py (+9/-17) lib/lp/registry/browser/tests/productseries-setbranch-view.txt (+4/-3) lib/lp/testing/factory.py (+9/-3) scripts/code-import-worker.py (+6/-3) |
To merge this branch: | bzr merge lp:~jelmer/launchpad/bzr-code-imports |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Hudson-Doyle | Approve | ||
Gavin Panella | Pending | ||
Robert Collins | code | Pending | |
Review via email: mp+72794@code.launchpad.net |
This proposal supersedes a proposal from 2011-08-16.
Commit message
[r=mwhudson][no-qa] Add support for importing Bazaar branches using the code import mechanism.
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.
Gavin Panella (allenap) wrote : Posted in a previous version of this proposal | # |
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.
Michael Hudson-Doyle (mwhudson) wrote : Posted in a previous version of this proposal | # |
Some random comments: it would have been nice to see the things you had to do wrt the bzr upgrade
The fact that you put this line in suggests that the tests aren't very well isolated:
Does something need to inherit from bzrlib's TestCase? It's all a complete tangle though.
Is simply prohibiting branch references the right thing to do? The branch puller goes to some lengths to support them safely -- being able to dump all that code would surely be very nice. Relatedly, and on thinking about it I think this is a bit more serious, I think you might need to be careful about stacking -- it's contrived but you might be able to construct a branch that was stacked on some DC-internal branch and have that be imported so you can grab it.
In terms of overall direction, I'm all for removing duplication. I think the UI will require some effort (e.g. do we want to count bzr mirrors as "imported branches" on the code.launchpad.net frontpage?) but engineering wise, this looks fine, modulo the above comments.
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.
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal | # |
On 06/24/2011 05:35 AM, Michael Hudson-Doyle wrote:
> Review: Approve
> Some random comments: it would have been nice to see the things you had to do wrt the bzr upgrade
This branch has two prerequisite branches, but I can only set one in
Launchpad. Hopefully the diff will get smaller when the update to the
newer bzr lands on lp:launchpad...
>
> The fact that you put this line in suggests that the tests aren't very well isolated:
>
> branch.
>
> Does something need to inherit from bzrlib's TestCase? It's all a complete tangle though.
Perhaps; bzr's TestCase does a lot though, and I'm kindof worried that
mixing it in will break other things. It should do more than just this
ad-hoc override though. Perhaps reset the global bzr configuration in setUp?
>
> Is simply prohibiting branch references the right thing to do? The branch puller goes to some lengths to support them safely -- being able to dump all that code would surely be very nice. Relatedly, and on thinking about it I think this is a bit more serious, I think you might need to be careful about stacking -- it's contrived but you might be able to construct a branch that was stacked on some DC-internal branch and have that be imported so you can grab it.
Stacking is a very good point, and one that I had not considered -
thanks. I should probably also have another, closer, look at the branch
puller to see what it does and why.
For branch references, the easiest thing to do for the moment seemed to
be to just refuse to mirror them. If code mirrors support branch
references at the moment, we should keep that support.
>
> In terms of overall direction, I'm all for removing duplication. I think the UI will require some effort (e.g. do we want to count bzr mirrors as "imported branches" on the code.launchpad.net frontpage?) but engineering wise, this looks fine, modulo the above comments.
Thanks for having a look at this. I'm only just getting into this code
and am not very familiar with it yet.
Cheers,
Jelmer
Michael Hudson-Doyle (mwhudson) wrote : Posted in a previous version of this proposal | # |
On Fri, 24 Jun 2011 19:55:45 +0200, Jelmer Vernooij <email address hidden> wrote:
> On 06/24/2011 05:35 AM, Michael Hudson-Doyle wrote:
> > Review: Approve
> > Some random comments: it would have been nice to see the things you had to do wrt the bzr upgrade
> This branch has two prerequisite branches, but I can only set one in
> Launchpad. Hopefully the diff will get smaller when the update to the
> newer bzr lands on lp:launchpad...
Ah! I guess you could have created a branch with both of the
prerequisites merged in, but that's getting pretty tedious...
> >
> > The fact that you put this line in suggests that the tests aren't very well isolated:
> >
> > branch.
> >
> > Does something need to inherit from bzrlib's TestCase? It's all a complete tangle though.
> Perhaps; bzr's TestCase does a lot though, and I'm kindof worried that
> mixing it in will break other things.
I'd be surprised if it broke other stuff on past experience, but you
might be right.
> It should do more than just this ad-hoc override though. Perhaps reset
> the global bzr configuration in setUp?
I guess what you want is a test fixture that just does the environment
isolation in bzrlib you can reuse here... but yes, doing something more
generic than just clearing create_signatures would be better, I think.
> >
> > Is simply prohibiting branch references the right thing to do? The branch puller goes to some lengths to support them safely -- being able to dump all that code would surely be very nice. Relatedly, and on thinking about it I think this is a bit more serious, I think you might need to be careful about stacking -- it's contrived but you might be able to construct a branch that was stacked on some DC-internal branch and have that be imported so you can grab it.
> Stacking is a very good point, and one that I had not considered -
> thanks. I should probably also have another, closer, look at the branch
> puller to see what it does and why.
For reasons I should probably be ashamed of, there seem to be two
implementations of 'safe opening' in Launchpad; one is around
lp.codehosting.
lp.codehosting.
(looking at worker.py makes me realize how much code we can delete once
this branch is done, never mind how much can be deleted when the puller
is gone completely).
> For branch references, the easiest thing to do for the moment seemed to
> be to just refuse to mirror them. If code mirrors support branch
> references at the moment, we should keep that support.
Yeah, they do.
> >
> > In terms of overall direction, I'm all for removing duplication. I think the UI will require some effort (e.g. do we want to count bzr mirrors as "imported branches" on the code.launchpad.net frontpage?) but engineering wise, this looks fine, modulo the above comments.
> Thanks for having a look at this. I'm only just getting into this code
> and am not very familiar with it yet.
You seem to be doing fine :)
Cheers,
mwh
Michael Hudson-Doyle (mwhudson) wrote : Posted in a previous version of this proposal | # |
I don't see anything to do with stacking safety and/or supporting branch references in here yet -- is this really ready for review again?
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal | # |
> I don't see anything to do with stacking safety and/or supporting branch
> references in here yet -- is this really ready for review again?
No, it indeed isn't - I was merely trying to generate a proper diff. Sorry.
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal | # |
Does this permit imports from lp hosted branches? if so, thats likely to permit bypassing of private branch ACLs - can you check that that isn't the please?
Gavin Panella (allenap) : Posted in a previous version of this proposal | # |
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal | # |
On Sun, 2011-08-07 at 22:41 +0000, Robert Collins wrote:
> Review: Needs Information
> Does this permit imports from lp hosted branches? if so, thats likely to permit bypassing of private branch ACLs - can you check that that isn't the please?
It does not allow imports from lp hosted branches, importing of branch
references to launchpad branches or even importing of branches stacked
on Launchpad branches.
It also limits imports to a specific (whitelist) set of schemes, which
excludes bzr+ssh://, sftp:// and file://.
This is the same policy as is currently in place for code mirrors at the
moment.
Cheers,
Jelmer
Gavin Panella (allenap) : Posted in a previous version of this proposal | # |
Michael Hudson-Doyle (mwhudson) wrote : | # |
This (finally!) looks fine to me. Thanks for plugging away. Maybe a positive test for a branch reference being imported would be nice?
BzrServer.
Jelmer Vernooij (jelmer) wrote : | # |
> This (finally!) looks fine to me. Thanks for plugging away. Maybe a positive
> test for a branch reference being imported would be nice?
I'll add one.
> BzrServer.
> deleted?
I'd like to keep it around for the moment. Hopefully we can switch to running actual VCS servers at some point, so we can use the proper database constrains for code import URLs.
Thanks for all your reviews :)
Preview Diff
1 | === modified file 'lib/canonical/config/schema-lazr.conf' |
2 | --- lib/canonical/config/schema-lazr.conf 2011-08-15 08:47:36 +0000 |
3 | +++ lib/canonical/config/schema-lazr.conf 2011-08-27 23:26:24 +0000 |
4 | @@ -473,6 +473,11 @@ |
5 | # datatype: integer |
6 | default_interval_cvs: 43200 |
7 | |
8 | +# The default value of the update interval of a code import from |
9 | +# Bazaar, in seconds. |
10 | +# datatype: integer |
11 | +default_interval_bzr: 21600 |
12 | + |
13 | # Where the tarballs of foreign branches are uploaded for storage. |
14 | # datatype: string |
15 | foreign_tree_store: sftp://hoover@escudero/srv/importd/sources/ |
16 | |
17 | === modified file 'lib/lp/code/mail/codeimport.py' |
18 | --- lib/lp/code/mail/codeimport.py 2011-08-12 14:39:51 +0000 |
19 | +++ lib/lp/code/mail/codeimport.py 2011-08-27 23:26:24 +0000 |
20 | @@ -51,6 +51,7 @@ |
21 | RevisionControlSystems.BZR_SVN: 'subversion', |
22 | RevisionControlSystems.GIT: 'git', |
23 | RevisionControlSystems.HG: 'mercurial', |
24 | + RevisionControlSystems.BZR: 'bazaar', |
25 | } |
26 | body = get_email_template('new-code-import.txt') % { |
27 | 'person': code_import.registrant.displayname, |
28 | @@ -123,7 +124,8 @@ |
29 | elif code_import.rcs_type in (RevisionControlSystems.SVN, |
30 | RevisionControlSystems.BZR_SVN, |
31 | RevisionControlSystems.GIT, |
32 | - RevisionControlSystems.HG): |
33 | + RevisionControlSystems.HG, |
34 | + RevisionControlSystems.BZR): |
35 | if CodeImportEventDataType.OLD_URL in event_data: |
36 | old_url = event_data[CodeImportEventDataType.OLD_URL] |
37 | body.append( |
38 | |
39 | === modified file 'lib/lp/code/model/codeimport.py' |
40 | --- lib/lp/code/model/codeimport.py 2011-04-27 01:42:46 +0000 |
41 | +++ lib/lp/code/model/codeimport.py 2011-08-27 23:26:24 +0000 |
42 | @@ -116,6 +116,8 @@ |
43 | config.codeimport.default_interval_git, |
44 | RevisionControlSystems.HG: |
45 | config.codeimport.default_interval_hg, |
46 | + RevisionControlSystems.BZR: |
47 | + config.codeimport.default_interval_bzr, |
48 | } |
49 | seconds = default_interval_dict[self.rcs_type] |
50 | return timedelta(seconds=seconds) |
51 | @@ -133,7 +135,8 @@ |
52 | RevisionControlSystems.SVN, |
53 | RevisionControlSystems.GIT, |
54 | RevisionControlSystems.BZR_SVN, |
55 | - RevisionControlSystems.HG): |
56 | + RevisionControlSystems.HG, |
57 | + RevisionControlSystems.BZR): |
58 | return self.url |
59 | else: |
60 | raise AssertionError( |
61 | @@ -252,7 +255,8 @@ |
62 | elif rcs_type in (RevisionControlSystems.SVN, |
63 | RevisionControlSystems.BZR_SVN, |
64 | RevisionControlSystems.GIT, |
65 | - RevisionControlSystems.HG): |
66 | + RevisionControlSystems.HG, |
67 | + RevisionControlSystems.BZR): |
68 | assert cvs_root is None and cvs_module is None |
69 | assert url is not None |
70 | else: |
71 | @@ -262,7 +266,8 @@ |
72 | if review_status is None: |
73 | # Auto approve git and hg imports. |
74 | if rcs_type in ( |
75 | - RevisionControlSystems.GIT, RevisionControlSystems.HG): |
76 | + RevisionControlSystems.GIT, RevisionControlSystems.HG, |
77 | + RevisionControlSystems.BZR): |
78 | review_status = CodeImportReviewStatus.REVIEWED |
79 | else: |
80 | review_status = CodeImportReviewStatus.NEW |
81 | |
82 | === modified file 'lib/lp/code/model/codeimportevent.py' |
83 | --- lib/lp/code/model/codeimportevent.py 2010-10-17 22:51:50 +0000 |
84 | +++ lib/lp/code/model/codeimportevent.py 2011-08-27 23:26:24 +0000 |
85 | @@ -269,7 +269,8 @@ |
86 | if code_import.rcs_type in (RevisionControlSystems.SVN, |
87 | RevisionControlSystems.BZR_SVN, |
88 | RevisionControlSystems.GIT, |
89 | - RevisionControlSystems.HG): |
90 | + RevisionControlSystems.HG, |
91 | + RevisionControlSystems.BZR): |
92 | yield 'URL', code_import.url |
93 | elif code_import.rcs_type == RevisionControlSystems.CVS: |
94 | yield 'CVS_ROOT', code_import.cvs_root |
95 | |
96 | === modified file 'lib/lp/code/model/tests/test_codeimport.py' |
97 | --- lib/lp/code/model/tests/test_codeimport.py 2011-08-12 11:37:08 +0000 |
98 | +++ lib/lp/code/model/tests/test_codeimport.py 2011-08-27 23:26:24 +0000 |
99 | @@ -70,6 +70,20 @@ |
100 | # No job is created for the import. |
101 | self.assertIs(None, code_import.import_job) |
102 | |
103 | + def test_new_svn_import_svn_scheme(self): |
104 | + """A subversion import can use the svn:// scheme.""" |
105 | + code_import = CodeImportSet().new( |
106 | + registrant=self.factory.makePerson(), |
107 | + target=IBranchTarget(self.factory.makeProduct()), |
108 | + branch_name='imported', |
109 | + rcs_type=RevisionControlSystems.SVN, |
110 | + url=self.factory.getUniqueURL(scheme="svn")) |
111 | + self.assertEqual( |
112 | + CodeImportReviewStatus.NEW, |
113 | + code_import.review_status) |
114 | + # No job is created for the import. |
115 | + self.assertIs(None, code_import.import_job) |
116 | + |
117 | def test_reviewed_svn_import(self): |
118 | """A specific review status can be set for a new import.""" |
119 | code_import = CodeImportSet().new( |
120 | @@ -116,6 +130,21 @@ |
121 | # A job is created for the import. |
122 | self.assertIsNot(None, code_import.import_job) |
123 | |
124 | + def test_git_import_git_scheme(self): |
125 | + """A git import can have a git:// style URL.""" |
126 | + code_import = CodeImportSet().new( |
127 | + registrant=self.factory.makePerson(), |
128 | + target=IBranchTarget(self.factory.makeProduct()), |
129 | + branch_name='imported', |
130 | + rcs_type=RevisionControlSystems.GIT, |
131 | + url=self.factory.getUniqueURL(scheme="git"), |
132 | + review_status=None) |
133 | + self.assertEqual( |
134 | + CodeImportReviewStatus.REVIEWED, |
135 | + code_import.review_status) |
136 | + # A job is created for the import. |
137 | + self.assertIsNot(None, code_import.import_job) |
138 | + |
139 | def test_git_import_reviewed(self): |
140 | """A new git import is always reviewed by default.""" |
141 | code_import = CodeImportSet().new( |
142 | @@ -146,6 +175,21 @@ |
143 | # A job is created for the import. |
144 | self.assertIsNot(None, code_import.import_job) |
145 | |
146 | + def test_bzr_import_reviewed(self): |
147 | + """A new bzr import is always reviewed by default.""" |
148 | + code_import = CodeImportSet().new( |
149 | + registrant=self.factory.makePerson(), |
150 | + target=IBranchTarget(self.factory.makeProduct()), |
151 | + branch_name='mirrored', |
152 | + rcs_type=RevisionControlSystems.BZR, |
153 | + url=self.factory.getUniqueURL(), |
154 | + review_status=None) |
155 | + self.assertEqual( |
156 | + CodeImportReviewStatus.REVIEWED, |
157 | + code_import.review_status) |
158 | + # A job is created for the import. |
159 | + self.assertIsNot(None, code_import.import_job) |
160 | + |
161 | def test_junk_code_import_rejected(self): |
162 | """You are not allowed to create code imports targetting +junk.""" |
163 | registrant = self.factory.makePerson() |
164 | |
165 | === modified file 'lib/lp/codehosting/codeimport/tests/servers.py' |
166 | --- lib/lp/codehosting/codeimport/tests/servers.py 2011-08-22 11:56:26 +0000 |
167 | +++ lib/lp/codehosting/codeimport/tests/servers.py 2011-08-27 23:26:24 +0000 |
168 | @@ -4,6 +4,7 @@ |
169 | """Server classes that know how to create various kinds of foreign archive.""" |
170 | |
171 | __all__ = [ |
172 | + 'BzrServer', |
173 | 'CVSServer', |
174 | 'GitServer', |
175 | 'MercurialServer', |
176 | @@ -23,7 +24,14 @@ |
177 | import time |
178 | import threading |
179 | |
180 | +from bzrlib.branch import Branch |
181 | +from bzrlib.branchbuilder import BranchBuilder |
182 | +from bzrlib.bzrdir import BzrDir |
183 | from bzrlib.tests.treeshape import build_tree_contents |
184 | +from bzrlib.tests.test_server import ( |
185 | + ReadonlySmartTCPServer_for_testing, |
186 | + TestServer, |
187 | + ) |
188 | from bzrlib.transport import Server |
189 | from bzrlib.urlutils import ( |
190 | escape, |
191 | @@ -348,3 +356,50 @@ |
192 | f.close() |
193 | repo[None].add([filename]) |
194 | repo.commit(text='<The commit message>', user='jane Foo <joe@foo.com>') |
195 | + |
196 | + |
197 | +class BzrServer(Server): |
198 | + |
199 | + def __init__(self, repository_path, use_server=False): |
200 | + super(BzrServer, self).__init__() |
201 | + self.repository_path = repository_path |
202 | + self._use_server = use_server |
203 | + |
204 | + def createRepository(self, path): |
205 | + BzrDir.create_branch_convenience(path) |
206 | + |
207 | + def makeRepo(self, tree_contents): |
208 | + branch = Branch.open(self.repository_path) |
209 | + branch.get_config().set_user_option("create_signatures", "never") |
210 | + builder = BranchBuilder(branch=branch) |
211 | + actions = [('add', ('', 'tree-root', 'directory', None))] |
212 | + actions += [('add', (path, path+'-id', 'file', content)) for (path, |
213 | + content) in tree_contents] |
214 | + builder.build_snapshot(None, None, |
215 | + actions, committer='Joe Foo <joe@foo.com>', |
216 | + message=u'<The commit message>') |
217 | + |
218 | + def get_url(self): |
219 | + if self._use_server: |
220 | + return self._bzrserver.get_url() |
221 | + else: |
222 | + return local_path_to_url(self.repository_path) |
223 | + |
224 | + def start_server(self): |
225 | + super(BzrServer, self).start_server() |
226 | + self.createRepository(self.repository_path) |
227 | + class LocalURLServer(TestServer): |
228 | + def __init__(self, repository_path): |
229 | + self.repository_path = repository_path |
230 | + def start_server(self): pass |
231 | + def get_url(self): |
232 | + return local_path_to_url(self.repository_path) |
233 | + if self._use_server: |
234 | + self._bzrserver = ReadonlySmartTCPServer_for_testing() |
235 | + self._bzrserver.start_server( |
236 | + LocalURLServer(self.repository_path)) |
237 | + |
238 | + def stop_server(self): |
239 | + super(BzrServer, self).stop_server() |
240 | + if self._use_server: |
241 | + self._bzrserver.stop_server() |
242 | |
243 | === modified file 'lib/lp/codehosting/codeimport/tests/test_worker.py' |
244 | --- lib/lp/codehosting/codeimport/tests/test_worker.py 2011-08-24 19:39:15 +0000 |
245 | +++ lib/lp/codehosting/codeimport/tests/test_worker.py 2011-08-27 23:26:24 +0000 |
246 | @@ -16,6 +16,9 @@ |
247 | Branch, |
248 | BranchReferenceFormat, |
249 | ) |
250 | +from bzrlib.branchbuilder import ( |
251 | + BranchBuilder, |
252 | + ) |
253 | from bzrlib.bzrdir import ( |
254 | BzrDir, |
255 | BzrDirFormat, |
256 | @@ -45,6 +48,7 @@ |
257 | from lp.codehosting import load_optional_plugin |
258 | from lp.codehosting.safe_open import ( |
259 | AcceptAnythingPolicy, |
260 | + BlacklistPolicy, |
261 | BadUrl, |
262 | SafeBranchOpener, |
263 | ) |
264 | @@ -53,6 +57,7 @@ |
265 | extract_tarball, |
266 | ) |
267 | from lp.codehosting.codeimport.tests.servers import ( |
268 | + BzrServer, |
269 | CVSServer, |
270 | GitServer, |
271 | MercurialServer, |
272 | @@ -60,6 +65,7 @@ |
273 | ) |
274 | from lp.codehosting.codeimport.worker import ( |
275 | BazaarBranchStore, |
276 | + BzrImportWorker, |
277 | BzrSvnImportWorker, |
278 | CodeImportBranchOpenPolicy, |
279 | CodeImportWorkerExitCode, |
280 | @@ -1012,13 +1018,13 @@ |
281 | t.mkdir('reference') |
282 | a_bzrdir = BzrDir.create(self.get_url('reference')) |
283 | BranchReferenceFormat().initialize(a_bzrdir, target_branch=branch) |
284 | - return a_bzrdir.root_transport.base |
285 | + return a_bzrdir.root_transport.base, branch.base |
286 | |
287 | def test_reject_branch_reference(self): |
288 | # URLs that point to other branch types than that expected by the |
289 | # import should be rejected. |
290 | args = {'rcstype': self.rcstype} |
291 | - reference_url = self.createBranchReference() |
292 | + reference_url, target_url = self.createBranchReference() |
293 | if self.rcstype in ('git', 'bzr-svn', 'hg'): |
294 | args['url'] = reference_url |
295 | else: |
296 | @@ -1195,6 +1201,83 @@ |
297 | opener_policy=opener_policy) |
298 | |
299 | |
300 | +class TestBzrImport(WorkerTest, TestActualImportMixin, |
301 | + PullingImportWorkerTests): |
302 | + |
303 | + rcstype = 'bzr' |
304 | + |
305 | + def setUp(self): |
306 | + super(TestBzrImport, self).setUp() |
307 | + self.setUpImport() |
308 | + |
309 | + def makeImportWorker(self, source_details, opener_policy): |
310 | + """Make a new `ImportWorker`.""" |
311 | + return BzrImportWorker( |
312 | + source_details, self.get_transport('import_data'), |
313 | + self.bazaar_store, logging.getLogger(), opener_policy) |
314 | + |
315 | + def makeForeignCommit(self, source_details): |
316 | + """Change the foreign tree, generating exactly one commit.""" |
317 | + branch = Branch.open(source_details.url) |
318 | + builder = BranchBuilder(branch=branch) |
319 | + builder.build_commit(message=self.factory.getUniqueString(), |
320 | + committer="Joe Random Hacker <joe@example.com>") |
321 | + self.foreign_commit_count += 1 |
322 | + |
323 | + def makeSourceDetails(self, branch_name, files): |
324 | + """Make Bzr `CodeImportSourceDetails` pointing at a real Bzr repo. |
325 | + """ |
326 | + repository_path = self.makeTemporaryDirectory() |
327 | + bzr_server = BzrServer(repository_path) |
328 | + bzr_server.start_server() |
329 | + self.addCleanup(bzr_server.stop_server) |
330 | + |
331 | + bzr_server.makeRepo(files) |
332 | + self.foreign_commit_count = 1 |
333 | + |
334 | + return self.factory.makeCodeImportSourceDetails( |
335 | + rcstype='bzr', url=bzr_server.get_url()) |
336 | + |
337 | + def test_partial(self): |
338 | + self.skip( |
339 | + "Partial fetching is not supported for native bzr branches " |
340 | + "at the moment.") |
341 | + |
342 | + def test_unsupported_feature(self): |
343 | + self.skip("All Bazaar features are supported by Bazaar.") |
344 | + |
345 | + def test_reject_branch_reference(self): |
346 | + # Branch references are allowed in the BzrImporter, but their URL |
347 | + # should be checked. |
348 | + reference_url, target_url = self.createBranchReference() |
349 | + source_details = self.factory.makeCodeImportSourceDetails( |
350 | + url=reference_url, rcstype='bzr') |
351 | + policy = BlacklistPolicy(True, [target_url]) |
352 | + worker = self.makeImportWorker(source_details, opener_policy=policy) |
353 | + self.assertEqual( |
354 | + CodeImportWorkerExitCode.FAILURE_FORBIDDEN, worker.run()) |
355 | + |
356 | + def test_support_branch_reference(self): |
357 | + # Branch references are allowed in the BzrImporter. |
358 | + reference_url, target_url = self.createBranchReference() |
359 | + target_branch = Branch.open(target_url) |
360 | + builder = BranchBuilder(branch=target_branch) |
361 | + builder.build_commit(message=self.factory.getUniqueString(), |
362 | + committer="Some Random Hacker <jane@example.com>") |
363 | + source_details = self.factory.makeCodeImportSourceDetails( |
364 | + url=reference_url, rcstype='bzr') |
365 | + worker = self.makeImportWorker(source_details, |
366 | + opener_policy=AcceptAnythingPolicy()) |
367 | + self.assertEqual( |
368 | + CodeImportWorkerExitCode.SUCCESS, worker.run()) |
369 | + branch = self.getStoredBazaarBranch(worker) |
370 | + self.assertEqual( |
371 | + 1, len(branch.revision_history())) |
372 | + self.assertEqual( |
373 | + "Some Random Hacker <jane@example.com>", |
374 | + branch.repository.get_revision(branch.last_revision()).committer) |
375 | + |
376 | + |
377 | class CodeImportBranchOpenPolicyTests(TestCase): |
378 | |
379 | def setUp(self): |
380 | |
381 | === modified file 'lib/lp/codehosting/codeimport/tests/test_workermonitor.py' |
382 | --- lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2011-08-27 03:35:53 +0000 |
383 | +++ lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2011-08-27 23:26:24 +0000 |
384 | @@ -49,6 +49,7 @@ |
385 | from lp.code.model.codeimportjob import CodeImportJob |
386 | from lp.codehosting import load_optional_plugin |
387 | from lp.codehosting.codeimport.tests.servers import ( |
388 | + BzrServer, |
389 | CVSServer, |
390 | GitServer, |
391 | MercurialServer, |
392 | @@ -67,6 +68,7 @@ |
393 | CodeImportWorkerMonitorProtocol, |
394 | ExitQuietly, |
395 | ) |
396 | +from lp.codehosting.safe_open import AcceptAnythingPolicy |
397 | from lp.services.log.logger import BufferLogger |
398 | from lp.services.twistedsupport import suppress_stderr |
399 | from lp.services.twistedsupport.tests.test_processmonitor import ( |
400 | @@ -704,6 +706,17 @@ |
401 | return self.factory.makeCodeImport( |
402 | hg_repo_url=self.hg_server.get_url()) |
403 | |
404 | + def makeBzrCodeImport(self): |
405 | + """Make a `CodeImport` that points to a real Bazaar branch.""" |
406 | + self.bzr_server = BzrServer(self.repo_path) |
407 | + self.bzr_server.start_server() |
408 | + self.addCleanup(self.bzr_server.stop_server) |
409 | + |
410 | + self.bzr_server.makeRepo([('README', 'contents')]) |
411 | + self.foreign_commit_count = 1 |
412 | + return self.factory.makeCodeImport( |
413 | + bzr_branch_url=self.bzr_server.get_url()) |
414 | + |
415 | def getStartedJobForImport(self, code_import): |
416 | """Get a started `CodeImportJob` for `code_import`. |
417 | |
418 | @@ -806,6 +819,15 @@ |
419 | result = self.performImport(job_id) |
420 | return result.addCallback(self.assertImported, code_import_id) |
421 | |
422 | + def test_import_bzr(self): |
423 | + # Create a Bazaar CodeImport and import it. |
424 | + job = self.getStartedJobForImport(self.makeBzrCodeImport()) |
425 | + code_import_id = job.code_import.id |
426 | + job_id = job.id |
427 | + self.layer.txn.commit() |
428 | + result = self.performImport(job_id) |
429 | + return result.addCallback(self.assertImported, code_import_id) |
430 | + |
431 | def test_import_bzrsvn(self): |
432 | # Create a Subversion-via-bzr-svn CodeImport and import it. |
433 | job = self.getStartedJobForImport(self.makeBzrSvnCodeImport()) |
434 | |
435 | === modified file 'lib/lp/codehosting/codeimport/worker.py' |
436 | --- lib/lp/codehosting/codeimport/worker.py 2011-08-26 10:00:49 +0000 |
437 | +++ lib/lp/codehosting/codeimport/worker.py 2011-08-27 23:26:24 +0000 |
438 | @@ -6,6 +6,7 @@ |
439 | __metaclass__ = type |
440 | __all__ = [ |
441 | 'BazaarBranchStore', |
442 | + 'BzrImportWorker', |
443 | 'BzrSvnImportWorker', |
444 | 'CSCVSImportWorker', |
445 | 'CodeImportBranchOpenPolicy', |
446 | @@ -33,8 +34,8 @@ |
447 | from bzrlib.errors import ( |
448 | ConnectionError, |
449 | InvalidEntryName, |
450 | + NoRepositoryPresent, |
451 | NoSuchFile, |
452 | - NoRepositoryPresent, |
453 | NotBranchError, |
454 | ) |
455 | from bzrlib.transport import get_transport |
456 | @@ -249,9 +250,9 @@ |
457 | |
458 | :ivar branch_id: The id of the branch associated to this code import, used |
459 | for locating the existing import and the foreign tree. |
460 | - :ivar rcstype: 'svn', 'cvs', 'hg', 'git', 'bzr-svn', as appropriate. |
461 | + :ivar rcstype: 'svn', 'cvs', 'hg', 'git', 'bzr-svn', 'bzr' as appropriate. |
462 | :ivar url: The branch URL if rcstype in ['svn', 'bzr-svn', |
463 | - 'git', 'hg'], None otherwise. |
464 | + 'git', 'hg', 'bzr'], None otherwise. |
465 | :ivar cvs_root: The $CVSROOT if rcstype == 'cvs', None otherwise. |
466 | :ivar cvs_module: The CVS module if rcstype == 'cvs', None otherwise. |
467 | """ |
468 | @@ -269,7 +270,7 @@ |
469 | """Convert command line-style arguments to an instance.""" |
470 | branch_id = int(arguments.pop(0)) |
471 | rcstype = arguments.pop(0) |
472 | - if rcstype in ['svn', 'bzr-svn', 'git', 'hg']: |
473 | + if rcstype in ['svn', 'bzr-svn', 'git', 'hg', 'bzr']: |
474 | [url] = arguments |
475 | cvs_root = cvs_module = None |
476 | elif rcstype == 'cvs': |
477 | @@ -296,6 +297,8 @@ |
478 | return cls(branch_id, 'git', str(code_import.url)) |
479 | elif code_import.rcs_type == RevisionControlSystems.HG: |
480 | return cls(branch_id, 'hg', str(code_import.url)) |
481 | + elif code_import.rcs_type == RevisionControlSystems.BZR: |
482 | + return cls(branch_id, 'bzr', str(code_import.url)) |
483 | else: |
484 | raise AssertionError("Unknown rcstype %r." % code_import.rcs_type) |
485 | |
486 | @@ -303,7 +306,7 @@ |
487 | """Return a list of arguments suitable for passing to a child process. |
488 | """ |
489 | result = [str(self.branch_id), self.rcstype] |
490 | - if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg']: |
491 | + if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg', 'bzr']: |
492 | result.append(self.url) |
493 | elif self.rcstype == 'cvs': |
494 | result.append(self.cvs_root) |
495 | @@ -708,7 +711,12 @@ |
496 | remote_branch_tip = remote_branch.last_revision() |
497 | inter_branch = InterBranch.get(remote_branch, bazaar_branch) |
498 | self._logger.info("Importing branch.") |
499 | - inter_branch.fetch(limit=self.getRevisionLimit()) |
500 | + revision_limit = self.getRevisionLimit() |
501 | + if revision_limit is None: |
502 | + # bzr < 2.4 does not support InterBranch.fetch() |
503 | + bazaar_branch.fetch(remote_branch) |
504 | + else: |
505 | + inter_branch.fetch(limit=revision_limit) |
506 | if bazaar_branch.repository.has_revision(remote_branch_tip): |
507 | pull_result = inter_branch.pull(overwrite=True) |
508 | if pull_result.old_revid != pull_result.new_revid: |
509 | @@ -922,3 +930,25 @@ |
510 | """See `PullingImportWorker.probers`.""" |
511 | from bzrlib.plugins.svn import SvnRemoteProber |
512 | return [SvnRemoteProber] |
513 | + |
514 | + |
515 | +class BzrImportWorker(PullingImportWorker): |
516 | + """An import worker for importing Bazaar branches.""" |
517 | + |
518 | + invalid_branch_exceptions = [ |
519 | + NotBranchError, |
520 | + ConnectionError, |
521 | + ] |
522 | + unsupported_feature_exceptions = [] |
523 | + |
524 | + def getRevisionLimit(self): |
525 | + """See `PullingImportWorker.getRevisionLimit`.""" |
526 | + # For now, just grab the whole branch at once. |
527 | + # bzr does support fetch(limit=) but it isn't very efficient at the moment. |
528 | + return None |
529 | + |
530 | + @property |
531 | + def probers(self): |
532 | + """See `PullingImportWorker.probers`.""" |
533 | + from bzrlib.bzrdir import BzrProber, RemoteBzrProber |
534 | + return [BzrProber, RemoteBzrProber] |
535 | |
536 | === modified file 'lib/lp/registry/browser/productseries.py' |
537 | --- lib/lp/registry/browser/productseries.py 2011-05-27 21:12:25 +0000 |
538 | +++ lib/lp/registry/browser/productseries.py 2011-08-27 23:26:24 +0000 |
539 | @@ -827,15 +827,6 @@ |
540 | )) |
541 | |
542 | |
543 | -class RevisionControlSystemsExtended(RevisionControlSystems): |
544 | - """External RCS plus Bazaar.""" |
545 | - BZR = DBItem(99, """ |
546 | - Bazaar |
547 | - |
548 | - External Bazaar branch. |
549 | - """) |
550 | - |
551 | - |
552 | class SetBranchForm(Interface): |
553 | """The fields presented on the form for setting a branch.""" |
554 | |
555 | @@ -844,7 +835,7 @@ |
556 | ['cvs_module']) |
557 | |
558 | rcs_type = Choice(title=_("Type of RCS"), |
559 | - required=False, vocabulary=RevisionControlSystemsExtended, |
560 | + required=False, vocabulary=RevisionControlSystems, |
561 | description=_( |
562 | "The version control system to import from. ")) |
563 | |
564 | @@ -908,7 +899,7 @@ |
565 | @property |
566 | def initial_values(self): |
567 | return dict( |
568 | - rcs_type=RevisionControlSystemsExtended.BZR, |
569 | + rcs_type=RevisionControlSystems.BZR, |
570 | branch_type=LINK_LP_BZR, |
571 | branch_location=self.context.branch) |
572 | |
573 | @@ -989,7 +980,7 @@ |
574 | self.setFieldError( |
575 | 'rcs_type', |
576 | 'You must specify the type of RCS for the remote host.') |
577 | - elif rcs_type == RevisionControlSystemsExtended.CVS: |
578 | + elif rcs_type == RevisionControlSystems.CVS: |
579 | if 'cvs_module' not in data: |
580 | self.setFieldError( |
581 | 'cvs_module', |
582 | @@ -1022,8 +1013,9 @@ |
583 | # Extend the allowed schemes for the repository URL based on |
584 | # rcs_type. |
585 | extra_schemes = { |
586 | - RevisionControlSystemsExtended.BZR_SVN: ['svn'], |
587 | - RevisionControlSystemsExtended.GIT: ['git'], |
588 | + RevisionControlSystems.BZR_SVN: ['svn'], |
589 | + RevisionControlSystems.GIT: ['git'], |
590 | + RevisionControlSystems.BZR: ['bzr'], |
591 | } |
592 | schemes.update(extra_schemes.get(rcs_type, [])) |
593 | return schemes |
594 | @@ -1050,7 +1042,7 @@ |
595 | # The branch location is not required for validation. |
596 | self._setRequired(['branch_location'], False) |
597 | # The cvs_module is required if it is a CVS import. |
598 | - if rcs_type == RevisionControlSystemsExtended.CVS: |
599 | + if rcs_type == RevisionControlSystems.CVS: |
600 | self._setRequired(['cvs_module'], True) |
601 | else: |
602 | raise AssertionError("Unknown branch type %s" % branch_type) |
603 | @@ -1110,7 +1102,7 @@ |
604 | # Either create an externally hosted bzr branch |
605 | # (a.k.a. 'mirrored') or create a new code import. |
606 | rcs_type = data.get('rcs_type') |
607 | - if rcs_type == RevisionControlSystemsExtended.BZR: |
608 | + if rcs_type == RevisionControlSystems.BZR: |
609 | branch = self._createBzrBranch( |
610 | BranchType.MIRRORED, branch_name, branch_owner, |
611 | data['repo_url']) |
612 | @@ -1123,7 +1115,7 @@ |
613 | 'the series.') |
614 | else: |
615 | # We need to create an import request. |
616 | - if rcs_type == RevisionControlSystemsExtended.CVS: |
617 | + if rcs_type == RevisionControlSystems.CVS: |
618 | cvs_root = data.get('repo_url') |
619 | cvs_module = data.get('cvs_module') |
620 | url = None |
621 | |
622 | === modified file 'lib/lp/registry/browser/tests/productseries-setbranch-view.txt' |
623 | --- lib/lp/registry/browser/tests/productseries-setbranch-view.txt 2010-04-30 05:21:19 +0000 |
624 | +++ lib/lp/registry/browser/tests/productseries-setbranch-view.txt 2011-08-27 23:26:24 +0000 |
625 | @@ -178,7 +178,7 @@ |
626 | ... 'field.rcs_type': 'BZR', |
627 | ... 'field.branch_name': 'blazer-branch', |
628 | ... 'field.branch_owner': product.owner.name, |
629 | - ... 'field.repo_url': 'bzr://bzr.com/foo', |
630 | + ... 'field.repo_url': 'bzr+ssh://bzr.com/foo', |
631 | ... 'field.actions.update': 'Update', |
632 | ... } |
633 | >>> view = create_initialized_view(series, name='+setbranch', principal=product.owner, form=form) |
634 | @@ -186,8 +186,9 @@ |
635 | ... print notification.message |
636 | >>> for error in view.errors: |
637 | ... print error |
638 | - ('repo_url'...The URI scheme "bzr" is not allowed. Only URIs with the following schemes may be |
639 | - used: http, https')) |
640 | + ('repo_url'...The URI scheme "bzr+ssh" is not allowed. Only URIs with the following schemes may be |
641 | + used: bzr, http, https')) |
642 | + |
643 | |
644 | A correct URL is accepted. |
645 | |
646 | |
647 | === modified file 'lib/lp/testing/factory.py' |
648 | --- lib/lp/testing/factory.py 2011-08-26 04:15:38 +0000 |
649 | +++ lib/lp/testing/factory.py 2011-08-27 23:26:24 +0000 |
650 | @@ -474,7 +474,7 @@ |
651 | branch_id = self.getUniqueInteger() |
652 | if rcstype is None: |
653 | rcstype = 'svn' |
654 | - if rcstype in ['svn', 'bzr-svn', 'hg']: |
655 | + if rcstype in ['svn', 'bzr-svn', 'hg', 'bzr']: |
656 | assert cvs_root is cvs_module is None |
657 | if url is None: |
658 | url = self.getUniqueURL() |
659 | @@ -2123,7 +2123,8 @@ |
660 | |
661 | def makeCodeImport(self, svn_branch_url=None, cvs_root=None, |
662 | cvs_module=None, target=None, branch_name=None, |
663 | - git_repo_url=None, hg_repo_url=None, registrant=None, |
664 | + git_repo_url=None, hg_repo_url=None, |
665 | + bzr_branch_url=None, registrant=None, |
666 | rcs_type=None, review_status=None): |
667 | """Create and return a new, arbitrary code import. |
668 | |
669 | @@ -2132,7 +2133,7 @@ |
670 | unique URL. |
671 | """ |
672 | if (svn_branch_url is cvs_root is cvs_module is git_repo_url is |
673 | - hg_repo_url is None): |
674 | + hg_repo_url is bzr_branch_url is None): |
675 | svn_branch_url = self.getUniqueURL() |
676 | |
677 | if target is None: |
678 | @@ -2163,6 +2164,11 @@ |
679 | registrant, target, branch_name, |
680 | rcs_type=RevisionControlSystems.HG, |
681 | url=hg_repo_url) |
682 | + elif bzr_branch_url is not None: |
683 | + code_import = code_import_set.new( |
684 | + registrant, target, branch_name, |
685 | + rcs_type=RevisionControlSystems.BZR, |
686 | + url=bzr_branch_url) |
687 | else: |
688 | assert rcs_type in (None, RevisionControlSystems.CVS) |
689 | code_import = code_import_set.new( |
690 | |
691 | === modified file 'scripts/code-import-worker.py' |
692 | --- scripts/code-import-worker.py 2011-08-24 21:36:52 +0000 |
693 | +++ scripts/code-import-worker.py 2011-08-27 23:26:24 +0000 |
694 | @@ -26,9 +26,9 @@ |
695 | from canonical.config import config |
696 | from lp.codehosting import load_optional_plugin |
697 | from lp.codehosting.codeimport.worker import ( |
698 | - BzrSvnImportWorker, CSCVSImportWorker, CodeImportBranchOpenPolicy, |
699 | - CodeImportSourceDetails, GitImportWorker, HgImportWorker, |
700 | - get_default_bazaar_branch_store) |
701 | + BzrImportWorker, BzrSvnImportWorker, CSCVSImportWorker, |
702 | + CodeImportBranchOpenPolicy, CodeImportSourceDetails, GitImportWorker, |
703 | + HgImportWorker, get_default_bazaar_branch_store) |
704 | from lp.codehosting.safe_open import AcceptAnythingPolicy |
705 | from canonical.launchpad import scripts |
706 | |
707 | @@ -77,6 +77,9 @@ |
708 | elif source_details.rcstype == 'hg': |
709 | load_optional_plugin('hg') |
710 | import_worker_cls = HgImportWorker |
711 | + elif source_details.rcstype == 'bzr': |
712 | + load_optional_plugin('loom') |
713 | + import_worker_cls = BzrImportWorker |
714 | elif source_details.rcstype in ['cvs', 'svn']: |
715 | import_worker_cls = CSCVSImportWorker |
716 | else: |
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.")