Merge lp:~rockstar/launchpad/unsupported-series-recipe-api into lp:launchpad
- unsupported-series-recipe-api
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Robert Collins |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11187 |
Proposed branch: | lp:~rockstar/launchpad/unsupported-series-recipe-api |
Merge into: | lp:launchpad |
Prerequisite: | lp:~rockstar/launchpad/create-recipe-error-messages |
Diff against target: |
518 lines (+120/-112) 10 files modified
database/schema/security.cfg (+2/-0) lib/lp/code/browser/configure.zcml (+2/-2) lib/lp/code/browser/sourcepackagerecipe.py (+4/-36) lib/lp/code/errors.py (+12/-0) lib/lp/code/model/sourcepackagerecipe.py (+22/-2) lib/lp/code/model/tests/test_sourcepackagerecipe.py (+22/-21) lib/lp/code/model/tests/test_sourcepackagerecipe.py.moved (+0/-40) lib/lp/code/model/tests/test_sourcepackagerecipebuild.py (+6/-6) lib/lp/code/vocabularies/sourcepackagerecipe.py (+38/-0) lib/lp/testing/factory.py (+12/-5) |
To merge this branch: | bzr merge lp:~rockstar/launchpad/unsupported-series-recipe-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins (community) | Approve | ||
Review via email: mp+30397@code.launchpad.net |
Commit message
Description of the change
This branch fixes bug #607125 - Yesterday, we had a user request builds for distroserieses that we don't have chroots for anymore using the API. At least, we SUSPECT that it was through the API, since the UI prevents this.
Going on the theory that it was through the API and that it's not possible to do through the UI, I took the code that helps the UI show the correct set of distroserieses and moved it to lp.code.
After everything was moved around, I made it so that ISourcePackageR
Once the tests were passing again, I wrote some a webservice test to stop that vector.
If you see random oddness in my branch, it's because I took the liberty of cleaning up some lint I found in 'make lint.' Make lint is also a little brain-dead in thinking that you can't have a single item tuple have a comma without whitespace. I didn't fix those, because I thought that rule was stupid... I'm happy to fix it if you're that hung up on it.
Preview Diff
1 | === modified file 'database/schema/security.cfg' | |||
2 | --- database/schema/security.cfg 2010-06-22 16:08:05 +0000 | |||
3 | +++ database/schema/security.cfg 2010-07-21 09:22:01 +0000 | |||
4 | @@ -694,9 +694,11 @@ | |||
5 | 694 | type=user | 694 | type=user |
6 | 695 | groups=script | 695 | groups=script |
7 | 696 | public.archive = SELECT | 696 | public.archive = SELECT |
8 | 697 | public.archivepermission = SELECT | ||
9 | 697 | public.buildqueue = SELECT, INSERT, UPDATE | 698 | public.buildqueue = SELECT, INSERT, UPDATE |
10 | 698 | public.branch = SELECT | 699 | public.branch = SELECT |
11 | 699 | public.component = SELECT | 700 | public.component = SELECT |
12 | 701 | public.distribution = SELECT | ||
13 | 700 | public.distroseries = SELECT | 702 | public.distroseries = SELECT |
14 | 701 | public.distroarchseries = SELECT | 703 | public.distroarchseries = SELECT |
15 | 702 | public.job = SELECT, INSERT | 704 | public.job = SELECT, INSERT |
16 | 703 | 705 | ||
17 | === modified file 'lib/lp/code/browser/configure.zcml' | |||
18 | --- lib/lp/code/browser/configure.zcml 2010-07-14 10:09:35 +0000 | |||
19 | +++ lib/lp/code/browser/configure.zcml 2010-07-21 09:22:01 +0000 | |||
20 | @@ -1201,14 +1201,14 @@ | |||
21 | 1201 | </facet> | 1201 | </facet> |
22 | 1202 | <securedutility | 1202 | <securedutility |
23 | 1203 | name="BuildableDistroSeries" | 1203 | name="BuildableDistroSeries" |
25 | 1204 | component="lp.code.browser.sourcepackagerecipe.buildable_distroseries_vocabulary" | 1204 | component="lp.code.vocabularies.sourcepackagerecipe.buildable_distroseries_vocabulary" |
26 | 1205 | provides="zope.schema.interfaces.IVocabularyFactory" | 1205 | provides="zope.schema.interfaces.IVocabularyFactory" |
27 | 1206 | > | 1206 | > |
28 | 1207 | <allow interface="zope.schema.interfaces.IVocabularyFactory"/> | 1207 | <allow interface="zope.schema.interfaces.IVocabularyFactory"/> |
29 | 1208 | </securedutility> | 1208 | </securedutility> |
30 | 1209 | <securedutility | 1209 | <securedutility |
31 | 1210 | name="TargetPPAs" | 1210 | name="TargetPPAs" |
33 | 1211 | component="lp.code.browser.sourcepackagerecipe.target_ppas_vocabulary" | 1211 | component="lp.code.vocabularies.sourcepackagerecipe.target_ppas_vocabulary" |
34 | 1212 | provides="zope.schema.interfaces.IVocabularyFactory" | 1212 | provides="zope.schema.interfaces.IVocabularyFactory" |
35 | 1213 | > | 1213 | > |
36 | 1214 | <allow interface="zope.schema.interfaces.IVocabularyFactory"/> | 1214 | <allow interface="zope.schema.interfaces.IVocabularyFactory"/> |
37 | 1215 | 1215 | ||
38 | === modified file 'lib/lp/code/browser/sourcepackagerecipe.py' | |||
39 | --- lib/lp/code/browser/sourcepackagerecipe.py 2010-07-19 10:31:16 +0000 | |||
40 | +++ lib/lp/code/browser/sourcepackagerecipe.py 2010-07-21 09:22:01 +0000 | |||
41 | @@ -24,18 +24,14 @@ | |||
42 | 24 | from zope.event import notify | 24 | from zope.event import notify |
43 | 25 | from zope.interface import implements, Interface | 25 | from zope.interface import implements, Interface |
44 | 26 | from zope.schema import Choice, List, Text | 26 | from zope.schema import Choice, List, Text |
45 | 27 | from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm | ||
46 | 28 | 27 | ||
47 | 29 | from canonical.database.constants import UTC_NOW | 28 | from canonical.database.constants import UTC_NOW |
48 | 30 | from canonical.launchpad.browser.launchpad import Hierarchy | 29 | from canonical.launchpad.browser.launchpad import Hierarchy |
49 | 31 | from canonical.launchpad.interfaces import ILaunchBag | ||
50 | 32 | from canonical.launchpad.webapp import ( | 30 | from canonical.launchpad.webapp import ( |
51 | 33 | action, canonical_url, ContextMenu, custom_widget, | 31 | action, canonical_url, ContextMenu, custom_widget, |
52 | 34 | enabled_with_permission, LaunchpadEditFormView, LaunchpadFormView, | 32 | enabled_with_permission, LaunchpadEditFormView, LaunchpadFormView, |
53 | 35 | LaunchpadView, Link, Navigation, NavigationMenu, stepthrough, structured) | 33 | LaunchpadView, Link, Navigation, NavigationMenu, stepthrough, structured) |
54 | 36 | from canonical.launchpad.webapp.authorization import check_permission | ||
55 | 37 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb | 34 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
56 | 38 | from canonical.launchpad.webapp.sorting import sorted_dotted_numbers | ||
57 | 39 | from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget | 35 | from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget |
58 | 40 | from lp.code.errors import ForbiddenInstruction | 36 | from lp.code.errors import ForbiddenInstruction |
59 | 41 | from lp.code.errors import BuildAlreadyPending | 37 | from lp.code.errors import BuildAlreadyPending |
60 | @@ -44,10 +40,6 @@ | |||
61 | 44 | ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT) | 40 | ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT) |
62 | 45 | from lp.code.interfaces.sourcepackagerecipebuild import ( | 41 | from lp.code.interfaces.sourcepackagerecipebuild import ( |
63 | 46 | ISourcePackageRecipeBuildSource) | 42 | ISourcePackageRecipeBuildSource) |
64 | 47 | from lp.soyuz.browser.archive import make_archive_vocabulary | ||
65 | 48 | from lp.soyuz.interfaces.archive import ( | ||
66 | 49 | IArchiveSet) | ||
67 | 50 | from lp.registry.interfaces.distroseries import IDistroSeriesSet | ||
68 | 51 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 43 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
69 | 52 | 44 | ||
70 | 53 | RECIPE_BETA_MESSAGE = structured( | 45 | RECIPE_BETA_MESSAGE = structured( |
71 | @@ -148,8 +140,8 @@ | |||
72 | 148 | """Default view of a SourcePackageRecipe.""" | 140 | """Default view of a SourcePackageRecipe.""" |
73 | 149 | 141 | ||
74 | 150 | def initialize(self): | 142 | def initialize(self): |
77 | 151 | # XXX: rockstar: This should be removed when source package recipes are | 143 | # XXX: rockstar: This should be removed when source package recipes |
78 | 152 | # put into production. spec=sourcepackagerecipes | 144 | # are put into production. spec=sourcepackagerecipes |
79 | 153 | super(SourcePackageRecipeView, self).initialize() | 145 | super(SourcePackageRecipeView, self).initialize() |
80 | 154 | self.request.response.addWarningNotification(RECIPE_BETA_MESSAGE) | 146 | self.request.response.addWarningNotification(RECIPE_BETA_MESSAGE) |
81 | 155 | 147 | ||
82 | @@ -177,28 +169,6 @@ | |||
83 | 177 | return builds | 169 | return builds |
84 | 178 | 170 | ||
85 | 179 | 171 | ||
86 | 180 | def buildable_distroseries_vocabulary(context): | ||
87 | 181 | """Return a vocabulary of buildable distroseries.""" | ||
88 | 182 | ppas = getUtility(IArchiveSet).getPPAsForUser(getUtility(ILaunchBag).user) | ||
89 | 183 | supported_distros = [ppa.distribution for ppa in ppas] | ||
90 | 184 | dsset = getUtility(IDistroSeriesSet).search() | ||
91 | 185 | terms = sorted_dotted_numbers( | ||
92 | 186 | [SimpleTerm(distro, distro.id, distro.displayname) | ||
93 | 187 | for distro in dsset if ( | ||
94 | 188 | distro.active and distro.distribution in supported_distros)], | ||
95 | 189 | key=lambda term: term.value.version) | ||
96 | 190 | terms.reverse() | ||
97 | 191 | return SimpleVocabulary(terms) | ||
98 | 192 | |||
99 | 193 | |||
100 | 194 | def target_ppas_vocabulary(context): | ||
101 | 195 | """Return a vocabulary of ppas that the current user can target.""" | ||
102 | 196 | ppas = getUtility(IArchiveSet).getPPAsForUser(getUtility(ILaunchBag).user) | ||
103 | 197 | return make_archive_vocabulary( | ||
104 | 198 | ppa for ppa in ppas | ||
105 | 199 | if check_permission('launchpad.Append', ppa)) | ||
106 | 200 | |||
107 | 201 | |||
108 | 202 | class SourcePackageRecipeRequestBuildsView(LaunchpadFormView): | 172 | class SourcePackageRecipeRequestBuildsView(LaunchpadFormView): |
109 | 203 | """A view for requesting builds of a SourcePackageRecipe.""" | 173 | """A view for requesting builds of a SourcePackageRecipe.""" |
110 | 204 | 174 | ||
111 | @@ -261,7 +231,6 @@ | |||
112 | 261 | self.next_url = self.cancel_url | 231 | self.next_url = self.cancel_url |
113 | 262 | 232 | ||
114 | 263 | 233 | ||
115 | 264 | |||
116 | 265 | class ISourcePackageAddEditSchema(Interface): | 234 | class ISourcePackageAddEditSchema(Interface): |
117 | 266 | """Schema for adding or editing a recipe.""" | 235 | """Schema for adding or editing a recipe.""" |
118 | 267 | 236 | ||
119 | @@ -281,7 +250,6 @@ | |||
120 | 281 | description=u'The text of the recipe.') | 250 | description=u'The text of the recipe.') |
121 | 282 | 251 | ||
122 | 283 | 252 | ||
123 | 284 | |||
124 | 285 | class RecipeTextValidatorMixin: | 253 | class RecipeTextValidatorMixin: |
125 | 286 | """Class to validate that the Source Package Recipe text is valid.""" | 254 | """Class to validate that the Source Package Recipe text is valid.""" |
126 | 287 | 255 | ||
127 | @@ -310,8 +278,8 @@ | |||
128 | 310 | custom_widget('distros', LabeledMultiCheckBoxWidget) | 278 | custom_widget('distros', LabeledMultiCheckBoxWidget) |
129 | 311 | 279 | ||
130 | 312 | def initialize(self): | 280 | def initialize(self): |
133 | 313 | # XXX: rockstar: This should be removed when source package recipes are | 281 | # XXX: rockstar: This should be removed when source package recipes |
134 | 314 | # put into production. spec=sourcepackagerecipes | 282 | # are put into production. spec=sourcepackagerecipes |
135 | 315 | super(SourcePackageRecipeAddView, self).initialize() | 283 | super(SourcePackageRecipeAddView, self).initialize() |
136 | 316 | self.request.response.addWarningNotification(RECIPE_BETA_MESSAGE) | 284 | self.request.response.addWarningNotification(RECIPE_BETA_MESSAGE) |
137 | 317 | 285 | ||
138 | 318 | 286 | ||
139 | === modified file 'lib/lp/code/errors.py' | |||
140 | --- lib/lp/code/errors.py 2010-06-17 20:45:58 +0000 | |||
141 | +++ lib/lp/code/errors.py 2010-07-21 09:22:01 +0000 | |||
142 | @@ -8,6 +8,7 @@ | |||
143 | 8 | 'BadBranchMergeProposalSearchContext', | 8 | 'BadBranchMergeProposalSearchContext', |
144 | 9 | 'BadStateTransition', | 9 | 'BadStateTransition', |
145 | 10 | 'BuildAlreadyPending', | 10 | 'BuildAlreadyPending', |
146 | 11 | 'BuildNotAllowedForDistro', | ||
147 | 11 | 'BranchMergeProposalExists', | 12 | 'BranchMergeProposalExists', |
148 | 12 | 'CodeImportAlreadyRequested', | 13 | 'CodeImportAlreadyRequested', |
149 | 13 | 'CodeImportAlreadyRunning', | 14 | 'CodeImportAlreadyRunning', |
150 | @@ -144,3 +145,14 @@ | |||
151 | 144 | RecipeBuildException.__init__( | 145 | RecipeBuildException.__init__( |
152 | 145 | self, recipe, distroseries, | 146 | self, recipe, distroseries, |
153 | 146 | 'An identical build of this recipe is already pending.') | 147 | 'An identical build of this recipe is already pending.') |
154 | 148 | |||
155 | 149 | |||
156 | 150 | class BuildNotAllowedForDistro(RecipeBuildException): | ||
157 | 151 | """A build was requested against an unsupported distroseries.""" | ||
158 | 152 | |||
159 | 153 | webservice_error(400) | ||
160 | 154 | |||
161 | 155 | def __init__(self, recipe, distroseries): | ||
162 | 156 | RecipeBuildException.__init__( | ||
163 | 157 | self, recipe, distroseries, | ||
164 | 158 | 'A build against this distro is not allowed.') | ||
165 | 147 | 159 | ||
166 | === modified file 'lib/lp/code/model/sourcepackagerecipe.py' | |||
167 | --- lib/lp/code/model/sourcepackagerecipe.py 2010-07-19 15:32:21 +0000 | |||
168 | +++ lib/lp/code/model/sourcepackagerecipe.py 2010-07-21 09:22:01 +0000 | |||
169 | @@ -7,6 +7,7 @@ | |||
170 | 7 | 7 | ||
171 | 8 | __metaclass__ = type | 8 | __metaclass__ = type |
172 | 9 | __all__ = [ | 9 | __all__ = [ |
173 | 10 | 'get_buildable_distroseries_set', | ||
174 | 10 | 'SourcePackageRecipe', | 11 | 'SourcePackageRecipe', |
175 | 11 | ] | 12 | ] |
176 | 12 | 13 | ||
177 | @@ -25,7 +26,8 @@ | |||
178 | 25 | from canonical.launchpad.interfaces.lpstorm import IMasterStore, IStore | 26 | from canonical.launchpad.interfaces.lpstorm import IMasterStore, IStore |
179 | 26 | 27 | ||
180 | 27 | from lp.buildmaster.interfaces.buildbase import BuildStatus | 28 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
182 | 28 | from lp.code.errors import BuildAlreadyPending, TooManyBuilds | 29 | from lp.code.errors import (BuildAlreadyPending, BuildNotAllowedForDistro, |
183 | 30 | TooManyBuilds) | ||
184 | 29 | from lp.code.interfaces.sourcepackagerecipe import ( | 31 | from lp.code.interfaces.sourcepackagerecipe import ( |
185 | 30 | ISourcePackageRecipe, ISourcePackageRecipeSource, | 32 | ISourcePackageRecipe, ISourcePackageRecipeSource, |
186 | 31 | ISourcePackageRecipeData) | 33 | ISourcePackageRecipeData) |
187 | @@ -33,11 +35,24 @@ | |||
188 | 33 | ISourcePackageRecipeBuildSource) | 35 | ISourcePackageRecipeBuildSource) |
189 | 34 | from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild | 36 | from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild |
190 | 35 | from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData | 37 | from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData |
191 | 38 | from lp.registry.interfaces.distroseries import IDistroSeriesSet | ||
192 | 36 | from lp.registry.model.distroseries import DistroSeries | 39 | from lp.registry.model.distroseries import DistroSeries |
194 | 37 | from lp.soyuz.interfaces.archive import ArchivePurpose | 40 | from lp.soyuz.interfaces.archive import ArchivePurpose, IArchiveSet |
195 | 38 | from lp.soyuz.interfaces.component import IComponentSet | 41 | from lp.soyuz.interfaces.component import IComponentSet |
196 | 39 | 42 | ||
197 | 40 | 43 | ||
198 | 44 | def get_buildable_distroseries_set(user): | ||
199 | 45 | ppas = getUtility(IArchiveSet).getPPAsForUser(user) | ||
200 | 46 | supported_distros = [ppa.distribution for ppa in ppas] | ||
201 | 47 | distros = getUtility(IDistroSeriesSet).search() | ||
202 | 48 | |||
203 | 49 | buildables = [] | ||
204 | 50 | for distro in distros: | ||
205 | 51 | if distro.active and distro.distribution in supported_distros: | ||
206 | 52 | buildables.append(distro) | ||
207 | 53 | return buildables | ||
208 | 54 | |||
209 | 55 | |||
210 | 41 | class NonPPABuildRequest(Exception): | 56 | class NonPPABuildRequest(Exception): |
211 | 42 | """A build was requested to a non-PPA and this is currently | 57 | """A build was requested to a non-PPA and this is currently |
212 | 43 | unsupported.""" | 58 | unsupported.""" |
213 | @@ -188,6 +203,11 @@ | |||
214 | 188 | if archive.purpose != ArchivePurpose.PPA: | 203 | if archive.purpose != ArchivePurpose.PPA: |
215 | 189 | raise NonPPABuildRequest | 204 | raise NonPPABuildRequest |
216 | 190 | component = getUtility(IComponentSet)["multiverse"] | 205 | component = getUtility(IComponentSet)["multiverse"] |
217 | 206 | |||
218 | 207 | buildable_distros = get_buildable_distroseries_set(archive.owner) | ||
219 | 208 | if distroseries not in buildable_distros: | ||
220 | 209 | raise BuildNotAllowedForDistro(self, distroseries) | ||
221 | 210 | |||
222 | 191 | reject_reason = archive.checkUpload( | 211 | reject_reason = archive.checkUpload( |
223 | 192 | requester, self.distroseries, None, component, pocket) | 212 | requester, self.distroseries, None, component, pocket) |
224 | 193 | if reject_reason is not None: | 213 | if reject_reason is not None: |
225 | 194 | 214 | ||
226 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py' | |||
227 | --- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-06-18 19:36:55 +0000 | |||
228 | +++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-07-21 09:22:01 +0000 | |||
229 | @@ -45,7 +45,6 @@ | |||
230 | 45 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 45 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
231 | 46 | from lp.services.job.interfaces.job import ( | 46 | from lp.services.job.interfaces.job import ( |
232 | 47 | IJob, JobStatus) | 47 | IJob, JobStatus) |
233 | 48 | from lp.soyuz.model.processor import ProcessorFamily | ||
234 | 49 | from lp.testing import ( | 48 | from lp.testing import ( |
235 | 50 | ANONYMOUS, launchpadlib_for, login, login_person, person_logged_in, | 49 | ANONYMOUS, launchpadlib_for, login, login_person, person_logged_in, |
236 | 51 | TestCaseWithFactory, ws_object) | 50 | TestCaseWithFactory, ws_object) |
237 | @@ -167,7 +166,6 @@ | |||
238 | 167 | branch2 = self.factory.makeAnyBranch() | 166 | branch2 = self.factory.makeAnyBranch() |
239 | 168 | builder_recipe2 = self.factory.makeRecipe(branch2) | 167 | builder_recipe2 = self.factory.makeRecipe(branch2) |
240 | 169 | login_person(sp_recipe.owner.teamowner) | 168 | login_person(sp_recipe.owner.teamowner) |
241 | 170 | #import pdb; pdb.set_trace() | ||
242 | 171 | sp_recipe.builder_recipe = builder_recipe2 | 169 | sp_recipe.builder_recipe = builder_recipe2 |
243 | 172 | self.assertEquals([branch2], list(sp_recipe.getReferencedBranches())) | 170 | self.assertEquals([branch2], list(sp_recipe.getReferencedBranches())) |
244 | 173 | 171 | ||
245 | @@ -297,6 +295,7 @@ | |||
246 | 297 | name=u'myrecipe', owner=requester) | 295 | name=u'myrecipe', owner=requester) |
247 | 298 | series = list(recipe.distroseries)[0] | 296 | series = list(recipe.distroseries)[0] |
248 | 299 | archive = self.factory.makeArchive(owner=requester) | 297 | archive = self.factory.makeArchive(owner=requester) |
249 | 298 | |||
250 | 300 | def request_build(): | 299 | def request_build(): |
251 | 301 | build = recipe.requestBuild(archive, requester, series, | 300 | build = recipe.requestBuild(archive, requester, series, |
252 | 302 | PackagePublishingPocket.RELEASE) | 301 | PackagePublishingPocket.RELEASE) |
253 | @@ -322,10 +321,8 @@ | |||
254 | 322 | self.factory.makeArchive(owner=recipe.owner), recipe.owner, | 321 | self.factory.makeArchive(owner=recipe.owner), recipe.owner, |
255 | 323 | series, PackagePublishingPocket.RELEASE) | 322 | series, PackagePublishingPocket.RELEASE) |
256 | 324 | # Varying distroseries allows build. | 323 | # Varying distroseries allows build. |
261 | 325 | new_distroseries = self.factory.makeDistroSeries() | 324 | new_distroseries = self.factory.makeSourcePackageRecipeDistroseries( |
262 | 326 | new_distroseries.nominatedarchindep = new_distroseries.newArch( | 325 | "hoary") |
259 | 327 | 'i386', ProcessorFamily.get(1), False, recipe.owner, | ||
260 | 328 | supports_virtualized=True) | ||
263 | 329 | recipe.requestBuild(archive, recipe.owner, | 326 | recipe.requestBuild(archive, recipe.owner, |
264 | 330 | new_distroseries, PackagePublishingPocket.RELEASE) | 327 | new_distroseries, PackagePublishingPocket.RELEASE) |
265 | 331 | # Changing status of old build allows new build. | 328 | # Changing status of old build allows new build. |
266 | @@ -430,6 +427,7 @@ | |||
267 | 430 | build.buildduration = timedelta(minutes=10) | 427 | build.buildduration = timedelta(minutes=10) |
268 | 431 | self.assertEqual( | 428 | self.assertEqual( |
269 | 432 | timedelta(minutes=10), recipe.getMedianBuildDuration()) | 429 | timedelta(minutes=10), recipe.getMedianBuildDuration()) |
270 | 430 | |||
271 | 433 | def addBuild(minutes): | 431 | def addBuild(minutes): |
272 | 434 | build = removeSecurityProxy( | 432 | build = removeSecurityProxy( |
273 | 435 | self.factory.makeSourcePackageRecipeBuild(recipe=recipe)) | 433 | self.factory.makeSourcePackageRecipeBuild(recipe=recipe)) |
274 | @@ -691,11 +689,7 @@ | |||
275 | 691 | """Build requests can be performed.""" | 689 | """Build requests can be performed.""" |
276 | 692 | person = self.factory.makePerson() | 690 | person = self.factory.makePerson() |
277 | 693 | archive = self.factory.makeArchive(owner=person) | 691 | archive = self.factory.makeArchive(owner=person) |
283 | 694 | distroseries = self.factory.makeDistroSeries() | 692 | distroseries = self.factory.makeSourcePackageRecipeDistroseries() |
279 | 695 | distroseries_i386 = distroseries.newArch( | ||
280 | 696 | 'i386', ProcessorFamily.get(1), False, person, | ||
281 | 697 | supports_virtualized=True) | ||
282 | 698 | distroseries.nominatedarchindep = distroseries_i386 | ||
284 | 699 | 693 | ||
285 | 700 | recipe, user, launchpad = self.makeRecipe(person) | 694 | recipe, user, launchpad = self.makeRecipe(person) |
286 | 701 | distroseries = ws_object(launchpad, distroseries) | 695 | distroseries = ws_object(launchpad, distroseries) |
287 | @@ -708,11 +702,7 @@ | |||
288 | 708 | """Build requests are rejected if already pending.""" | 702 | """Build requests are rejected if already pending.""" |
289 | 709 | person = self.factory.makePerson() | 703 | person = self.factory.makePerson() |
290 | 710 | archive = self.factory.makeArchive(owner=person) | 704 | archive = self.factory.makeArchive(owner=person) |
296 | 711 | distroseries = self.factory.makeDistroSeries() | 705 | distroseries = self.factory.makeSourcePackageRecipeDistroseries() |
292 | 712 | distroseries_i386 = distroseries.newArch( | ||
293 | 713 | 'i386', ProcessorFamily.get(1), False, person, | ||
294 | 714 | supports_virtualized=True) | ||
295 | 715 | distroseries.nominatedarchindep = distroseries_i386 | ||
297 | 716 | 706 | ||
298 | 717 | recipe, user, launchpad = self.makeRecipe(person) | 707 | recipe, user, launchpad = self.makeRecipe(person) |
299 | 718 | distroseries = ws_object(launchpad, distroseries) | 708 | distroseries = ws_object(launchpad, distroseries) |
300 | @@ -729,11 +719,7 @@ | |||
301 | 729 | """Build requests are rejected if they exceed quota.""" | 719 | """Build requests are rejected if they exceed quota.""" |
302 | 730 | person = self.factory.makePerson() | 720 | person = self.factory.makePerson() |
303 | 731 | archives = [self.factory.makeArchive(owner=person) for x in range(6)] | 721 | archives = [self.factory.makeArchive(owner=person) for x in range(6)] |
309 | 732 | distroseries = self.factory.makeDistroSeries() | 722 | distroseries = self.factory.makeSourcePackageRecipeDistroseries() |
305 | 733 | distroseries_i386 = distroseries.newArch( | ||
306 | 734 | 'i386', ProcessorFamily.get(1), False, person, | ||
307 | 735 | supports_virtualized=True) | ||
308 | 736 | distroseries.nominatedarchindep = distroseries_i386 | ||
310 | 737 | 723 | ||
311 | 738 | recipe, user, launchpad = self.makeRecipe(person) | 724 | recipe, user, launchpad = self.makeRecipe(person) |
312 | 739 | distroseries = ws_object(launchpad, distroseries) | 725 | distroseries = ws_object(launchpad, distroseries) |
313 | @@ -749,6 +735,21 @@ | |||
314 | 749 | pocket=PackagePublishingPocket.RELEASE.title) | 735 | pocket=PackagePublishingPocket.RELEASE.title) |
315 | 750 | self.assertIn('TooManyBuilds', str(e)) | 736 | self.assertIn('TooManyBuilds', str(e)) |
316 | 751 | 737 | ||
317 | 738 | def test_requestBuildRejectUnsupportedDistroSeries(self): | ||
318 | 739 | """Build requests are rejected if they have a bad distroseries.""" | ||
319 | 740 | person = self.factory.makePerson() | ||
320 | 741 | archives = [self.factory.makeArchive(owner=person) for x in range(6)] | ||
321 | 742 | distroseries = self.factory.makeDistroSeries() | ||
322 | 743 | |||
323 | 744 | recipe, user, launchpad = self.makeRecipe(person) | ||
324 | 745 | distroseries = ws_object(launchpad, distroseries) | ||
325 | 746 | archive = ws_object(launchpad, archives[-1]) | ||
326 | 747 | |||
327 | 748 | e = self.assertRaises(Exception, recipe.requestBuild, | ||
328 | 749 | archive=archive, distroseries=distroseries, | ||
329 | 750 | pocket=PackagePublishingPocket.RELEASE.title) | ||
330 | 751 | self.assertIn('BuildNotAllowedForDistro', str(e)) | ||
331 | 752 | |||
332 | 752 | 753 | ||
333 | 753 | def test_suite(): | 754 | def test_suite(): |
334 | 754 | return unittest.TestLoader().loadTestsFromName(__name__) | 755 | return unittest.TestLoader().loadTestsFromName(__name__) |
335 | 755 | 756 | ||
336 | === removed file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py.moved' | |||
337 | --- lib/lp/code/model/tests/test_sourcepackagerecipe.py.moved 2010-03-26 19:29:56 +0000 | |||
338 | +++ lib/lp/code/model/tests/test_sourcepackagerecipe.py.moved 1970-01-01 00:00:00 +0000 | |||
339 | @@ -1,40 +0,0 @@ | |||
340 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
341 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
342 | 3 | |||
343 | 4 | |||
344 | 5 | __metaclass__ = type | ||
345 | 6 | |||
346 | 7 | |||
347 | 8 | import unittest | ||
348 | 9 | |||
349 | 10 | from canonical.testing import DatabaseFunctionalLayer | ||
350 | 11 | from lp.testing import login_person, TestCaseWithFactory | ||
351 | 12 | |||
352 | 13 | |||
353 | 14 | class TestSourcePackageRecipe(TestCaseWithFactory): | ||
354 | 15 | |||
355 | 16 | layer = DatabaseFunctionalLayer | ||
356 | 17 | |||
357 | 18 | def test_distroseries(self): | ||
358 | 19 | """Test that the distroseries behaves as a set.""" | ||
359 | 20 | recipe = self.factory.makeSourcePackageRecipe() | ||
360 | 21 | distroseries = self.factory.makeDistroSeries() | ||
361 | 22 | (old_distroseries,) = recipe.distroseries | ||
362 | 23 | recipe.distroseries.add(distroseries) | ||
363 | 24 | self.assertEqual( | ||
364 | 25 | set([distroseries, old_distroseries]), set(recipe.distroseries)) | ||
365 | 26 | recipe.distroseries.remove(distroseries) | ||
366 | 27 | self.assertEqual([old_distroseries], list(recipe.distroseries)) | ||
367 | 28 | recipe.distroseries.clear() | ||
368 | 29 | self.assertEqual([], list(recipe.distroseries)) | ||
369 | 30 | |||
370 | 31 | def test_build_daily(self): | ||
371 | 32 | """Test that build_daily behaves as a bool.""" | ||
372 | 33 | recipe = self.factory.makeSourcePackageRecipe() | ||
373 | 34 | self.assertFalse(recipe.build_daily) | ||
374 | 35 | login_person(recipe.owner) | ||
375 | 36 | recipe.build_daily = True | ||
376 | 37 | self.assertTrue(recipe.build_daily) | ||
377 | 38 | |||
378 | 39 | def test_suite(): | ||
379 | 40 | return unittest.TestLoader().loadTestsFromName(__name__) | ||
380 | 41 | 0 | ||
381 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py' | |||
382 | --- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-07-14 08:42:01 +0000 | |||
383 | +++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-07-21 09:22:01 +0000 | |||
384 | @@ -249,10 +249,8 @@ | |||
385 | 249 | recipe.requestBuild( | 249 | recipe.requestBuild( |
386 | 250 | recipe.daily_build_archive, recipe.owner, first_distroseries, | 250 | recipe.daily_build_archive, recipe.owner, first_distroseries, |
387 | 251 | PackagePublishingPocket.RELEASE) | 251 | PackagePublishingPocket.RELEASE) |
392 | 252 | second_distroseries = self.factory.makeDistroSeries() | 252 | second_distroseries = \ |
393 | 253 | second_distroseries.nominatedarchindep = second_distroseries.newArch( | 253 | self.factory.makeSourcePackageRecipeDistroseries("hoary") |
390 | 254 | 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(), | ||
391 | 255 | supports_virtualized=True) | ||
394 | 256 | recipe.distroseries.add(second_distroseries) | 254 | recipe.distroseries.add(second_distroseries) |
395 | 257 | builds = SourcePackageRecipeBuild.makeDailyBuilds() | 255 | builds = SourcePackageRecipeBuild.makeDailyBuilds() |
396 | 258 | self.assertEqual( | 256 | self.assertEqual( |
397 | @@ -274,6 +272,7 @@ | |||
398 | 274 | recipe=recipe, distroseries=series) | 272 | recipe=recipe, distroseries=series) |
399 | 275 | self.factory.makeSourcePackageRecipeBuild( | 273 | self.factory.makeSourcePackageRecipeBuild( |
400 | 276 | requester=requester, distroseries=series) | 274 | requester=requester, distroseries=series) |
401 | 275 | |||
402 | 277 | def get_recent(): | 276 | def get_recent(): |
403 | 278 | Store.of(build).flush() | 277 | Store.of(build).flush() |
404 | 279 | return SourcePackageRecipeBuild.getRecentBuilds( | 278 | return SourcePackageRecipeBuild.getRecentBuilds( |
405 | @@ -332,11 +331,11 @@ | |||
406 | 332 | body, footer = message.get_payload(decode=True).split('\n-- \n') | 331 | body, footer = message.get_payload(decode=True).split('\n-- \n') |
407 | 333 | self.assertEqual( | 332 | self.assertEqual( |
408 | 334 | 'Build person/recipe into ppa for distroseries: Successfully' | 333 | 'Build person/recipe into ppa for distroseries: Successfully' |
411 | 335 | ' built.\n', body | 334 | ' built.\n', body) |
410 | 336 | ) | ||
412 | 337 | 335 | ||
413 | 338 | def test_handleStatusNotifies(self): | 336 | def test_handleStatusNotifies(self): |
414 | 339 | """"handleStatus causes notification, even if OK.""" | 337 | """"handleStatus causes notification, even if OK.""" |
415 | 338 | |||
416 | 340 | def prepare_build(): | 339 | def prepare_build(): |
417 | 341 | queue_record = self.factory.makeSourcePackageRecipeBuildJob() | 340 | queue_record = self.factory.makeSourcePackageRecipeBuildJob() |
418 | 342 | build = queue_record.specific_job.build | 341 | build = queue_record.specific_job.build |
419 | @@ -345,6 +344,7 @@ | |||
420 | 345 | slave = WaitingSlave('BuildStatus.OK') | 344 | slave = WaitingSlave('BuildStatus.OK') |
421 | 346 | queue_record.builder.setSlaveForTesting(slave) | 345 | queue_record.builder.setSlaveForTesting(slave) |
422 | 347 | return build | 346 | return build |
423 | 347 | |||
424 | 348 | def assertNotifyOnce(status, build): | 348 | def assertNotifyOnce(status, build): |
425 | 349 | build.handleStatus(status, None, {'filemap': {}}) | 349 | build.handleStatus(status, None, {'filemap': {}}) |
426 | 350 | self.assertEqual(1, len(pop_notifications())) | 350 | self.assertEqual(1, len(pop_notifications())) |
427 | 351 | 351 | ||
428 | === added file 'lib/lp/code/vocabularies/sourcepackagerecipe.py' | |||
429 | --- lib/lp/code/vocabularies/sourcepackagerecipe.py 1970-01-01 00:00:00 +0000 | |||
430 | +++ lib/lp/code/vocabularies/sourcepackagerecipe.py 2010-07-21 09:22:01 +0000 | |||
431 | @@ -0,0 +1,38 @@ | |||
432 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
433 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
434 | 3 | |||
435 | 4 | """Source Package Recipe vocabularies used in the lp/code modules.""" | ||
436 | 5 | __metaclass__ = type | ||
437 | 6 | __all__ = [ | ||
438 | 7 | 'buildable_distroseries_vocabulary', | ||
439 | 8 | 'target_ppas_vocabulary', | ||
440 | 9 | ] | ||
441 | 10 | |||
442 | 11 | from zope.component import getUtility | ||
443 | 12 | from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm | ||
444 | 13 | |||
445 | 14 | from canonical.launchpad.interfaces import ILaunchBag | ||
446 | 15 | from canonical.launchpad.webapp.authorization import check_permission | ||
447 | 16 | from canonical.launchpad.webapp.sorting import sorted_dotted_numbers | ||
448 | 17 | from lp.code.model.sourcepackagerecipe import get_buildable_distroseries_set | ||
449 | 18 | from lp.soyuz.browser.archive import make_archive_vocabulary | ||
450 | 19 | from lp.soyuz.interfaces.archive import IArchiveSet | ||
451 | 20 | |||
452 | 21 | |||
453 | 22 | def buildable_distroseries_vocabulary(context): | ||
454 | 23 | """Return a vocabulary of buildable distroseries.""" | ||
455 | 24 | distros = get_buildable_distroseries_set(getUtility(ILaunchBag).user) | ||
456 | 25 | terms = sorted_dotted_numbers( | ||
457 | 26 | [SimpleTerm(distro, distro.id, distro.displayname) | ||
458 | 27 | for distro in distros], | ||
459 | 28 | key=lambda term: term.value.version) | ||
460 | 29 | terms.reverse() | ||
461 | 30 | return SimpleVocabulary(terms) | ||
462 | 31 | |||
463 | 32 | |||
464 | 33 | def target_ppas_vocabulary(context): | ||
465 | 34 | """Return a vocabulary of ppas that the current user can target.""" | ||
466 | 35 | ppas = getUtility(IArchiveSet).getPPAsForUser(getUtility(ILaunchBag).user) | ||
467 | 36 | return make_archive_vocabulary( | ||
468 | 37 | ppa for ppa in ppas | ||
469 | 38 | if check_permission('launchpad.Append', ppa)) | ||
470 | 0 | 39 | ||
471 | === modified file 'lib/lp/testing/factory.py' | |||
472 | --- lib/lp/testing/factory.py 2010-07-17 21:02:33 +0000 | |||
473 | +++ lib/lp/testing/factory.py 2010-07-21 09:22:01 +0000 | |||
474 | @@ -145,7 +145,7 @@ | |||
475 | 145 | from lp.soyuz.interfaces.section import ISectionSet | 145 | from lp.soyuz.interfaces.section import ISectionSet |
476 | 146 | from lp.soyuz.model.binarypackagename import BinaryPackageName | 146 | from lp.soyuz.model.binarypackagename import BinaryPackageName |
477 | 147 | from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease | 147 | from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease |
479 | 148 | from lp.soyuz.model.processor import ProcessorFamily, ProcessorFamilySet | 148 | from lp.soyuz.model.processor import ProcessorFamilySet |
480 | 149 | from lp.soyuz.model.publishing import ( | 149 | from lp.soyuz.model.publishing import ( |
481 | 150 | BinaryPackagePublishingHistory, SourcePackagePublishingHistory) | 150 | BinaryPackagePublishingHistory, SourcePackagePublishingHistory) |
482 | 151 | from lp.testing import ( | 151 | from lp.testing import ( |
483 | @@ -1798,6 +1798,15 @@ | |||
484 | 1798 | parser = RecipeParser(self.makeRecipeText(*branches)) | 1798 | parser = RecipeParser(self.makeRecipeText(*branches)) |
485 | 1799 | return parser.parse() | 1799 | return parser.parse() |
486 | 1800 | 1800 | ||
487 | 1801 | def makeSourcePackageRecipeDistroseries(self, name="warty"): | ||
488 | 1802 | """Return a supported Distroseries to use with Source Package Recipes. | ||
489 | 1803 | |||
490 | 1804 | Ew. This uses sampledata currently, which is the ONLY reason this | ||
491 | 1805 | method exists: it gives us a migration path away from sampledata. | ||
492 | 1806 | """ | ||
493 | 1807 | ubuntu = getUtility(IDistributionSet).getByName('ubuntu') | ||
494 | 1808 | return ubuntu.getSeries(name) | ||
495 | 1809 | |||
496 | 1801 | def makeSourcePackageRecipe(self, registrant=None, owner=None, | 1810 | def makeSourcePackageRecipe(self, registrant=None, owner=None, |
497 | 1802 | distroseries=None, name=None, | 1811 | distroseries=None, name=None, |
498 | 1803 | description=None, branches=(), | 1812 | description=None, branches=(), |
499 | @@ -1809,10 +1818,7 @@ | |||
500 | 1809 | if owner is None: | 1818 | if owner is None: |
501 | 1810 | owner = self.makePerson() | 1819 | owner = self.makePerson() |
502 | 1811 | if distroseries is None: | 1820 | if distroseries is None: |
507 | 1812 | distroseries = self.makeDistroSeries() | 1821 | distroseries = self.makeSourcePackageRecipeDistroseries() |
504 | 1813 | distroseries.nominatedarchindep = distroseries.newArch( | ||
505 | 1814 | 'i386', ProcessorFamily.get(1), False, owner, | ||
506 | 1815 | supports_virtualized=True) | ||
508 | 1816 | 1822 | ||
509 | 1817 | if name is None: | 1823 | if name is None: |
510 | 1818 | name = self.getUniqueString().decode('utf8') | 1824 | name = self.getUniqueString().decode('utf8') |
511 | @@ -1903,6 +1909,7 @@ | |||
512 | 1903 | ddd57463774cae9b50e70cd51221281b 185913 ed_0.2.orig.tar.gz | 1909 | ddd57463774cae9b50e70cd51221281b 185913 ed_0.2.orig.tar.gz |
513 | 1904 | f9e1e5f13725f581919e9bfd62272a05 8506 ed_0.2-20.diff.gz | 1910 | f9e1e5f13725f581919e9bfd62272a05 8506 ed_0.2-20.diff.gz |
514 | 1905 | """)) | 1911 | """)) |
515 | 1912 | |||
516 | 1906 | class Changes: | 1913 | class Changes: |
517 | 1907 | architectures = ['source'] | 1914 | architectures = ['source'] |
518 | 1908 | logger = QuietFakeLogger() | 1915 | logger = QuietFakeLogger() |
get_buildable_ distroseries_ set could use a docstring - just a little one noting why its not doing the search at the sql layer perhaps?
+ new_distroseries = archive. distribution. getSeries( 'hoary' )
doesn't seem to use your helper function - could it?
copy=n=paste mismatch:
294 + def test_requestBui ldRejectUnsuppo rtedDistroSerie s(self) :
295 + """Build requests are rejected if they exceed quota."""
this :
369 + second_distroseries = \ makeSourcePacka geRecipeDistros eries(" hoary")
370 + self.factory.
is that a change to use another non-sample- data-tied helper?
Generally fine to land, consider these recommendations.
Thanks,
Rob