Merge lp:~abentley/launchpad/permit-commands into lp:launchpad
- permit-commands
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 11517 |
Proposed branch: | lp:~abentley/launchpad/permit-commands |
Merge into: | lp:launchpad |
Prerequisite: | lp:~abentley/launchpad/recipe-interfaces |
Diff against target: |
494 lines (+68/-102) 9 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+6/-8) lib/lp/code/errors.py (+0/-9) lib/lp/code/interfaces/sourcepackagerecipe.py (+2/-2) lib/lp/code/model/sourcepackagerecipe.py (+7/-10) lib/lp/code/model/sourcepackagerecipedata.py (+10/-8) lib/lp/code/model/tests/test_sourcepackagerecipe.py (+36/-59) lib/lp/registry/model/person.py (+1/-3) lib/lp/testing/factory.py (+5/-2) utilities/sourcedeps.conf (+1/-1) |
To merge this branch: | bzr merge lp:~abentley/launchpad/permit-commands |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Hummer (community) | Approve | ||
Review via email: mp+34476@code.launchpad.net |
Commit message
Update forbidden command handling to use bzr-builder facilities.
Description of the change
= Summary =
Update launchpad to use bzr-builder's facilities for using only safe
instructions.
== Proposed fix ==
Provide permitted_
== Pre-implementation notes ==
None
== Implementation details ==
Several things are needed for this change. Crucially, there should be a single
codepath that is used to parse recipe text, so that no one can forget to supply
permitted_
From this, it made sense to change the constructor of SourcePackageRecipe to
take unparsed recipe text as instead of a parsed "builder recipe" as an
argument.
This required changing many tests.
Additionally, the check for forbidden instructions now happens at parse time,
instead of at as a later stage.
Additionally, tests were changed to use
LaunchpadObject
tests, it was simpler to use LaunchpadObject
makeRecipe.
== Tests ==
bin/tests test_sourcepack
== Demo and Q/A ==
None
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
utilities/
lib/lp/
lib/lp/
lib/lp/
./lib/lp/
174: E202 whitespace before ')'
./lib/lp/
142: E231 missing whitespace after ','
172: E231 missing whitespace after ','
180: E231 missing whitespace after ','
./lib/lp/
243: E501 line too long (80 characters)
256: E231 missing whitespace after ','
280: E231 missing whitespace after ','
288: E231 missing whitespace after ','
295: E231 missing whitespace after ','
303: E231 missing whitespace after ','
341: E231 missing whitespace after ','
401: E231 missing whitespace after ','
243: Line exceeds 78 characters.
./lib/lp/
163: E231 missing whitespace after ','
271: E202 whitespace before ']'
381: E231 missing whitespace after ','
./lib/lp/
77: E231 missing whitespace after ','
121: E202 whitespace before ')'
121: E231 missing whitespace after ','
150: E302 expected 2 blank lines, found 1
./lib/lp/
1617: E301 expected 1 blank line, found 0
1624: E301 expected 1 blank line, found 0
Preview Diff
1 | === modified file 'lib/lp/code/browser/sourcepackagerecipe.py' | |||
2 | --- lib/lp/code/browser/sourcepackagerecipe.py 2010-08-23 02:07:45 +0000 | |||
3 | +++ lib/lp/code/browser/sourcepackagerecipe.py 2010-09-09 13:41:57 +0000 | |||
4 | @@ -16,6 +16,7 @@ | |||
5 | 16 | 16 | ||
6 | 17 | 17 | ||
7 | 18 | from bzrlib.plugins.builder.recipe import ( | 18 | from bzrlib.plugins.builder.recipe import ( |
8 | 19 | ForbiddenInstructionError, | ||
9 | 19 | RecipeParseError, | 20 | RecipeParseError, |
10 | 20 | RecipeParser, | 21 | RecipeParser, |
11 | 21 | ) | 22 | ) |
12 | @@ -57,7 +58,6 @@ | |||
13 | 57 | from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget | 58 | from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget |
14 | 58 | from lp.code.errors import ( | 59 | from lp.code.errors import ( |
15 | 59 | BuildAlreadyPending, | 60 | BuildAlreadyPending, |
16 | 60 | ForbiddenInstruction, | ||
17 | 61 | NoSuchBranch, | 61 | NoSuchBranch, |
18 | 62 | PrivateBranchRecipe, | 62 | PrivateBranchRecipe, |
19 | 63 | ) | 63 | ) |
20 | @@ -326,16 +326,14 @@ | |||
21 | 326 | 326 | ||
22 | 327 | @action('Create Recipe', name='create') | 327 | @action('Create Recipe', name='create') |
23 | 328 | def request_action(self, action, data): | 328 | def request_action(self, action, data): |
24 | 329 | parser = RecipeParser(data['recipe_text']) | ||
25 | 330 | recipe = parser.parse() | ||
26 | 331 | try: | 329 | try: |
27 | 332 | source_package_recipe = getUtility( | 330 | source_package_recipe = getUtility( |
28 | 333 | ISourcePackageRecipeSource).new( | 331 | ISourcePackageRecipeSource).new( |
31 | 334 | self.user, data['owner'], data['name'], recipe, | 332 | self.user, data['owner'], data['name'], |
32 | 335 | data['description'], data['distros'], | 333 | data['recipe_text'], data['description'], data['distros'], |
33 | 336 | data['daily_build_archive'], data['build_daily']) | 334 | data['daily_build_archive'], data['build_daily']) |
34 | 337 | Store.of(source_package_recipe).flush() | 335 | Store.of(source_package_recipe).flush() |
36 | 338 | except ForbiddenInstruction: | 336 | except ForbiddenInstructionError: |
37 | 339 | # XXX: bug=592513 We shouldn't be hardcoding "run" here. | 337 | # XXX: bug=592513 We shouldn't be hardcoding "run" here. |
38 | 340 | self.setFieldError( | 338 | self.setFieldError( |
39 | 341 | 'recipe_text', | 339 | 'recipe_text', |
40 | @@ -397,9 +395,9 @@ | |||
41 | 397 | recipe = parser.parse() | 395 | recipe = parser.parse() |
42 | 398 | if self.context.builder_recipe != recipe: | 396 | if self.context.builder_recipe != recipe: |
43 | 399 | try: | 397 | try: |
45 | 400 | self.context.builder_recipe = recipe | 398 | self.context.setRecipeText(recipe_text) |
46 | 401 | changed = True | 399 | changed = True |
48 | 402 | except ForbiddenInstruction: | 400 | except ForbiddenInstructionError: |
49 | 403 | # XXX: bug=592513 We shouldn't be hardcoding "run" here. | 401 | # XXX: bug=592513 We shouldn't be hardcoding "run" here. |
50 | 404 | self.setFieldError( | 402 | self.setFieldError( |
51 | 405 | 'recipe_text', | 403 | 'recipe_text', |
52 | 406 | 404 | ||
53 | === modified file 'lib/lp/code/errors.py' | |||
54 | --- lib/lp/code/errors.py 2010-08-03 03:43:33 +0000 | |||
55 | +++ lib/lp/code/errors.py 2010-09-09 13:41:57 +0000 | |||
56 | @@ -25,7 +25,6 @@ | |||
57 | 25 | 'CodeImportAlreadyRunning', | 25 | 'CodeImportAlreadyRunning', |
58 | 26 | 'CodeImportNotInReviewedState', | 26 | 'CodeImportNotInReviewedState', |
59 | 27 | 'ClaimReviewFailed', | 27 | 'ClaimReviewFailed', |
60 | 28 | 'ForbiddenInstruction', | ||
61 | 29 | 'InvalidBranchMergeProposal', | 28 | 'InvalidBranchMergeProposal', |
62 | 30 | 'InvalidNamespace', | 29 | 'InvalidNamespace', |
63 | 31 | 'NoLinkedBranch', | 30 | 'NoLinkedBranch', |
64 | @@ -242,14 +241,6 @@ | |||
65 | 242 | webservice_error(400) | 241 | webservice_error(400) |
66 | 243 | 242 | ||
67 | 244 | 243 | ||
68 | 245 | class ForbiddenInstruction(Exception): | ||
69 | 246 | """A forbidden instruction was found in the recipe.""" | ||
70 | 247 | |||
71 | 248 | def __init__(self, instruction_name): | ||
72 | 249 | super(ForbiddenInstruction, self).__init__() | ||
73 | 250 | self.instruction_name = instruction_name | ||
74 | 251 | |||
75 | 252 | |||
76 | 253 | class TooNewRecipeFormat(Exception): | 244 | class TooNewRecipeFormat(Exception): |
77 | 254 | """The format of the recipe supplied was too new.""" | 245 | """The format of the recipe supplied was too new.""" |
78 | 255 | 246 | ||
79 | 256 | 247 | ||
80 | === modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py' | |||
81 | --- lib/lp/code/interfaces/sourcepackagerecipe.py 2010-09-09 13:41:38 +0000 | |||
82 | +++ lib/lp/code/interfaces/sourcepackagerecipe.py 2010-09-09 13:41:57 +0000 | |||
83 | @@ -93,8 +93,6 @@ | |||
84 | 93 | required=True, readonly=True, | 93 | required=True, readonly=True, |
85 | 94 | vocabulary='ValidPersonOrTeam')) | 94 | vocabulary='ValidPersonOrTeam')) |
86 | 95 | 95 | ||
87 | 96 | is_stale = Bool(title=_('Recipe is stale.')) | ||
88 | 97 | |||
89 | 98 | recipe_text = exported(Text()) | 96 | recipe_text = exported(Text()) |
90 | 99 | 97 | ||
91 | 100 | def isOverQuota(requester, distroseries): | 98 | def isOverQuota(requester, distroseries): |
92 | @@ -183,6 +181,8 @@ | |||
93 | 183 | 181 | ||
94 | 184 | date_last_modified = Datetime(required=True, readonly=True) | 182 | date_last_modified = Datetime(required=True, readonly=True) |
95 | 185 | 183 | ||
96 | 184 | is_stale = Bool(title=_('Recipe is stale.')) | ||
97 | 185 | |||
98 | 186 | 186 | ||
99 | 187 | class ISourcePackageRecipe(ISourcePackageRecipeData, | 187 | class ISourcePackageRecipe(ISourcePackageRecipeData, |
100 | 188 | ISourcePackageRecipeEdit, ISourcePackageRecipeEditableAttributes, | 188 | ISourcePackageRecipeEdit, ISourcePackageRecipeEditableAttributes, |
101 | 189 | 189 | ||
102 | === modified file 'lib/lp/code/model/sourcepackagerecipe.py' | |||
103 | --- lib/lp/code/model/sourcepackagerecipe.py 2010-09-01 03:25:36 +0000 | |||
104 | +++ lib/lp/code/model/sourcepackagerecipe.py 2010-09-09 13:41:57 +0000 | |||
105 | @@ -11,7 +11,6 @@ | |||
106 | 11 | 'SourcePackageRecipe', | 11 | 'SourcePackageRecipe', |
107 | 12 | ] | 12 | ] |
108 | 13 | 13 | ||
109 | 14 | from bzrlib.plugins.builder.recipe import RecipeParser | ||
110 | 15 | from lazr.delegates import delegates | 14 | from lazr.delegates import delegates |
111 | 16 | from storm.locals import ( | 15 | from storm.locals import ( |
112 | 17 | Bool, | 16 | Bool, |
113 | @@ -139,33 +138,30 @@ | |||
114 | 139 | SourcePackageRecipeData, | 138 | SourcePackageRecipeData, |
115 | 140 | SourcePackageRecipeData.sourcepackage_recipe == self).one() | 139 | SourcePackageRecipeData.sourcepackage_recipe == self).one() |
116 | 141 | 140 | ||
118 | 142 | def _get_builder_recipe(self): | 141 | @property |
119 | 142 | def builder_recipe(self): | ||
120 | 143 | """Accesses of the recipe go to the SourcePackageRecipeData.""" | 143 | """Accesses of the recipe go to the SourcePackageRecipeData.""" |
121 | 144 | return self._recipe_data.getRecipe() | 144 | return self._recipe_data.getRecipe() |
122 | 145 | 145 | ||
123 | 146 | def _set_builder_recipe(self, value): | ||
124 | 147 | """Setting of the recipe goes to the SourcePackageRecipeData.""" | ||
125 | 148 | self._recipe_data.setRecipe(value) | ||
126 | 149 | |||
127 | 150 | builder_recipe = property(_get_builder_recipe, _set_builder_recipe) | ||
128 | 151 | |||
129 | 152 | @property | 146 | @property |
130 | 153 | def base_branch(self): | 147 | def base_branch(self): |
131 | 154 | return self._recipe_data.base_branch | 148 | return self._recipe_data.base_branch |
132 | 155 | 149 | ||
133 | 156 | def setRecipeText(self, recipe_text): | 150 | def setRecipeText(self, recipe_text): |
135 | 157 | self.builder_recipe = RecipeParser(recipe_text).parse() | 151 | parsed = SourcePackageRecipeData.getParsedRecipe(recipe_text) |
136 | 152 | self._recipe_data.setRecipe(parsed) | ||
137 | 158 | 153 | ||
138 | 159 | @property | 154 | @property |
139 | 160 | def recipe_text(self): | 155 | def recipe_text(self): |
140 | 161 | return str(self.builder_recipe) | 156 | return str(self.builder_recipe) |
141 | 162 | 157 | ||
142 | 163 | @staticmethod | 158 | @staticmethod |
144 | 164 | def new(registrant, owner, name, builder_recipe, description, | 159 | def new(registrant, owner, name, recipe, description, |
145 | 165 | distroseries=None, daily_build_archive=None, build_daily=False): | 160 | distroseries=None, daily_build_archive=None, build_daily=False): |
146 | 166 | """See `ISourcePackageRecipeSource.new`.""" | 161 | """See `ISourcePackageRecipeSource.new`.""" |
147 | 167 | store = IMasterStore(SourcePackageRecipe) | 162 | store = IMasterStore(SourcePackageRecipe) |
148 | 168 | sprecipe = SourcePackageRecipe() | 163 | sprecipe = SourcePackageRecipe() |
149 | 164 | builder_recipe = SourcePackageRecipeData.getParsedRecipe(recipe) | ||
150 | 169 | SourcePackageRecipeData(builder_recipe, sprecipe) | 165 | SourcePackageRecipeData(builder_recipe, sprecipe) |
151 | 170 | sprecipe.registrant = registrant | 166 | sprecipe.registrant = registrant |
152 | 171 | sprecipe.owner = owner | 167 | sprecipe.owner = owner |
153 | @@ -201,6 +197,7 @@ | |||
154 | 201 | store = Store.of(self) | 197 | store = Store.of(self) |
155 | 202 | self.distroseries.clear() | 198 | self.distroseries.clear() |
156 | 203 | self._recipe_data.instructions.find().remove() | 199 | self._recipe_data.instructions.find().remove() |
157 | 200 | |||
158 | 204 | def destroyBuilds(pending): | 201 | def destroyBuilds(pending): |
159 | 205 | builds = self.getBuilds(pending=pending) | 202 | builds = self.getBuilds(pending=pending) |
160 | 206 | for build in builds: | 203 | for build in builds: |
161 | 207 | 204 | ||
162 | === modified file 'lib/lp/code/model/sourcepackagerecipedata.py' | |||
163 | --- lib/lp/code/model/sourcepackagerecipedata.py 2010-08-20 20:31:18 +0000 | |||
164 | +++ lib/lp/code/model/sourcepackagerecipedata.py 2010-09-09 13:41:57 +0000 | |||
165 | @@ -18,6 +18,8 @@ | |||
166 | 18 | MergeInstruction, | 18 | MergeInstruction, |
167 | 19 | NestInstruction, | 19 | NestInstruction, |
168 | 20 | RecipeBranch, | 20 | RecipeBranch, |
169 | 21 | RecipeParser, | ||
170 | 22 | SAFE_INSTRUCTIONS, | ||
171 | 21 | ) | 23 | ) |
172 | 22 | from lazr.enum import ( | 24 | from lazr.enum import ( |
173 | 23 | DBEnumeratedType, | 25 | DBEnumeratedType, |
174 | @@ -39,7 +41,6 @@ | |||
175 | 39 | from canonical.database.enumcol import EnumCol | 41 | from canonical.database.enumcol import EnumCol |
176 | 40 | from canonical.launchpad.interfaces.lpstorm import IStore | 42 | from canonical.launchpad.interfaces.lpstorm import IStore |
177 | 41 | from lp.code.errors import ( | 43 | from lp.code.errors import ( |
178 | 42 | ForbiddenInstruction, | ||
179 | 43 | NoSuchBranch, | 44 | NoSuchBranch, |
180 | 44 | PrivateBranchRecipe, | 45 | PrivateBranchRecipe, |
181 | 45 | TooNewRecipeFormat, | 46 | TooNewRecipeFormat, |
182 | @@ -151,6 +152,11 @@ | |||
183 | 151 | sourcepackage_recipe_build_id, 'SourcePackageRecipeBuild.id') | 152 | sourcepackage_recipe_build_id, 'SourcePackageRecipeBuild.id') |
184 | 152 | 153 | ||
185 | 153 | @staticmethod | 154 | @staticmethod |
186 | 155 | def getParsedRecipe(recipe_text): | ||
187 | 156 | parser = RecipeParser(recipe_text) | ||
188 | 157 | return parser.parse(permitted_instructions=SAFE_INSTRUCTIONS) | ||
189 | 158 | |||
190 | 159 | @staticmethod | ||
191 | 154 | def findRecipes(branch): | 160 | def findRecipes(branch): |
192 | 155 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe | 161 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe |
193 | 156 | store = Store.of(branch) | 162 | store = Store.of(branch) |
194 | @@ -179,10 +185,9 @@ | |||
195 | 179 | with. | 185 | with. |
196 | 180 | :return: an instance of SourcePackageRecipeData. | 186 | :return: an instance of SourcePackageRecipeData. |
197 | 181 | """ | 187 | """ |
202 | 182 | from bzrlib.plugins.builder.recipe import RecipeParser | 188 | parsed = cls.getParsedRecipe(text) |
203 | 183 | parser = RecipeParser(text) | 189 | return cls( |
204 | 184 | return cls(parser.parse(), | 190 | parsed, sourcepackage_recipe_build=sourcepackage_recipe_build) |
201 | 185 | sourcepackage_recipe_build=sourcepackage_recipe_build) | ||
205 | 186 | 191 | ||
206 | 187 | def getRecipe(self): | 192 | def getRecipe(self): |
207 | 188 | """The BaseRecipeBranch version of the recipe.""" | 193 | """The BaseRecipeBranch version of the recipe.""" |
208 | @@ -213,9 +218,6 @@ | |||
209 | 213 | """ | 218 | """ |
210 | 214 | r = {} | 219 | r = {} |
211 | 215 | for instruction in recipe_branch.child_branches: | 220 | for instruction in recipe_branch.child_branches: |
212 | 216 | if not (isinstance(instruction, MergeInstruction) or | ||
213 | 217 | isinstance(instruction, NestInstruction)): | ||
214 | 218 | raise ForbiddenInstruction(str(instruction)) | ||
215 | 219 | db_branch = getUtility(IBranchLookup).getByUrl( | 221 | db_branch = getUtility(IBranchLookup).getByUrl( |
216 | 220 | instruction.recipe_branch.url) | 222 | instruction.recipe_branch.url) |
217 | 221 | if db_branch is None: | 223 | if db_branch is None: |
218 | 222 | 224 | ||
219 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py' | |||
220 | --- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-09-09 13:41:38 +0000 | |||
221 | +++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-09-09 13:41:57 +0000 | |||
222 | @@ -14,7 +14,9 @@ | |||
223 | 14 | import textwrap | 14 | import textwrap |
224 | 15 | import unittest | 15 | import unittest |
225 | 16 | 16 | ||
227 | 17 | from bzrlib.plugins.builder.recipe import RecipeParser | 17 | from bzrlib.plugins.builder.recipe import ( |
228 | 18 | ForbiddenInstructionError, | ||
229 | 19 | ) | ||
230 | 18 | from pytz import UTC | 20 | from pytz import UTC |
231 | 19 | from storm.locals import Store | 21 | from storm.locals import Store |
232 | 20 | import transaction | 22 | import transaction |
233 | @@ -33,7 +35,6 @@ | |||
234 | 33 | from lp.buildmaster.model.buildqueue import BuildQueue | 35 | from lp.buildmaster.model.buildqueue import BuildQueue |
235 | 34 | from lp.code.errors import ( | 36 | from lp.code.errors import ( |
236 | 35 | BuildAlreadyPending, | 37 | BuildAlreadyPending, |
237 | 36 | ForbiddenInstruction, | ||
238 | 37 | PrivateBranchRecipe, | 38 | PrivateBranchRecipe, |
239 | 38 | TooManyBuilds, | 39 | TooManyBuilds, |
240 | 39 | TooNewRecipeFormat, | 40 | TooNewRecipeFormat, |
241 | @@ -84,18 +85,6 @@ | |||
242 | 84 | recipe = self.factory.makeSourcePackageRecipe() | 85 | recipe = self.factory.makeSourcePackageRecipe() |
243 | 85 | verifyObject(ISourcePackageRecipe, recipe) | 86 | verifyObject(ISourcePackageRecipe, recipe) |
244 | 86 | 87 | ||
245 | 87 | def makeSourcePackageRecipeFromBuilderRecipe(self, builder_recipe): | ||
246 | 88 | """Make a SourcePackageRecipe from a recipe with arbitrary other data. | ||
247 | 89 | """ | ||
248 | 90 | registrant = self.factory.makePerson() | ||
249 | 91 | owner = self.factory.makeTeam(owner=registrant) | ||
250 | 92 | distroseries = self.factory.makeDistroSeries() | ||
251 | 93 | name = self.factory.getUniqueString(u'recipe-name') | ||
252 | 94 | description = self.factory.getUniqueString(u'recipe-description') | ||
253 | 95 | return getUtility(ISourcePackageRecipeSource).new( | ||
254 | 96 | registrant=registrant, owner=owner, distroseries=[distroseries], | ||
255 | 97 | name=name, description=description, builder_recipe=builder_recipe) | ||
256 | 98 | |||
257 | 99 | def makeRecipeComponents(self, branches=()): | 88 | def makeRecipeComponents(self, branches=()): |
258 | 100 | """Return a dict of values that can be used to make a recipe. | 89 | """Return a dict of values that can be used to make a recipe. |
259 | 101 | 90 | ||
260 | @@ -110,7 +99,7 @@ | |||
261 | 110 | distroseries = [self.factory.makeDistroSeries()], | 99 | distroseries = [self.factory.makeDistroSeries()], |
262 | 111 | name = self.factory.getUniqueString(u'recipe-name'), | 100 | name = self.factory.getUniqueString(u'recipe-name'), |
263 | 112 | description = self.factory.getUniqueString(u'recipe-description'), | 101 | description = self.factory.getUniqueString(u'recipe-description'), |
265 | 113 | builder_recipe = self.factory.makeRecipe(*branches)) | 102 | recipe = self.factory.makeRecipeText(*branches)) |
266 | 114 | 103 | ||
267 | 115 | def test_creation(self): | 104 | def test_creation(self): |
268 | 116 | # The metadata supplied when a SourcePackageRecipe is created is | 105 | # The metadata supplied when a SourcePackageRecipe is created is |
269 | @@ -174,8 +163,7 @@ | |||
270 | 174 | 163 | ||
271 | 175 | def test_recipe_implements_interface(self): | 164 | def test_recipe_implements_interface(self): |
272 | 176 | # SourcePackageRecipe objects implement ISourcePackageRecipe. | 165 | # SourcePackageRecipe objects implement ISourcePackageRecipe. |
275 | 177 | recipe = self.makeSourcePackageRecipeFromBuilderRecipe( | 166 | recipe = self.factory.makeSourcePackageRecipe() |
274 | 178 | self.factory.makeRecipe()) | ||
276 | 179 | transaction.commit() | 167 | transaction.commit() |
277 | 180 | with person_logged_in(recipe.owner): | 168 | with person_logged_in(recipe.owner): |
278 | 181 | self.assertProvides(recipe, ISourcePackageRecipe) | 169 | self.assertProvides(recipe, ISourcePackageRecipe) |
279 | @@ -183,9 +171,7 @@ | |||
280 | 183 | def test_base_branch(self): | 171 | def test_base_branch(self): |
281 | 184 | # When a recipe is created, we can access its base branch. | 172 | # When a recipe is created, we can access its base branch. |
282 | 185 | branch = self.factory.makeAnyBranch() | 173 | branch = self.factory.makeAnyBranch() |
286 | 186 | builder_recipe = self.factory.makeRecipe(branch) | 174 | sp_recipe = self.factory.makeSourcePackageRecipe(branches=[branch]) |
284 | 187 | sp_recipe = self.makeSourcePackageRecipeFromBuilderRecipe( | ||
285 | 188 | builder_recipe) | ||
287 | 189 | transaction.commit() | 175 | transaction.commit() |
288 | 190 | self.assertEquals(branch, sp_recipe.base_branch) | 176 | self.assertEquals(branch, sp_recipe.base_branch) |
289 | 191 | 177 | ||
290 | @@ -193,9 +179,8 @@ | |||
291 | 193 | # When a recipe is created, we can query it for links to the branch | 179 | # When a recipe is created, we can query it for links to the branch |
292 | 194 | # it references. | 180 | # it references. |
293 | 195 | branch = self.factory.makeAnyBranch() | 181 | branch = self.factory.makeAnyBranch() |
297 | 196 | builder_recipe = self.factory.makeRecipe(branch) | 182 | sp_recipe = self.factory.makeSourcePackageRecipe( |
298 | 197 | sp_recipe = self.makeSourcePackageRecipeFromBuilderRecipe( | 183 | branches=[branch]) |
296 | 198 | builder_recipe) | ||
299 | 199 | transaction.commit() | 184 | transaction.commit() |
300 | 200 | self.assertEquals([branch], list(sp_recipe.getReferencedBranches())) | 185 | self.assertEquals([branch], list(sp_recipe.getReferencedBranches())) |
301 | 201 | 186 | ||
302 | @@ -204,9 +189,8 @@ | |||
303 | 204 | # returns all of them. | 189 | # returns all of them. |
304 | 205 | branch1 = self.factory.makeAnyBranch() | 190 | branch1 = self.factory.makeAnyBranch() |
305 | 206 | branch2 = self.factory.makeAnyBranch() | 191 | branch2 = self.factory.makeAnyBranch() |
309 | 207 | builder_recipe = self.factory.makeRecipe(branch1, branch2) | 192 | sp_recipe = self.factory.makeSourcePackageRecipe( |
310 | 208 | sp_recipe = self.makeSourcePackageRecipeFromBuilderRecipe( | 193 | branches=[branch1, branch2]) |
308 | 209 | builder_recipe) | ||
311 | 210 | transaction.commit() | 194 | transaction.commit() |
312 | 211 | self.assertEquals( | 195 | self.assertEquals( |
313 | 212 | sorted([branch1, branch2]), | 196 | sorted([branch1, branch2]), |
314 | @@ -215,27 +199,23 @@ | |||
315 | 215 | def test_random_user_cant_edit(self): | 199 | def test_random_user_cant_edit(self): |
316 | 216 | # An arbitrary user can't set attributes. | 200 | # An arbitrary user can't set attributes. |
317 | 217 | branch1 = self.factory.makeAnyBranch() | 201 | branch1 = self.factory.makeAnyBranch() |
323 | 218 | builder_recipe1 = self.factory.makeRecipe(branch1) | 202 | recipe_1 = self.factory.makeRecipeText(branch1) |
324 | 219 | sp_recipe = self.makeSourcePackageRecipeFromBuilderRecipe( | 203 | sp_recipe = self.factory.makeSourcePackageRecipe( |
325 | 220 | builder_recipe1) | 204 | recipe=recipe_1) |
321 | 221 | branch2 = self.factory.makeAnyBranch() | ||
322 | 222 | builder_recipe2 = self.factory.makeRecipe(branch2) | ||
326 | 223 | login_person(self.factory.makePerson()) | 205 | login_person(self.factory.makePerson()) |
327 | 224 | self.assertRaises( | 206 | self.assertRaises( |
330 | 225 | Unauthorized, setattr, sp_recipe, 'builder_recipe', | 207 | Unauthorized, getattr, sp_recipe, 'setRecipeText') |
329 | 226 | builder_recipe2) | ||
331 | 227 | 208 | ||
332 | 228 | def test_set_recipe_text_resets_branch_references(self): | 209 | def test_set_recipe_text_resets_branch_references(self): |
333 | 229 | # When the recipe_text is replaced, getReferencedBranches returns | 210 | # When the recipe_text is replaced, getReferencedBranches returns |
334 | 230 | # (only) the branches referenced by the new recipe. | 211 | # (only) the branches referenced by the new recipe. |
335 | 231 | branch1 = self.factory.makeAnyBranch() | 212 | branch1 = self.factory.makeAnyBranch() |
339 | 232 | builder_recipe1 = self.factory.makeRecipe(branch1) | 213 | sp_recipe = self.factory.makeSourcePackageRecipe( |
340 | 233 | sp_recipe = self.makeSourcePackageRecipeFromBuilderRecipe( | 214 | branches=[branch1]) |
338 | 234 | builder_recipe1) | ||
341 | 235 | branch2 = self.factory.makeAnyBranch() | 215 | branch2 = self.factory.makeAnyBranch() |
345 | 236 | builder_recipe2 = self.factory.makeRecipe(branch2) | 216 | new_recipe = self.factory.makeRecipeText(branch2) |
346 | 237 | login_person(sp_recipe.owner.teamowner) | 217 | with person_logged_in(sp_recipe.owner): |
347 | 238 | sp_recipe.builder_recipe = builder_recipe2 | 218 | sp_recipe.setRecipeText(new_recipe) |
348 | 239 | self.assertEquals([branch2], list(sp_recipe.getReferencedBranches())) | 219 | self.assertEquals([branch2], list(sp_recipe.getReferencedBranches())) |
349 | 240 | 220 | ||
350 | 241 | def test_rejects_run_command(self): | 221 | def test_rejects_run_command(self): |
351 | @@ -244,36 +224,32 @@ | |||
352 | 244 | %(base)s | 224 | %(base)s |
353 | 245 | run touch test | 225 | run touch test |
354 | 246 | ''' % dict(base=self.factory.makeAnyBranch().bzr_identity) | 226 | ''' % dict(base=self.factory.makeAnyBranch().bzr_identity) |
357 | 247 | parser = RecipeParser(textwrap.dedent(recipe_text)) | 227 | recipe_text = textwrap.dedent(recipe_text) |
356 | 248 | builder_recipe = parser.parse() | ||
358 | 249 | self.assertRaises( | 228 | self.assertRaises( |
361 | 250 | ForbiddenInstruction, | 229 | ForbiddenInstructionError, self.factory.makeSourcePackageRecipe, |
362 | 251 | self.makeSourcePackageRecipeFromBuilderRecipe, builder_recipe) | 230 | recipe=recipe_text) |
363 | 252 | 231 | ||
364 | 253 | def test_run_rejected_without_mangling_recipe(self): | 232 | def test_run_rejected_without_mangling_recipe(self): |
369 | 254 | branch1 = self.factory.makeAnyBranch() | 233 | sp_recipe = self.factory.makeSourcePackageRecipe() |
370 | 255 | builder_recipe1 = self.factory.makeRecipe(branch1) | 234 | old_branches = list(sp_recipe.getReferencedBranches()) |
367 | 256 | sp_recipe = self.makeSourcePackageRecipeFromBuilderRecipe( | ||
368 | 257 | builder_recipe1) | ||
371 | 258 | recipe_text = '''\ | 235 | recipe_text = '''\ |
372 | 259 | # bzr-builder format 0.2 deb-version 0.1-{revno} | 236 | # bzr-builder format 0.2 deb-version 0.1-{revno} |
373 | 260 | %(base)s | 237 | %(base)s |
374 | 261 | run touch test | 238 | run touch test |
375 | 262 | ''' % dict(base=self.factory.makeAnyBranch().bzr_identity) | 239 | ''' % dict(base=self.factory.makeAnyBranch().bzr_identity) |
383 | 263 | parser = RecipeParser(textwrap.dedent(recipe_text)) | 240 | recipe_text = textwrap.dedent(recipe_text) |
384 | 264 | builder_recipe2 = parser.parse() | 241 | with person_logged_in(sp_recipe.owner): |
385 | 265 | login_person(sp_recipe.owner.teamowner) | 242 | self.assertRaises( |
386 | 266 | self.assertRaises( | 243 | ForbiddenInstructionError, sp_recipe.setRecipeText, recipe_text) |
387 | 267 | ForbiddenInstruction, setattr, sp_recipe, 'builder_recipe', | 244 | self.assertEquals( |
388 | 268 | builder_recipe2) | 245 | old_branches, list(sp_recipe.getReferencedBranches())) |
382 | 269 | self.assertEquals([branch1], list(sp_recipe.getReferencedBranches())) | ||
389 | 270 | 246 | ||
390 | 271 | def test_reject_newer_formats(self): | 247 | def test_reject_newer_formats(self): |
391 | 272 | builder_recipe = self.factory.makeRecipe() | 248 | builder_recipe = self.factory.makeRecipe() |
392 | 273 | builder_recipe.format = 0.3 | 249 | builder_recipe.format = 0.3 |
393 | 274 | self.assertRaises( | 250 | self.assertRaises( |
394 | 275 | TooNewRecipeFormat, | 251 | TooNewRecipeFormat, |
396 | 276 | self.makeSourcePackageRecipeFromBuilderRecipe, builder_recipe) | 252 | self.factory.makeSourcePackageRecipe, recipe=str(builder_recipe)) |
397 | 277 | 253 | ||
398 | 278 | def test_requestBuild(self): | 254 | def test_requestBuild(self): |
399 | 279 | recipe = self.factory.makeSourcePackageRecipe() | 255 | recipe = self.factory.makeSourcePackageRecipe() |
400 | @@ -503,6 +479,7 @@ | |||
401 | 503 | SourcePackageRecipe.findStaleDailyBuilds()) | 479 | SourcePackageRecipe.findStaleDailyBuilds()) |
402 | 504 | 480 | ||
403 | 505 | def test_getMedianBuildDuration(self): | 481 | def test_getMedianBuildDuration(self): |
404 | 482 | |||
405 | 506 | def set_duration(build, minutes): | 483 | def set_duration(build, minutes): |
406 | 507 | duration = timedelta(minutes=minutes) | 484 | duration = timedelta(minutes=minutes) |
407 | 508 | build = removeSecurityProxy(build) | 485 | build = removeSecurityProxy(build) |
408 | @@ -542,7 +519,7 @@ | |||
409 | 542 | } | 519 | } |
410 | 543 | 520 | ||
411 | 544 | def get_recipe(self, recipe_text): | 521 | def get_recipe(self, recipe_text): |
413 | 545 | builder_recipe = RecipeParser(textwrap.dedent(recipe_text)).parse() | 522 | recipe_text = textwrap.dedent(recipe_text) |
414 | 546 | registrant = self.factory.makePerson() | 523 | registrant = self.factory.makePerson() |
415 | 547 | owner = self.factory.makeTeam(owner=registrant) | 524 | owner = self.factory.makeTeam(owner=registrant) |
416 | 548 | distroseries = self.factory.makeDistroSeries() | 525 | distroseries = self.factory.makeDistroSeries() |
417 | @@ -550,7 +527,7 @@ | |||
418 | 550 | description = self.factory.getUniqueString(u'recipe-description') | 527 | description = self.factory.getUniqueString(u'recipe-description') |
419 | 551 | recipe = getUtility(ISourcePackageRecipeSource).new( | 528 | recipe = getUtility(ISourcePackageRecipeSource).new( |
420 | 552 | registrant=registrant, owner=owner, distroseries=[distroseries], | 529 | registrant=registrant, owner=owner, distroseries=[distroseries], |
422 | 553 | name=name, description=description, builder_recipe=builder_recipe) | 530 | name=name, description=description, recipe=recipe_text) |
423 | 554 | transaction.commit() | 531 | transaction.commit() |
424 | 555 | return recipe.builder_recipe | 532 | return recipe.builder_recipe |
425 | 556 | 533 | ||
426 | @@ -724,8 +701,8 @@ | |||
427 | 724 | return MINIMAL_RECIPE_TEXT % branch.bzr_identity | 701 | return MINIMAL_RECIPE_TEXT % branch.bzr_identity |
428 | 725 | 702 | ||
429 | 726 | def makeRecipe(self, user=None, owner=None, recipe_text=None): | 703 | def makeRecipe(self, user=None, owner=None, recipe_text=None): |
432 | 727 | # rockstar 21 Jul 2010 - This function does more commits than I'd like, | 704 | # rockstar 21 Jul 2010 - This function does more commits than I'd |
433 | 728 | # but it's the result of the fact that the webservice runs in a | 705 | # like, but it's the result of the fact that the webservice runs in a |
434 | 729 | # separate thread so doesn't get the database updates without those | 706 | # separate thread so doesn't get the database updates without those |
435 | 730 | # commits. | 707 | # commits. |
436 | 731 | if user is None: | 708 | if user is None: |
437 | 732 | 709 | ||
438 | === modified file 'lib/lp/registry/model/person.py' | |||
439 | --- lib/lp/registry/model/person.py 2010-09-03 16:43:11 +0000 | |||
440 | +++ lib/lp/registry/model/person.py 2010-09-09 13:41:57 +0000 | |||
441 | @@ -36,7 +36,6 @@ | |||
442 | 36 | import subprocess | 36 | import subprocess |
443 | 37 | import weakref | 37 | import weakref |
444 | 38 | 38 | ||
445 | 39 | from bzrlib.plugins.builder.recipe import RecipeParser | ||
446 | 40 | import pytz | 39 | import pytz |
447 | 41 | from sqlobject import ( | 40 | from sqlobject import ( |
448 | 42 | BoolCol, | 41 | BoolCol, |
449 | @@ -2634,9 +2633,8 @@ | |||
450 | 2634 | registrant, daily_build_archive=None, build_daily=False): | 2633 | registrant, daily_build_archive=None, build_daily=False): |
451 | 2635 | """See `IPerson`.""" | 2634 | """See `IPerson`.""" |
452 | 2636 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe | 2635 | from lp.code.model.sourcepackagerecipe import SourcePackageRecipe |
453 | 2637 | builder_recipe = RecipeParser(recipe_text).parse() | ||
454 | 2638 | recipe = SourcePackageRecipe.new( | 2636 | recipe = SourcePackageRecipe.new( |
456 | 2639 | registrant, self, name, builder_recipe, description, distroseries, | 2637 | registrant, self, name, recipe_text, description, distroseries, |
457 | 2640 | daily_build_archive, build_daily) | 2638 | daily_build_archive, build_daily) |
458 | 2641 | Store.of(recipe).flush() | 2639 | Store.of(recipe).flush() |
459 | 2642 | return recipe | 2640 | return recipe |
460 | 2643 | 2641 | ||
461 | === modified file 'lib/lp/testing/factory.py' | |||
462 | --- lib/lp/testing/factory.py 2010-09-03 16:43:11 +0000 | |||
463 | +++ lib/lp/testing/factory.py 2010-09-09 13:41:57 +0000 | |||
464 | @@ -2035,7 +2035,7 @@ | |||
465 | 2035 | distroseries=None, name=None, | 2035 | distroseries=None, name=None, |
466 | 2036 | description=None, branches=(), | 2036 | description=None, branches=(), |
467 | 2037 | build_daily=False, daily_build_archive=None, | 2037 | build_daily=False, daily_build_archive=None, |
469 | 2038 | is_stale=None): | 2038 | is_stale=None, recipe=None): |
470 | 2039 | """Make a `SourcePackageRecipe`.""" | 2039 | """Make a `SourcePackageRecipe`.""" |
471 | 2040 | if registrant is None: | 2040 | if registrant is None: |
472 | 2041 | registrant = self.makePerson() | 2041 | registrant = self.makePerson() |
473 | @@ -2051,7 +2051,10 @@ | |||
474 | 2051 | if daily_build_archive is None: | 2051 | if daily_build_archive is None: |
475 | 2052 | daily_build_archive = self.makeArchive( | 2052 | daily_build_archive = self.makeArchive( |
476 | 2053 | distribution=distroseries.distribution, owner=owner) | 2053 | distribution=distroseries.distribution, owner=owner) |
478 | 2054 | recipe = self.makeRecipe(*branches) | 2054 | if recipe is None: |
479 | 2055 | recipe = self.makeRecipeText(*branches) | ||
480 | 2056 | else: | ||
481 | 2057 | assert branches == () | ||
482 | 2055 | source_package_recipe = getUtility(ISourcePackageRecipeSource).new( | 2058 | source_package_recipe = getUtility(ISourcePackageRecipeSource).new( |
483 | 2056 | registrant, owner, name, recipe, description, [distroseries], | 2059 | registrant, owner, name, recipe, description, [distroseries], |
484 | 2057 | daily_build_archive, build_daily) | 2060 | daily_build_archive, build_daily) |
485 | 2058 | 2061 | ||
486 | === modified file 'utilities/sourcedeps.conf' | |||
487 | --- utilities/sourcedeps.conf 2010-09-03 03:12:39 +0000 | |||
488 | +++ utilities/sourcedeps.conf 2010-09-09 13:41:57 +0000 | |||
489 | @@ -1,4 +1,4 @@ | |||
491 | 1 | bzr-builder lp:~launchpad-pqm/bzr-builder/trunk;revno=65 | 1 | bzr-builder lp:~launchpad-pqm/bzr-builder/trunk;revno=66 |
492 | 2 | bzr-git lp:~launchpad-pqm/bzr-git/devel;revno=258 | 2 | bzr-git lp:~launchpad-pqm/bzr-git/devel;revno=258 |
493 | 3 | bzr-hg lp:~launchpad-pqm/bzr-hg/devel;revno=283 | 3 | bzr-hg lp:~launchpad-pqm/bzr-hg/devel;revno=283 |
494 | 4 | bzr-loom lp:~launchpad-pqm/bzr-loom/trunk;revno=48 | 4 | bzr-loom lp:~launchpad-pqm/bzr-loom/trunk;revno=48 |
Thanks for simplifying the parsed recipe stuff.