Merge lp:~cjohnston/launchpad/ppa-rtm-updates into lp:launchpad

Proposed by Chris Johnston
Status: Merged
Merged at revision: 17185
Proposed branch: lp:~cjohnston/launchpad/ppa-rtm-updates
Merge into: lp:launchpad
Diff against target: 533 lines (+134/-61)
9 files modified
lib/lp/code/javascript/tests/test_requestbuild_overlay.html (+2/-2)
lib/lp/soyuz/browser/archive.py (+2/-3)
lib/lp/soyuz/browser/tests/archive-views.txt (+8/-8)
lib/lp/soyuz/doc/vocabularies.txt (+13/-13)
lib/lp/soyuz/model/archive.py (+7/-1)
lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt (+50/-13)
lib/lp/soyuz/tests/test_archive.py (+25/-1)
lib/lp/soyuz/tests/test_vocabularies.py (+1/-1)
lib/lp/soyuz/vocabularies.py (+26/-19)
To merge this branch: bzr merge lp:~cjohnston/launchpad/ppa-rtm-updates
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+230142@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/javascript/tests/test_requestbuild_overlay.html'
2--- lib/lp/code/javascript/tests/test_requestbuild_overlay.html 2012-10-26 09:54:28 +0000
3+++ lib/lp/code/javascript/tests/test_requestbuild_overlay.html 2014-08-08 17:27:51 +0000
4@@ -1,6 +1,6 @@
5 <!DOCTYPE html>
6 <!--
7-Copyright 2012 Canonical Ltd. This software is licensed under the
8+Copyright 2012-2014 Canonical Ltd. This software is licensed under the
9 GNU Affero General Public License version 3 (see the file LICENSE).
10 -->
11
12@@ -73,7 +73,7 @@
13 <script type="text/x-template" id="requestbuilds-form-template">
14 <div class="form" id="launchpad-form-widgets">
15 <select id="field.archive" name="field.archive" size="1" >
16- <option selected="selected" value="mark/ppa">
17+ <option selected="selected" value="~mark/ubuntu/ppa">
18 PPA for Mark Shuttleworth</option>
19 </select>
20 <input name="field.archive-empty-marker" type="hidden" value="1" />
21
22=== modified file 'lib/lp/soyuz/browser/archive.py'
23--- lib/lp/soyuz/browser/archive.py 2014-08-01 08:47:00 +0000
24+++ lib/lp/soyuz/browser/archive.py 2014-08-08 17:27:51 +0000
25@@ -1391,6 +1391,7 @@
26 for archive in archives:
27 label = '%s [%s]' % (archive.displayname, archive.reference)
28 terms.append(SimpleTerm(archive, archive.reference, label))
29+ terms.sort(key=lambda x: x.value.reference)
30 return SimpleVocabulary(terms)
31
32
33@@ -1627,10 +1628,8 @@
34 canonical_url(dependency), archive_dependency.title)
35 else:
36 dependency_label = archive_dependency.title
37- dependency_token = '%s/%s' % (
38- dependency.owner.name, dependency.name)
39 term = SimpleTerm(
40- dependency, dependency_token, dependency_label)
41+ dependency, dependency.reference, dependency_label)
42 terms.append(term)
43 return form.Fields(
44 List(__name__='selected_dependencies',
45
46=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
47--- lib/lp/soyuz/browser/tests/archive-views.txt 2014-07-24 09:37:03 +0000
48+++ lib/lp/soyuz/browser/tests/archive-views.txt 2014-08-08 17:27:51 +0000
49@@ -716,7 +716,7 @@
50
51 Let's emulate a dependency addition. Note that the form contains, a
52 empty 'selected_dependencies' (as it was rendered in the empty
53-request) and 'dependency_candidate' contains a valid PPA owner name.
54+request) and 'dependency_candidate' contains a valid PPA name.
55 Validation checks are documented in
56 pagetests/ppa/xx-edit-dependencies.txt.
57
58@@ -724,7 +724,7 @@
59 ... cprov.archive, name="+edit-dependencies",
60 ... form={
61 ... 'field.selected_dependencies': [],
62- ... 'field.dependency_candidate': 'mark/ppa',
63+ ... 'field.dependency_candidate': '~mark/ubuntu/ppa',
64 ... 'field.primary_dependencies': 'UPDATES',
65 ... 'field.primary_components': 'ALL_COMPONENTS',
66 ... 'field.actions.save': 'Save',
67@@ -762,7 +762,7 @@
68 PPA for Mark Shuttleworth
69
70 >>> print dependency.token
71- mark/ppa
72+ ~mark/ubuntu/ppa
73
74 >>> print dependency.title.escapedtext
75 <a href="http://launchpad.dev/~mark/+archive/ubuntu/ppa">PPA for Mark
76@@ -794,7 +794,7 @@
77 PPA for Mark Shuttleworth
78
79 >>> print dependency.token
80- mark/ppa
81+ ~mark/ubuntu/ppa
82
83 >>> print dependency.title
84 PPA for Mark Shuttleworth
85@@ -805,7 +805,7 @@
86 >>> view = create_initialized_view(
87 ... cprov.archive, name="+edit-dependencies",
88 ... form={
89- ... 'field.selected_dependencies': ['mark/ppa'],
90+ ... 'field.selected_dependencies': ['~mark/ubuntu/ppa'],
91 ... 'field.dependency_candidate': '',
92 ... 'field.primary_dependencies': 'UPDATES',
93 ... 'field.primary_components': 'ALL_COMPONENTS',
94@@ -849,7 +849,7 @@
95 >>> view.widgets.get('primary_dependencies')._getCurrentValue()
96 <DBItem PackagePublishingPocket.UPDATES, (20) Updates>
97
98-A similar widget is used for the primary archive component overrides ,
99+A similar widget is used for the primary archive component overrides,
100 which contains two pre-defined options. By default all PPAs use all
101 ubuntu components available to satisfy build dependencies, i.e. the
102 'multiverse' component.
103@@ -965,7 +965,7 @@
104 <DBItem PackagePublishingPocket.UPDATES, (20) Updates>
105
106 Dependencies on private PPAs can be only set if the user performing
107-the action also have permission to view the private PPA and if the
108+the action also has permission to view the private PPA and if the
109 context PPA is also private.
110
111 The latter guarantee that the P3A buildd_secret won't get exposed in
112@@ -988,7 +988,7 @@
113
114 >>> add_private_form = {
115 ... 'field.selected_dependencies': [],
116- ... 'field.dependency_candidate': 'pirulito-team/ppa',
117+ ... 'field.dependency_candidate': '~pirulito-team/ubuntu/ppa',
118 ... 'field.primary_dependencies': 'UPDATES',
119 ... 'field.primary_components': 'FOLLOW_PRIMARY',
120 ... 'field.actions.save': 'Save',
121
122=== modified file 'lib/lp/soyuz/doc/vocabularies.txt'
123--- lib/lp/soyuz/doc/vocabularies.txt 2013-07-12 06:14:22 +0000
124+++ lib/lp/soyuz/doc/vocabularies.txt 2014-08-08 17:27:51 +0000
125@@ -189,13 +189,13 @@
126 * value: the IArchive object;
127 * title: the first line of the PPA description text.
128
129- >>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
130+ >>> cprov_term = vocabulary.getTermByToken('~cprov/ubuntu/ppa')
131
132 >>> print cprov_term.token
133- cprov/ppa
134+ ~cprov/ubuntu/ppa
135
136 >>> print cprov_term.value
137- <Archive ...>
138+ <... lp.soyuz.model.archive.Archive instance ...>
139
140 >>> print cprov_term.title
141 packages to help my friends.
142@@ -216,15 +216,15 @@
143
144 >>> cprov_search = vocabulary.search(u'cprov')
145 >>> print_search_results(cprov_search)
146- cprov/ppa: packages to help my friends.
147+ ~cprov/ubuntu/ppa: packages to help my friends.
148
149 >>> celso_search = vocabulary.search(u'celso')
150 >>> print_search_results(celso_search)
151- cprov/ppa: packages to help my friends.
152+ ~cprov/ubuntu/ppa: packages to help my friends.
153
154 >>> friends_search = vocabulary.search(u'friends')
155 >>> print_search_results(friends_search)
156- cprov/ppa: packages to help my friends.
157+ ~cprov/ubuntu/ppa: packages to help my friends.
158
159 We will create an additional PPA for Celso named 'testing'
160
161@@ -242,15 +242,15 @@
162
163 >>> cprov_search = vocabulary.search(u'cprov')
164 >>> print_search_results(cprov_search)
165- cprov/ppa: packages to help my friends.
166- cprov/testing: testing packages.
167+ ~cprov/ubuntu/ppa: packages to help my friends.
168+ ~cprov/ubuntu/testing: testing packages.
169
170 The vocabulary search also supports specific named PPA lookups
171 follwing the same combined syntax used to build unique tokens.
172
173- >>> named_search = vocabulary.search(u'cprov/testing')
174+ >>> named_search = vocabulary.search(u'~cprov/ubuntu/testing')
175 >>> print_search_results(named_search)
176- cprov/testing: testing packages.
177+ ~cprov/ubuntu/testing: testing packages.
178
179 As mentioned the PPA vocabulary term title only contains the first
180 line of the PPA description.
181@@ -258,14 +258,14 @@
182 >>> cprov.archive.description = "Single line."
183 >>> flush_database_updates()
184
185- >>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
186+ >>> cprov_term = vocabulary.getTermByToken('~cprov/ubuntu/ppa')
187 >>> print cprov_term.title
188 Single line.
189
190 >>> cprov.archive.description = "First line\nSecond line."
191 >>> flush_database_updates()
192
193- >>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
194+ >>> cprov_term = vocabulary.getTermByToken('~cprov/ubuntu/ppa')
195 >>> print cprov_term.title
196 First line
197
198@@ -274,7 +274,7 @@
199 >>> cprov.archive.description = None
200 >>> flush_database_updates()
201
202- >>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
203+ >>> cprov_term = vocabulary.getTermByToken('~cprov/ubuntu/ppa')
204 >>> print cprov_term.title
205 No description available
206
207
208=== modified file 'lib/lp/soyuz/model/archive.py'
209--- lib/lp/soyuz/model/archive.py 2014-08-07 07:53:27 +0000
210+++ lib/lp/soyuz/model/archive.py 2014-08-08 17:27:51 +0000
211@@ -1,4 +1,4 @@
212-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
213+# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
214 # GNU Affero General Public License version 3 (see the file LICENSE).
215
216 """Database class for table Archive."""
217@@ -1077,7 +1077,13 @@
218 raise ArchiveDependencyError(
219 "Non-primary archives only support the '%s' component." %
220 dependency.default_component.name)
221+ if dependency.distribution != self.distribution:
222+ raise ArchiveDependencyError(
223+ "This dependency uses a different archive.")
224
225+ if not dependency.enabled:
226+ raise ArchiveDependencyError(
227+ "This dependency is not active.")
228 return ArchiveDependency(
229 archive=self, dependency=dependency, pocket=pocket,
230 component=component)
231
232=== modified file 'lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt'
233--- lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt 2014-07-24 09:37:03 +0000
234+++ lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt 2014-08-08 17:27:51 +0000
235@@ -112,7 +112,7 @@
236 is rendered on top of the page and the list of dependencies available
237 for removal is updated.
238
239- >>> admin_browser.getControl("Add PPA dependency").value = 'mark/ppa'
240+ >>> admin_browser.getControl("Add PPA dependency").value = '~mark/ubuntu/ppa'
241 >>> admin_browser.getControl("Save").click()
242 >>> print_feedback_messages(admin_browser.contents)
243 Dependency added: PPA for Mark Shuttleworth
244@@ -123,7 +123,7 @@
245
246 Trying to add a dependency that is already recorded results in a error.
247
248- >>> admin_browser.getControl("Add PPA dependency").value = 'mark/ppa'
249+ >>> admin_browser.getControl("Add PPA dependency").value = '~mark/ubuntu/ppa'
250 >>> admin_browser.getControl("Save").click()
251 >>> print_feedback_messages(admin_browser.contents)
252 There is 1 error.
253@@ -132,7 +132,7 @@
254 Trying to add a dependency for the context PPA itself also results in
255 a error.
256
257- >>> admin_browser.getControl("Add PPA dependency").value = 'cprov/ppa'
258+ >>> admin_browser.getControl("Add PPA dependency").value = '~cprov/ubuntu/ppa'
259 >>> admin_browser.getControl("Save").click()
260 >>> print_feedback_messages(admin_browser.contents)
261 There is 1 error.
262@@ -140,7 +140,7 @@
263
264 If it's a new dependency everything is fine.
265
266- >>> admin_browser.getControl("Add PPA dependency").value = 'no-priv/ppa'
267+ >>> admin_browser.getControl("Add PPA dependency").value = '~no-priv/ubuntu/ppa'
268 >>> admin_browser.getControl("Save").click()
269 >>> print_feedback_messages(admin_browser.contents)
270 Dependency added: PPA for No Privileges Person
271@@ -152,7 +152,7 @@
272 PPA for Mark Shuttleworth
273 PPA for No Privileges Person
274
275-The dependencies are presented in a separated section (bellow the
276+The dependencies are presented in a separated section (below the
277 sources.list widget).
278
279 >>> user_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
280@@ -196,7 +196,7 @@
281 http://launchpad.dev/~no-priv/+archive/ubuntu/ppa
282
283 When accessed by their owners, a PPA depending on disabled archives
284-will additionally show an warning for uploaders . This way PPA
285+will additionally show an warning for uploaders. This way a PPA
286 maintainer can react to this problem.
287
288 >>> cprov_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
289@@ -234,7 +234,7 @@
290
291 >>> admin_browser.getControl(
292 ... name="field.selected_dependencies").value = [
293- ... 'mark/ppa', 'no-priv/ppa']
294+ ... '~mark/ubuntu/ppa', '~no-priv/ubuntu/ppa']
295 >>> admin_browser.getControl("Save").click()
296 >>> print_feedback_messages(admin_browser.contents)
297 Dependencies removed:
298@@ -258,6 +258,43 @@
299 ... user_browser.contents, 'archive-dependencies')
300 None
301
302+We should also make sure that a user is unable to add a disabled PPA as a
303+dependency.
304+
305+ # Disable Mark's PPA.
306+ >>> login('foo.bar@canonical.com')
307+ >>> from zope.component import getUtility
308+ >>> from lp.registry.interfaces.person import IPersonSet
309+ >>> mark = getUtility(IPersonSet).getByName('mark')
310+ >>> mark.archive.disable()
311+ >>> logout()
312+
313+ # Attempt to add Mark's PPA
314+ >>> admin_browser.getControl("Add PPA dependency").value = '~mark/ubuntu/ppa'
315+ >>> admin_browser.getControl("Save").click()
316+ >>> print_feedback_messages(admin_browser.contents)
317+ There is 1 error.
318+ Invalid value
319+
320+ # When the page is reloaded, there shouldn't be any dependencies.
321+ >>> admin_browser.reload()
322+ >>> print_ppa_dependencies(admin_browser.contents)
323+ No dependencies recorded for this PPA yet.
324+
325+Re-enable Mark's PPA for subsequent tests.
326+
327+ >>> login('foo.bar@canonical.com')
328+ >>> mark.archive.enable()
329+ >>> logout()
330+
331+Clear the page.
332+
333+ >>> admin_browser.getControl("Add PPA dependency").value = ''
334+ >>> admin_browser.getControl("Save").click()
335+ >>> admin_browser.reload()
336+ >>> print_ppa_dependencies(admin_browser.contents)
337+ No dependencies recorded for this PPA yet.
338+
339 == Primary dependencies ==
340
341 A user can modify how a PPA depends on its corresponding
342@@ -418,9 +455,9 @@
343
344 The form can perform multiple actions in a single submit.
345
346-First we will create a PPA dependency for No privileged' PPA.
347+First we will create a PPA dependency for 'No privileged' PPA.
348
349- >>> admin_browser.getControl("Add PPA dependency").value = 'no-priv/ppa'
350+ >>> admin_browser.getControl("Add PPA dependency").value = '~no-priv/ubuntu/ppa'
351 >>> admin_browser.getControl("Save").click()
352 >>> print_feedback_messages(admin_browser.contents)
353 Dependency added: PPA for No Privileges Person
354@@ -450,12 +487,12 @@
355 RELEASE.
356
357 >>> admin_browser.getControl(
358- ... name="field.selected_dependencies").value = ['no-priv/ppa']
359+ ... name="field.selected_dependencies").value = ['~no-priv/ubuntu/ppa']
360
361 >>> admin_browser.getControl(
362 ... "Use all Ubuntu components available.").selected = True
363
364- >>> admin_browser.getControl("Add PPA dependency").value = 'mark/ppa'
365+ >>> admin_browser.getControl("Add PPA dependency").value = '~mark/ubuntu/ppa'
366
367 >>> admin_browser.getControl(
368 ... "Basic (only released packages).").selected = True
369@@ -513,9 +550,9 @@
370
371 >>> admin_browser.getLink('Edit PPA dependencies').click()
372
373- >>> admin_browser.getControl("Add PPA dependency").value = 'no-priv/ppa'
374+ >>> admin_browser.getControl("Add PPA dependency").value = '~no-priv/ubuntu/ppa'
375 >>> admin_browser.getControl(
376- ... name="field.selected_dependencies").value = ['mark/ppa']
377+ ... name="field.selected_dependencies").value = ['~mark/ubuntu/ppa']
378 >>> admin_browser.getControl(
379 ... "Default (security dependencies and recommended updates)."
380 ... ).selected = True
381
382=== modified file 'lib/lp/soyuz/tests/test_archive.py'
383--- lib/lp/soyuz/tests/test_archive.py 2014-08-07 07:53:27 +0000
384+++ lib/lp/soyuz/tests/test_archive.py 2014-08-08 17:27:51 +0000
385@@ -1,4 +1,4 @@
386-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
387+# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
388 # GNU Affero General Public License version 3 (see the file LICENSE).
389
390 """Test Archive features."""
391@@ -1415,6 +1415,30 @@
392 PackagePublishingPocket.RELEASE)
393 self.assertContentEqual(archive.dependencies, [archive_dependency])
394
395+ def test_dependency_has_different_distribution(self):
396+ # A public archive may not depend on a private archive.
397+ archive = self.factory.makeArchive()
398+ distro = self.factory.makeDistribution()
399+ dependency = self.factory.makeArchive(
400+ distribution=distro, owner=archive.owner)
401+ with person_logged_in(archive.owner):
402+ with ExpectedException(
403+ ArchiveDependencyError,
404+ "This dependency uses a different archive."):
405+ archive.addArchiveDependency(
406+ dependency, PackagePublishingPocket.RELEASE)
407+
408+ def test_dependency_is_disabled(self):
409+ # A public archive may not depend on a private archive.
410+ archive = self.factory.makeArchive()
411+ dependency = self.factory.makeArchive(
412+ owner=archive.owner, enabled=False)
413+ with person_logged_in(archive.owner):
414+ with ExpectedException(
415+ ArchiveDependencyError,
416+ "This dependency is not active."):
417+ archive.addArchiveDependency(
418+ dependency, PackagePublishingPocket.RELEASE)
419
420 class TestArchiveDependencies(TestCaseWithFactory):
421
422
423=== modified file 'lib/lp/soyuz/tests/test_vocabularies.py'
424--- lib/lp/soyuz/tests/test_vocabularies.py 2014-06-11 08:29:40 +0000
425+++ lib/lp/soyuz/tests/test_vocabularies.py 2014-08-08 17:27:51 +0000
426@@ -22,5 +22,5 @@
427 term = vocab.toTerm(archive)
428 self.assertThat(term, MatchesStructure.byEquality(
429 value=archive,
430- token='%s/%s' % (archive.owner.name, archive.name),
431+ token=archive.reference,
432 title='No description available'))
433
434=== modified file 'lib/lp/soyuz/vocabularies.py'
435--- lib/lp/soyuz/vocabularies.py 2013-09-10 06:28:26 +0000
436+++ lib/lp/soyuz/vocabularies.py 2014-08-08 17:27:51 +0000
437@@ -1,4 +1,4 @@
438-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the GNU
439+# Copyright 2009-2014 Canonical Ltd. This software is licensed under the GNU
440 # Affero General Public License version 3 (see the file LICENSE).
441
442 """Soyuz vocabularies."""
443@@ -20,6 +20,7 @@
444 from zope.component import getUtility
445 from zope.interface import implements
446 from zope.schema.vocabulary import SimpleTerm
447+from zope.security.interfaces import Unauthorized
448
449 from lp.registry.model.distroseries import DistroSeries
450 from lp.registry.model.person import Person
451@@ -32,6 +33,7 @@
452 SQLObjectVocabularyBase,
453 )
454 from lp.soyuz.enums import ArchivePurpose
455+from lp.soyuz.interfaces.archive import IArchiveSet
456 from lp.soyuz.model.archive import Archive
457 from lp.soyuz.model.component import Component
458 from lp.soyuz.model.distroarchseries import DistroArchSeries
459@@ -88,6 +90,7 @@
460 _orderBy = ['Person.name, Archive.name']
461 _clauseTables = ['Person']
462 _filter = And(
463+ Archive._enabled == True,
464 Person.q.id == Archive.q.ownerID,
465 Archive.q.purpose == ArchivePurpose.PPA)
466 displayname = 'Select a PPA'
467@@ -95,35 +98,33 @@
468
469 def toTerm(self, archive):
470 """See `IVocabulary`."""
471- description = archive.description
472- if description:
473- summary = description.splitlines()[0]
474- else:
475- summary = "No description available"
476+ try:
477+ description = archive.description
478+ if description:
479+ summary = description.splitlines()[0]
480+ else:
481+ summary = "No description available"
482+ except Unauthorized:
483+ summary = None
484
485- token = '%s/%s' % (archive.owner.name, archive.name)
486+ token = archive.reference
487
488 return SimpleTerm(archive, token, summary)
489
490 def getTermByToken(self, token):
491 """See `IVocabularyTokenized`."""
492 try:
493- owner_name, archive_name = token.split('/')
494+ owner_name, distro_name, archive_name = token.split('/')
495 except ValueError:
496 raise LookupError(token)
497
498- clause = And(
499- self._filter,
500- Person.name == owner_name,
501- Archive.name == archive_name)
502-
503- obj = self._table.selectOne(
504- clause, clauseTables=self._clauseTables)
505-
506+ obj = getUtility(IArchiveSet).getByReference(token)
507 if obj is None:
508- raise LookupError(token)
509- else:
510+ return LookupError(token)
511+ elif obj.enabled:
512 return self.toTerm(obj)
513+ else:
514+ raise LookupError(token)
515
516 def search(self, query, vocab_filter=None):
517 """Return a resultset of archives.
518@@ -135,8 +136,14 @@
519
520 query = query.lower()
521
522+ if query.startswith('~'):
523+ query = query.strip('~')
524 try:
525- owner_name, archive_name = query.split('/')
526+ query_split = query.split('/')
527+ if len(query_split) == 3:
528+ owner_name, distro_name, archive_name = query_split
529+ else:
530+ owner_name, archive_name = query_split
531 except ValueError:
532 clause = And(
533 self._filter,