Merge lp:~stevenk/launchpad/no-oops-request-daily-build into lp:launchpad

Proposed by Steve Kowalik on 2012-08-15
Status: Merged
Approved by: Steve Kowalik on 2012-08-15
Approved revision: no longer in the source branch.
Merged at revision: 15813
Proposed branch: lp:~stevenk/launchpad/no-oops-request-daily-build
Merge into: lp:launchpad
Diff against target: 194 lines (+65/-11)
5 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+10/-2)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+18/-1)
lib/lp/code/interfaces/sourcepackagerecipe.py (+5/-1)
lib/lp/code/model/sourcepackagerecipe.py (+9/-2)
lib/lp/code/model/tests/test_sourcepackagerecipe.py (+23/-5)
To merge this branch: bzr merge lp:~stevenk/launchpad/no-oops-request-daily-build
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code 2012-08-15 Approve on 2012-08-15
Review via email: mp+119654@code.launchpad.net

Commit Message

Skip obsolete series in ISourcePackageRecipe.performDailyBuild(), and warn users about them.

Description of the Change

Currently, if you request a daily build of a recipe that contains an obsolete series (such as Maverick), you get an OOPS since the code raises a BuildNotAllowedForDistro exception, which is not caught.

I have changed ISourcePackageRecipe.performDailyBuild() to filter out distroseries which can't be built, and have added a helper method which the view calls to add a warning to the notification.

Also driveby a little cleanup.

To post a comment you must log in.
Curtis Hovey (sinzui) wrote :

