Merge lp:~rockstar/launchpad/recipe-oopses into lp:launchpad
- recipe-oopses
- Merge into devel
Proposed by
Paul Hummer
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 10997 | ||||
Proposed branch: | lp:~rockstar/launchpad/recipe-oopses | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
312 lines (+111/-46) 7 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+24/-5) lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+49/-5) lib/lp/code/errors.py (+32/-0) lib/lp/code/interfaces/sourcepackagerecipe.py (+0/-31) lib/lp/code/model/sourcepackagerecipe.py (+2/-1) lib/lp/code/model/sourcepackagerecipedata.py (+1/-2) lib/lp/code/model/tests/test_sourcepackagerecipe.py (+3/-2) |
||||
To merge this branch: | bzr merge lp:~rockstar/launchpad/recipe-oopses | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Penhey (community) | Approve | ||
Review via email: mp+27313@code.launchpad.net |
Commit message
Description of the change
This bug fixes bug #586966 - The run command is not allowed in our use of bzr-builder, and so we should catch that case and notify the user that what they're doing just ain't cool.
To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote : | # |
Approved conditional on fixing the formatting on the XXX comments :)
We should have name, date and bug.
review:
Approve
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 2010-06-09 15:32:32 +0000 | |||
3 | +++ lib/lp/code/browser/sourcepackagerecipe.py 2010-06-11 19:07:30 +0000 | |||
4 | @@ -39,6 +39,7 @@ | |||
5 | 39 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb | 39 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
6 | 40 | from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget | 40 | from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget |
7 | 41 | from lp.buildmaster.interfaces.buildbase import BuildStatus | 41 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
8 | 42 | from lp.code.errors import ForbiddenInstruction | ||
9 | 42 | from lp.code.interfaces.sourcepackagerecipe import ( | 43 | from lp.code.interfaces.sourcepackagerecipe import ( |
10 | 43 | ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT) | 44 | ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT) |
11 | 44 | from lp.code.interfaces.sourcepackagerecipebuild import ( | 45 | from lp.code.interfaces.sourcepackagerecipebuild import ( |
12 | @@ -352,9 +353,18 @@ | |||
13 | 352 | def request_action(self, action, data): | 353 | def request_action(self, action, data): |
14 | 353 | parser = RecipeParser(data['recipe_text']) | 354 | parser = RecipeParser(data['recipe_text']) |
15 | 354 | recipe = parser.parse() | 355 | recipe = parser.parse() |
19 | 355 | source_package_recipe = getUtility(ISourcePackageRecipeSource).new( | 356 | try: |
20 | 356 | self.user, self.user, data['distros'], | 357 | source_package_recipe = getUtility( |
21 | 357 | data['name'], recipe, data['description']) | 358 | ISourcePackageRecipeSource).new( |
22 | 359 | self.user, self.user, data['distros'], | ||
23 | 360 | data['name'], recipe, data['description']) | ||
24 | 361 | except ForbiddenInstruction: | ||
25 | 362 | # XXX: bug=592513 We shouldn't be hardcoding "run" here. | ||
26 | 363 | self.setFieldError( | ||
27 | 364 | 'recipe_text', | ||
28 | 365 | 'The bzr-builder instruction "run" is not permitted here.') | ||
29 | 366 | return | ||
30 | 367 | |||
31 | 358 | self.next_url = canonical_url(source_package_recipe) | 368 | self.next_url = canonical_url(source_package_recipe) |
32 | 359 | 369 | ||
33 | 360 | def validate(self, data): | 370 | def validate(self, data): |
34 | @@ -402,8 +412,17 @@ | |||
35 | 402 | parser = RecipeParser(recipe_text) | 412 | parser = RecipeParser(recipe_text) |
36 | 403 | recipe = parser.parse() | 413 | recipe = parser.parse() |
37 | 404 | if self.context.builder_recipe != recipe: | 414 | if self.context.builder_recipe != recipe: |
40 | 405 | self.context.builder_recipe = recipe | 415 | try: |
41 | 406 | changed = True | 416 | self.context.builder_recipe = recipe |
42 | 417 | changed = True | ||
43 | 418 | except ForbiddenInstruction: | ||
44 | 419 | # XXX: bug=592513 We shouldn't be hardcoding "run" here. | ||
45 | 420 | self.setFieldError( | ||
46 | 421 | 'recipe_text', | ||
47 | 422 | 'The bzr-builder instruction "run" is not permitted here.' | ||
48 | 423 | ) | ||
49 | 424 | return | ||
50 | 425 | |||
51 | 407 | 426 | ||
52 | 408 | distros = data.pop('distros') | 427 | distros = data.pop('distros') |
53 | 409 | if distros != self.context.distroseries: | 428 | if distros != self.context.distroseries: |
54 | 410 | 429 | ||
55 | === modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py' | |||
56 | --- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-09 15:32:32 +0000 | |||
57 | +++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-11 19:07:30 +0000 | |||
58 | @@ -88,7 +88,6 @@ | |||
59 | 88 | name='ratatouille', displayname='Ratatouille') | 88 | name='ratatouille', displayname='Ratatouille') |
60 | 89 | branch = self.factory.makeBranch( | 89 | branch = self.factory.makeBranch( |
61 | 90 | owner=self.chef, product=product, name='veggies') | 90 | owner=self.chef, product=product, name='veggies') |
62 | 91 | self.factory.makeSourcePackage() | ||
63 | 92 | 91 | ||
64 | 93 | # A new recipe can be created from the branch page. | 92 | # A new recipe can be created from the branch page. |
65 | 94 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) | 93 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) |
66 | @@ -120,6 +119,31 @@ | |||
67 | 120 | self.assertTextMatchesExpressionIgnoreWhitespace( | 119 | self.assertTextMatchesExpressionIgnoreWhitespace( |
68 | 121 | pattern, main_text) | 120 | pattern, main_text) |
69 | 122 | 121 | ||
70 | 122 | def test_create_recipe_forbidden_instruction(self): | ||
71 | 123 | # We don't allow the "run" instruction in our recipes. Make sure this | ||
72 | 124 | # is communicated to the user properly. | ||
73 | 125 | product = self.factory.makeProduct( | ||
74 | 126 | name='ratatouille', displayname='Ratatouille') | ||
75 | 127 | branch = self.factory.makeBranch( | ||
76 | 128 | owner=self.chef, product=product, name='veggies') | ||
77 | 129 | |||
78 | 130 | # A new recipe can be created from the branch page. | ||
79 | 131 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) | ||
80 | 132 | browser.getLink('Create packaging recipe').click() | ||
81 | 133 | |||
82 | 134 | browser.getControl(name='field.name').value = 'daily' | ||
83 | 135 | browser.getControl('Description').value = 'Make some food!' | ||
84 | 136 | browser.getControl('Secret Squirrel').click() | ||
85 | 137 | |||
86 | 138 | browser.getControl('Recipe text').value = ( | ||
87 | 139 | browser.getControl('Recipe text').value + 'run cat /etc/passwd') | ||
88 | 140 | |||
89 | 141 | browser.getControl('Create Recipe').click() | ||
90 | 142 | |||
91 | 143 | self.assertEqual( | ||
92 | 144 | extract_text(find_tags_by_class(browser.contents, 'message')[1]), | ||
93 | 145 | 'The bzr-builder instruction "run" is not permitted here.') | ||
94 | 146 | |||
95 | 123 | def test_create_new_recipe_empty_name(self): | 147 | def test_create_new_recipe_empty_name(self): |
96 | 124 | # Leave off the name and make sure that the widgets validate before | 148 | # Leave off the name and make sure that the widgets validate before |
97 | 125 | # the content validates. | 149 | # the content validates. |
98 | @@ -127,7 +151,6 @@ | |||
99 | 127 | name='ratatouille', displayname='Ratatouille') | 151 | name='ratatouille', displayname='Ratatouille') |
100 | 128 | branch = self.factory.makeBranch( | 152 | branch = self.factory.makeBranch( |
101 | 129 | owner=self.chef, product=product, name='veggies') | 153 | owner=self.chef, product=product, name='veggies') |
102 | 130 | self.factory.makeSourcePackage() | ||
103 | 131 | 154 | ||
104 | 132 | # A new recipe can be created from the branch page. | 155 | # A new recipe can be created from the branch page. |
105 | 133 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) | 156 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) |
106 | @@ -148,7 +171,6 @@ | |||
107 | 148 | name='ratatouille', displayname='Ratatouille') | 171 | name='ratatouille', displayname='Ratatouille') |
108 | 149 | branch = self.factory.makeBranch( | 172 | branch = self.factory.makeBranch( |
109 | 150 | owner=self.chef, product=product, name='veggies') | 173 | owner=self.chef, product=product, name='veggies') |
110 | 151 | self.factory.makeSourcePackage() | ||
111 | 152 | 174 | ||
112 | 153 | # A new recipe can be created from the branch page. | 175 | # A new recipe can be created from the branch page. |
113 | 154 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) | 176 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) |
114 | @@ -172,7 +194,6 @@ | |||
115 | 172 | name='ratatouille', displayname='Ratatouille') | 194 | name='ratatouille', displayname='Ratatouille') |
116 | 173 | branch = self.factory.makeBranch( | 195 | branch = self.factory.makeBranch( |
117 | 174 | owner=self.chef, product=product, name='veggies') | 196 | owner=self.chef, product=product, name='veggies') |
118 | 175 | self.factory.makeSourcePackage(sourcepackagename='ratatouille') | ||
119 | 176 | 197 | ||
120 | 177 | # A new recipe can be created from the branch page. | 198 | # A new recipe can be created from the branch page. |
121 | 178 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) | 199 | browser = self.getUserBrowser(canonical_url(branch), user=self.chef) |
122 | @@ -241,6 +262,29 @@ | |||
123 | 241 | self.assertTextMatchesExpressionIgnoreWhitespace( | 262 | self.assertTextMatchesExpressionIgnoreWhitespace( |
124 | 242 | pattern, main_text) | 263 | pattern, main_text) |
125 | 243 | 264 | ||
126 | 265 | def test_edit_recipe_forbidden_instruction(self): | ||
127 | 266 | self.factory.makeDistroSeries( | ||
128 | 267 | displayname='Mumbly Midget', name='mumbly', | ||
129 | 268 | distribution=self.ppa.distribution) | ||
130 | 269 | product = self.factory.makeProduct( | ||
131 | 270 | name='ratatouille', displayname='Ratatouille') | ||
132 | 271 | veggie_branch = self.factory.makeBranch( | ||
133 | 272 | owner=self.chef, product=product, name='veggies') | ||
134 | 273 | recipe = self.factory.makeSourcePackageRecipe( | ||
135 | 274 | owner=self.chef, registrant=self.chef, | ||
136 | 275 | name=u'things', description=u'This is a recipe', | ||
137 | 276 | distroseries=self.squirrel, branches=[veggie_branch]) | ||
138 | 277 | |||
139 | 278 | browser = self.getUserBrowser(canonical_url(recipe), user=self.chef) | ||
140 | 279 | browser.getLink('Edit recipe').click() | ||
141 | 280 | browser.getControl('Recipe text').value = ( | ||
142 | 281 | browser.getControl('Recipe text').value + 'run cat /etc/passwd') | ||
143 | 282 | browser.getControl('Update Recipe').click() | ||
144 | 283 | |||
145 | 284 | self.assertEqual( | ||
146 | 285 | extract_text(find_tags_by_class(browser.contents, 'message')[1]), | ||
147 | 286 | 'The bzr-builder instruction "run" is not permitted here.') | ||
148 | 287 | |||
149 | 244 | def test_edit_recipe_already_exists(self): | 288 | def test_edit_recipe_already_exists(self): |
150 | 245 | self.factory.makeDistroSeries( | 289 | self.factory.makeDistroSeries( |
151 | 246 | displayname='Mumbly Midget', name='mumbly', | 290 | displayname='Mumbly Midget', name='mumbly', |
152 | @@ -255,7 +299,7 @@ | |||
153 | 255 | owner=self.chef, registrant=self.chef, | 299 | owner=self.chef, registrant=self.chef, |
154 | 256 | name=u'things', description=u'This is a recipe', | 300 | name=u'things', description=u'This is a recipe', |
155 | 257 | distroseries=self.squirrel, branches=[veggie_branch]) | 301 | distroseries=self.squirrel, branches=[veggie_branch]) |
157 | 258 | recipe_fings = self.factory.makeSourcePackageRecipe( | 302 | self.factory.makeSourcePackageRecipe( |
158 | 259 | owner=self.chef, registrant=self.chef, | 303 | owner=self.chef, registrant=self.chef, |
159 | 260 | name=u'fings', description=u'This is a recipe', | 304 | name=u'fings', description=u'This is a recipe', |
160 | 261 | distroseries=self.squirrel, branches=[veggie_branch]) | 305 | distroseries=self.squirrel, branches=[veggie_branch]) |
161 | 262 | 306 | ||
162 | === modified file 'lib/lp/code/errors.py' | |||
163 | --- lib/lp/code/errors.py 2010-05-26 17:16:01 +0000 | |||
164 | +++ lib/lp/code/errors.py 2010-06-11 19:07:30 +0000 | |||
165 | @@ -12,8 +12,11 @@ | |||
166 | 12 | 'CodeImportAlreadyRunning', | 12 | 'CodeImportAlreadyRunning', |
167 | 13 | 'CodeImportNotInReviewedState', | 13 | 'CodeImportNotInReviewedState', |
168 | 14 | 'ClaimReviewFailed', | 14 | 'ClaimReviewFailed', |
169 | 15 | 'ForbiddenInstruction', | ||
170 | 15 | 'InvalidBranchMergeProposal', | 16 | 'InvalidBranchMergeProposal', |
171 | 16 | 'ReviewNotPending', | 17 | 'ReviewNotPending', |
172 | 18 | 'TooManyBuilds', | ||
173 | 19 | 'TooNewRecipeFormat', | ||
174 | 17 | 'UnknownBranchTypeError', | 20 | 'UnknownBranchTypeError', |
175 | 18 | 'UserHasExistingReview', | 21 | 'UserHasExistingReview', |
176 | 19 | 'UserNotBranchReviewer', | 22 | 'UserNotBranchReviewer', |
177 | @@ -91,3 +94,32 @@ | |||
178 | 91 | """Raised when the user requests an import that is already running.""" | 94 | """Raised when the user requests an import that is already running.""" |
179 | 92 | 95 | ||
180 | 93 | webservice_error(400) | 96 | webservice_error(400) |
181 | 97 | |||
182 | 98 | |||
183 | 99 | class ForbiddenInstruction(Exception): | ||
184 | 100 | """A forbidden instruction was found in the recipe.""" | ||
185 | 101 | |||
186 | 102 | def __init__(self, instruction_name): | ||
187 | 103 | super(ForbiddenInstruction, self).__init__() | ||
188 | 104 | self.instruction_name = instruction_name | ||
189 | 105 | |||
190 | 106 | |||
191 | 107 | class TooNewRecipeFormat(Exception): | ||
192 | 108 | """The format of the recipe supplied was too new.""" | ||
193 | 109 | |||
194 | 110 | def __init__(self, supplied_format, newest_supported): | ||
195 | 111 | super(TooNewRecipeFormat, self).__init__() | ||
196 | 112 | self.supplied_format = supplied_format | ||
197 | 113 | self.newest_supported = newest_supported | ||
198 | 114 | |||
199 | 115 | |||
200 | 116 | class TooManyBuilds(Exception): | ||
201 | 117 | """A build was requested that exceeded the quota.""" | ||
202 | 118 | |||
203 | 119 | def __init__(self, recipe, distroseries): | ||
204 | 120 | self.recipe = recipe | ||
205 | 121 | self.distroseries = distroseries | ||
206 | 122 | msg = ( | ||
207 | 123 | 'You have exceeded your quota for recipe %s for distroseries %s' | ||
208 | 124 | % (self.recipe, distroseries)) | ||
209 | 125 | Exception.__init__(self, msg) | ||
210 | 94 | 126 | ||
211 | === modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py' | |||
212 | --- lib/lp/code/interfaces/sourcepackagerecipe.py 2010-06-04 16:14:54 +0000 | |||
213 | +++ lib/lp/code/interfaces/sourcepackagerecipe.py 2010-06-11 19:07:30 +0000 | |||
214 | @@ -10,13 +10,10 @@ | |||
215 | 10 | 10 | ||
216 | 11 | 11 | ||
217 | 12 | __all__ = [ | 12 | __all__ = [ |
218 | 13 | 'ForbiddenInstruction', | ||
219 | 14 | 'ISourcePackageRecipe', | 13 | 'ISourcePackageRecipe', |
220 | 15 | 'ISourcePackageRecipeData', | 14 | 'ISourcePackageRecipeData', |
221 | 16 | 'ISourcePackageRecipeSource', | 15 | 'ISourcePackageRecipeSource', |
222 | 17 | 'MINIMAL_RECIPE_TEXT', | 16 | 'MINIMAL_RECIPE_TEXT', |
223 | 18 | 'TooManyBuilds', | ||
224 | 19 | 'TooNewRecipeFormat', | ||
225 | 20 | ] | 17 | ] |
226 | 21 | 18 | ||
227 | 22 | 19 | ||
228 | @@ -47,34 +44,6 @@ | |||
229 | 47 | %s | 44 | %s |
230 | 48 | ''') | 45 | ''') |
231 | 49 | 46 | ||
232 | 50 | class ForbiddenInstruction(Exception): | ||
233 | 51 | """A forbidden instruction was found in the recipe.""" | ||
234 | 52 | |||
235 | 53 | def __init__(self, instruction_name): | ||
236 | 54 | super(ForbiddenInstruction, self).__init__() | ||
237 | 55 | self.instruction_name = instruction_name | ||
238 | 56 | |||
239 | 57 | |||
240 | 58 | class TooManyBuilds(Exception): | ||
241 | 59 | """A build was requested that exceeded the quota.""" | ||
242 | 60 | |||
243 | 61 | def __init__(self, recipe, distroseries): | ||
244 | 62 | self.recipe = recipe | ||
245 | 63 | self.distroseries = distroseries | ||
246 | 64 | msg = ( | ||
247 | 65 | 'You have exceeded your quota for recipe %s for distroseries %s' | ||
248 | 66 | % (self.recipe, distroseries)) | ||
249 | 67 | Exception.__init__(self, msg) | ||
250 | 68 | |||
251 | 69 | |||
252 | 70 | class TooNewRecipeFormat(Exception): | ||
253 | 71 | """The format of the recipe supplied was too new.""" | ||
254 | 72 | |||
255 | 73 | def __init__(self, supplied_format, newest_supported): | ||
256 | 74 | super(TooNewRecipeFormat, self).__init__() | ||
257 | 75 | self.supplied_format = supplied_format | ||
258 | 76 | self.newest_supported = newest_supported | ||
259 | 77 | |||
260 | 78 | 47 | ||
261 | 79 | class ISourcePackageRecipeData(Interface): | 48 | class ISourcePackageRecipeData(Interface): |
262 | 80 | """A recipe as database data, not text.""" | 49 | """A recipe as database data, not text.""" |
263 | 81 | 50 | ||
264 | === modified file 'lib/lp/code/model/sourcepackagerecipe.py' | |||
265 | --- lib/lp/code/model/sourcepackagerecipe.py 2010-06-10 09:55:32 +0000 | |||
266 | +++ lib/lp/code/model/sourcepackagerecipe.py 2010-06-11 19:07:30 +0000 | |||
267 | @@ -23,9 +23,10 @@ | |||
268 | 23 | from canonical.database.datetimecol import UtcDateTimeCol | 23 | from canonical.database.datetimecol import UtcDateTimeCol |
269 | 24 | from canonical.launchpad.interfaces.lpstorm import IMasterStore, IStore | 24 | from canonical.launchpad.interfaces.lpstorm import IMasterStore, IStore |
270 | 25 | 25 | ||
271 | 26 | from lp.code.errors import TooManyBuilds | ||
272 | 26 | from lp.code.interfaces.sourcepackagerecipe import ( | 27 | from lp.code.interfaces.sourcepackagerecipe import ( |
273 | 27 | ISourcePackageRecipe, ISourcePackageRecipeSource, | 28 | ISourcePackageRecipe, ISourcePackageRecipeSource, |
275 | 28 | ISourcePackageRecipeData, TooManyBuilds) | 29 | ISourcePackageRecipeData) |
276 | 29 | from lp.code.interfaces.sourcepackagerecipebuild import ( | 30 | from lp.code.interfaces.sourcepackagerecipebuild import ( |
277 | 30 | ISourcePackageRecipeBuildSource) | 31 | ISourcePackageRecipeBuildSource) |
278 | 31 | from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild | 32 | from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild |
279 | 32 | 33 | ||
280 | === modified file 'lib/lp/code/model/sourcepackagerecipedata.py' | |||
281 | --- lib/lp/code/model/sourcepackagerecipedata.py 2010-04-19 05:04:00 +0000 | |||
282 | +++ lib/lp/code/model/sourcepackagerecipedata.py 2010-06-11 19:07:30 +0000 | |||
283 | @@ -27,11 +27,10 @@ | |||
284 | 27 | from canonical.database.enumcol import EnumCol | 27 | from canonical.database.enumcol import EnumCol |
285 | 28 | from canonical.launchpad.interfaces.lpstorm import IStore | 28 | from canonical.launchpad.interfaces.lpstorm import IStore |
286 | 29 | 29 | ||
287 | 30 | from lp.code.errors import ForbiddenInstruction, TooNewRecipeFormat | ||
288 | 30 | from lp.code.model.branch import Branch | 31 | from lp.code.model.branch import Branch |
289 | 31 | from lp.code.interfaces.branch import NoSuchBranch | 32 | from lp.code.interfaces.branch import NoSuchBranch |
290 | 32 | from lp.code.interfaces.branchlookup import IBranchLookup | 33 | from lp.code.interfaces.branchlookup import IBranchLookup |
291 | 33 | from lp.code.interfaces.sourcepackagerecipe import ( | ||
292 | 34 | ForbiddenInstruction, TooNewRecipeFormat) | ||
293 | 35 | 34 | ||
294 | 36 | 35 | ||
295 | 37 | class InstructionType(DBEnumeratedType): | 36 | class InstructionType(DBEnumeratedType): |
296 | 38 | 37 | ||
297 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py' | |||
298 | --- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-06-10 09:55:32 +0000 | |||
299 | +++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-06-11 19:07:30 +0000 | |||
300 | @@ -29,9 +29,10 @@ | |||
301 | 29 | InvalidPocketForPPA) | 29 | InvalidPocketForPPA) |
302 | 30 | from lp.buildmaster.interfaces.buildqueue import IBuildQueue | 30 | from lp.buildmaster.interfaces.buildqueue import IBuildQueue |
303 | 31 | from lp.buildmaster.model.buildqueue import BuildQueue | 31 | from lp.buildmaster.model.buildqueue import BuildQueue |
304 | 32 | from lp.code.errors import ( | ||
305 | 33 | ForbiddenInstruction, TooManyBuilds, TooNewRecipeFormat) | ||
306 | 32 | from lp.code.interfaces.sourcepackagerecipe import ( | 34 | from lp.code.interfaces.sourcepackagerecipe import ( |
309 | 33 | ForbiddenInstruction, ISourcePackageRecipe, ISourcePackageRecipeSource, | 35 | ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT) |
308 | 34 | TooManyBuilds, TooNewRecipeFormat, MINIMAL_RECIPE_TEXT) | ||
310 | 35 | from lp.code.interfaces.sourcepackagerecipebuild import ( | 36 | from lp.code.interfaces.sourcepackagerecipebuild import ( |
311 | 36 | ISourcePackageRecipeBuild, ISourcePackageRecipeBuildJob) | 37 | ISourcePackageRecipeBuild, ISourcePackageRecipeBuildJob) |
312 | 37 | from lp.code.model.sourcepackagerecipebuild import ( | 38 | from lp.code.model.sourcepackagerecipebuild import ( |
ForbiddenInstru ction is being imported from interfaces. sourcepakagerec ipe. Can we put that in lp.code.errors
lp.code.
instead?
> except ForbiddenInstru ction:
> self.setFieldError(
> 'recipe_text',
> 'The bzr-builder instruction "run" is not permitted here.')
Can we get the instruction out of the forbidden instruction exception? That
makes more sense than hard coding it. This may cause unnecessary confusion if
they have a different forbidden instruction.
Also, you should do the check in the validate method, not in the action.