Thank you.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
2--- lib/lp/code/browser/sourcepackagerecipe.py 2012-08-10 04:48:36 +0000
3+++ lib/lp/code/browser/sourcepackagerecipe.py 2012-08-15 01:19:20 +0000
4@@ -11,6 +11,7 @@
5 'SourcePackageRecipeEditView',
6 'SourcePackageRecipeNavigationMenu',
7 'SourcePackageRecipeRequestBuildsView',
8+ 'SourcePackageRecipeRequestDailyBuildView',
9 'SourcePackageRecipeView',
10 ]
11
12@@ -366,7 +367,8 @@
13 return builds
14
15
16-def new_builds_notification_text(builds, already_pending=None):
17+def new_builds_notification_text(builds, already_pending=None,
18+ contains_unbuildable=False):
19 nr_builds = len(builds)
20 if not nr_builds:
21 builds_text = "All requested recipe builds are already queued."
22@@ -376,6 +378,9 @@
23 builds_text = "%d new recipe builds have been queued." % nr_builds
24 if nr_builds > 0 and already_pending:
25 builds_text = "<p>%s</p>%s" % (builds_text, already_pending)
26+ if contains_unbuildable:
27+ builds_text = ("%s<p>The recipe contains an obsolete distroseries, "
28+ "which has been skipped.</p>" % builds_text)
29 return structured(builds_text)
30
31
32@@ -554,9 +559,12 @@
33 "../templates/sourcepackagerecipe-builds.pt")
34 return template(self)
35 else:
36+ contains_unbuildable = recipe.containsUnbuildableSeries(
37+ recipe.daily_build_archive)
38 self.next_url = canonical_url(recipe)
39 self.request.response.addNotification(
40- new_builds_notification_text(builds))
41+ new_builds_notification_text(
42+ builds, contains_unbuildable=contains_unbuildable))
43
44 @property
45 def builds(self):
46
47=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
48--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2012-08-13 19:34:10 +0000
49+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2012-08-15 01:19:20 +0000
50@@ -1,6 +1,5 @@
51 # Copyright 2010-2012 Canonical Ltd. This software is licensed under the
52 # GNU Affero General Public License version 3 (see the file LICENSE).
53-# pylint: disable-msg=F0401,E1002
54
55 """Tests for the source package recipe view classes and templates."""
56
57@@ -1417,6 +1416,24 @@
58 "Secret PPA is disabled.",
59 harness.view.request.notifications[0].message)
60
61+ def test_request_daily_builds_obsolete_series(self):
62+ # Requesting a daily build with an obsolete series gives a warning.
63+ recipe = self.factory.makeSourcePackageRecipe(
64+ owner=self.chef, daily_build_archive=self.ppa,
65+ name=u'julia', is_stale=True, build_daily=True)
66+ warty = self.factory.makeSourcePackageRecipeDistroseries()
67+ hoary = self.factory.makeSourcePackageRecipeDistroseries(name='hoary')
68+ with person_logged_in(self.chef):
69+ recipe.updateSeries((warty, hoary))
70+ removeSecurityProxy(warty).status = SeriesStatus.OBSOLETE
71+ harness = LaunchpadFormHarness(
72+ recipe, SourcePackageRecipeRequestDailyBuildView)
73+ harness.submit('build', {})
74+ self.assertEqual(
75+ '1 new recipe build has been queued.<p>The recipe contains an '
76+ 'obsolete distroseries, which has been skipped.</p>',
77+ harness.view.request.notifications[0].message)
78+
79 def test_request_builds_page(self):
80 """Ensure the +request-builds page is sane."""
81 recipe = self.makeRecipe()
82
83=== modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py'
84--- lib/lp/code/interfaces/sourcepackagerecipe.py 2011-12-24 16:54:44 +0000
85+++ lib/lp/code/interfaces/sourcepackagerecipe.py 2012-08-15 01:19:20 +0000
86@@ -1,4 +1,4 @@
87-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
88+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
89 # GNU Affero General Public License version 3 (see the file LICENSE).
90
91 # pylint: disable-msg=E0211,E0213,F0401
92@@ -161,6 +161,10 @@
93 able to upload to the archive.
94 """
95
96+ def containsUnbuildableSeries(archive):
97+ """Does the recipe contain series that can not be built into.
98+ """
99+
100 @export_write_operation()
101 def performDailyBuild():
102 """Perform a build into the daily build archive."""
103
104=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
105--- lib/lp/code/model/sourcepackagerecipe.py 2011-12-30 06:14:56 +0000
106+++ lib/lp/code/model/sourcepackagerecipe.py 2012-08-15 01:19:20 +0000
107@@ -1,4 +1,4 @@
108-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
109+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
110 # GNU Affero General Public License version 3 (see the file LICENSE).
111
112 # pylint: disable-msg=F0401,W1001
113@@ -281,6 +281,10 @@
114 return SourcePackageRecipeBuild.getRecentBuilds(
115 requester, self, distroseries).count() >= 5
116
117+ def containsUnbuildableSeries(self, archive):
118+ buildable_distros = set(get_buildable_distroseries_set(archive.owner))
119+ return len(set(self.distroseries).difference(buildable_distros)) >= 1
120+
121 def requestBuild(self, archive, requester, distroseries,
122 pocket=PackagePublishingPocket.RELEASE,
123 manual=False):
124@@ -321,7 +325,10 @@
125 """See `ISourcePackageRecipe`."""
126 builds = []
127 self.is_stale = False
128- for distroseries in self.distroseries:
129+ buildable_distros = set(get_buildable_distroseries_set(
130+ self.daily_build_archive.owner))
131+ build_for = set(self.distroseries).intersection(buildable_distros)
132+ for distroseries in build_for:
133 try:
134 build = self.requestBuild(
135 self.daily_build_archive, self.owner,
136
137=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
138--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2012-05-31 03:54:13 +0000
139+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2012-08-15 01:19:20 +0000
140@@ -52,6 +52,7 @@
141 from lp.code.tests.helpers import recipe_parser_newest_version
142 from lp.registry.enums import InformationType
143 from lp.registry.interfaces.pocket import PackagePublishingPocket
144+from lp.registry.interfaces.series import SeriesStatus
145 from lp.services.database.bulk import load_referencing
146 from lp.services.database.constants import UTC_NOW
147 from lp.services.job.interfaces.job import (
148@@ -69,7 +70,6 @@
149 )
150 from lp.testing import (
151 ANONYMOUS,
152- feature_flags,
153 launchpadlib_for,
154 login,
155 login_person,
156@@ -89,10 +89,6 @@
157
158 layer = DatabaseFunctionalLayer
159
160- def setUp(self):
161- super(TestSourcePackageRecipe, self).setUp()
162- self.useContext(feature_flags())
163-
164 def test_implements_interface(self):
165 """SourcePackageRecipe implements ISourcePackageRecipe."""
166 recipe = self.factory.makeSourcePackageRecipe()
167@@ -738,6 +734,28 @@
168 self.assertEqual([], list(recipe.completed_builds))
169 self.assertEqual([], list(recipe.pending_builds))
170
171+ def test_containsUnbuildableSeries(self):
172+ recipe = self.factory.makeSourcePackageRecipe()
173+ self.assertFalse(recipe.containsUnbuildableSeries(
174+ recipe.daily_build_archive))
175+
176+ def test_containsUnbuildableSeries_with_obsolete_series(self):
177+ recipe = self.factory.makeSourcePackageRecipe()
178+ warty = self.factory.makeSourcePackageRecipeDistroseries()
179+ removeSecurityProxy(warty).status = SeriesStatus.OBSOLETE
180+ self.assertTrue(recipe.containsUnbuildableSeries(
181+ recipe.daily_build_archive))
182+
183+ def test_performDailyBuild_filters_obsolete_series(self):
184+ recipe = self.factory.makeSourcePackageRecipe()
185+ warty = self.factory.makeSourcePackageRecipeDistroseries()
186+ hoary = self.factory.makeSourcePackageRecipeDistroseries(name='hoary')
187+ with person_logged_in(recipe.owner):
188+ recipe.updateSeries((warty, hoary))
189+ removeSecurityProxy(warty).status = SeriesStatus.OBSOLETE
190+ builds = recipe.performDailyBuild()
191+ self.assertEqual([build.recipe for build in builds], [recipe])
192+
193
194 class TestRecipeBranchRoundTripping(TestCaseWithFactory):
195