Merge ~cjwatson/launchpad:black-oci into launchpad:master
- Git
- lp:~cjwatson/launchpad
- black-oci
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 93641418f92b92cbd6ebab7517425b517d3bff21 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:black-oci |
Merge into: | launchpad:master |
Diff against target: |
20835 lines (+7477/-4555) 40 files modified
.git-blame-ignore-revs (+2/-0) .pre-commit-config.yaml (+3/-0) lib/lp/oci/browser/hasocirecipes.py (+10/-7) lib/lp/oci/browser/ocirecipe.py (+420/-297) lib/lp/oci/browser/ocirecipebuild.py (+39/-25) lib/lp/oci/browser/ocirecipesubscription.py (+47/-34) lib/lp/oci/browser/tests/test_ocirecipe.py (+1276/-712) lib/lp/oci/browser/tests/test_ocirecipebuild.py (+120/-66) lib/lp/oci/browser/tests/test_ocirecipesubscription.py (+125/-60) lib/lp/oci/enums.py (+15/-12) lib/lp/oci/interfaces/ocipushrule.py (+46/-33) lib/lp/oci/interfaces/ocirecipe.py (+407/-226) lib/lp/oci/interfaces/ocirecipebuild.py (+180/-121) lib/lp/oci/interfaces/ocirecipebuildjob.py (+24/-25) lib/lp/oci/interfaces/ocirecipejob.py (+43/-41) lib/lp/oci/interfaces/ocirecipesubscription.py (+18/-15) lib/lp/oci/interfaces/ociregistryclient.py (+10/-7) lib/lp/oci/interfaces/ociregistrycredentials.py (+30/-30) lib/lp/oci/interfaces/webservice.py (+20/-20) lib/lp/oci/model/ocipushrule.py (+29/-30) lib/lp/oci/model/ocirecipe.py (+402/-264) lib/lp/oci/model/ocirecipebuild.py (+207/-127) lib/lp/oci/model/ocirecipebuildbehaviour.py (+80/-56) lib/lp/oci/model/ocirecipebuildjob.py (+70/-61) lib/lp/oci/model/ocirecipejob.py (+96/-79) lib/lp/oci/model/ocirecipesubscription.py (+12/-15) lib/lp/oci/model/ociregistryclient.py (+221/-136) lib/lp/oci/model/ociregistrycredentials.py (+50/-43) lib/lp/oci/security.py (+31/-29) lib/lp/oci/subscribers/ocirecipebuild.py (+22/-9) lib/lp/oci/tests/helpers.py (+17/-12) lib/lp/oci/tests/test_ocipushrule.py (+26/-17) lib/lp/oci/tests/test_ocirecipe.py (+920/-502) lib/lp/oci/tests/test_ocirecipebuild.py (+376/-202) lib/lp/oci/tests/test_ocirecipebuildbehaviour.py (+643/-397) lib/lp/oci/tests/test_ocirecipebuildjob.py (+206/-119) lib/lp/oci/tests/test_ocirecipejob.py (+24/-19) lib/lp/oci/tests/test_ociregistryclient.py (+1001/-590) lib/lp/oci/tests/test_ociregistrycredentials.py (+196/-99) lib/lp/oci/vocabularies.py (+13/-18) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+427157@code.launchpad.net |
Commit message
lp.oci: Apply black
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs |
2 | index f667459..1166ab6 100644 |
3 | --- a/.git-blame-ignore-revs |
4 | +++ b/.git-blame-ignore-revs |
5 | @@ -76,3 +76,5 @@ a6bed71f3d2fdbceae20c2d435c993e8bededdce |
6 | ed7d7b97b8fb4ebe92799f922b0fa9c4bd1714e8 |
7 | # apply black to lp.coop |
8 | 1e6ead9387a1d073eea9271fe8dc646bb22fa3d2 |
9 | +# apply black to lp.oci |
10 | +06b1048510a0b65bb0a6fc46d4e063510b52b5f6 |
11 | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml |
12 | index a7d8233..43f0476 100644 |
13 | --- a/.pre-commit-config.yaml |
14 | +++ b/.pre-commit-config.yaml |
15 | @@ -52,6 +52,7 @@ repos: |
16 | |code |
17 | |codehosting |
18 | |coop |
19 | + |oci |
20 | )/ |
21 | - repo: https://github.com/PyCQA/isort |
22 | rev: 5.9.2 |
23 | @@ -80,6 +81,7 @@ repos: |
24 | |code |
25 | |codehosting |
26 | |coop |
27 | + |oci |
28 | )/ |
29 | - id: isort |
30 | alias: isort-black |
31 | @@ -98,6 +100,7 @@ repos: |
32 | |code |
33 | |codehosting |
34 | |coop |
35 | + |oci |
36 | )/ |
37 | - repo: https://github.com/PyCQA/flake8 |
38 | rev: 3.9.2 |
39 | diff --git a/lib/lp/oci/browser/hasocirecipes.py b/lib/lp/oci/browser/hasocirecipes.py |
40 | index a32ff79..efb92aa 100644 |
41 | --- a/lib/lp/oci/browser/hasocirecipes.py |
42 | +++ b/lib/lp/oci/browser/hasocirecipes.py |
43 | @@ -4,8 +4,8 @@ |
44 | """Mixins for browser classes for objects related to OCI recipe.""" |
45 | |
46 | __all__ = [ |
47 | - 'HasOCIRecipesMenuMixin', |
48 | - ] |
49 | + "HasOCIRecipesMenuMixin", |
50 | +] |
51 | |
52 | from zope.component import getUtility |
53 | |
54 | @@ -17,8 +17,11 @@ class HasOCIRecipesMenuMixin: |
55 | """A mixin for context menus for objects that has OCI recipes.""" |
56 | |
57 | def view_oci_recipes(self): |
58 | - target = '+oci-recipes' |
59 | - text = 'View OCI recipes' |
60 | - enabled = not getUtility(IOCIRecipeSet).findByContext( |
61 | - self.context, visible_by_user=self.user).is_empty() |
62 | - return Link(target, text, enabled=enabled, icon='info') |
63 | + target = "+oci-recipes" |
64 | + text = "View OCI recipes" |
65 | + enabled = ( |
66 | + not getUtility(IOCIRecipeSet) |
67 | + .findByContext(self.context, visible_by_user=self.user) |
68 | + .is_empty() |
69 | + ) |
70 | + return Link(target, text, enabled=enabled, icon="info") |
71 | diff --git a/lib/lp/oci/browser/ocirecipe.py b/lib/lp/oci/browser/ocirecipe.py |
72 | index 7329652..0b9f4d2 100644 |
73 | --- a/lib/lp/oci/browser/ocirecipe.py |
74 | +++ b/lib/lp/oci/browser/ocirecipe.py |
75 | @@ -4,22 +4,19 @@ |
76 | """OCI recipe views.""" |
77 | |
78 | __all__ = [ |
79 | - 'OCIRecipeAddView', |
80 | - 'OCIRecipeAdminView', |
81 | - 'OCIRecipeContextMenu', |
82 | - 'OCIRecipeDeleteView', |
83 | - 'OCIRecipeEditPushRulesView', |
84 | - 'OCIRecipeEditView', |
85 | - 'OCIRecipeNavigation', |
86 | - 'OCIRecipeNavigationMenu', |
87 | - 'OCIRecipeRequestBuildsView', |
88 | - 'OCIRecipeView', |
89 | - ] |
90 | - |
91 | -from lazr.restful.interface import ( |
92 | - copy_field, |
93 | - use_template, |
94 | - ) |
95 | + "OCIRecipeAddView", |
96 | + "OCIRecipeAdminView", |
97 | + "OCIRecipeContextMenu", |
98 | + "OCIRecipeDeleteView", |
99 | + "OCIRecipeEditPushRulesView", |
100 | + "OCIRecipeEditView", |
101 | + "OCIRecipeNavigation", |
102 | + "OCIRecipeNavigationMenu", |
103 | + "OCIRecipeRequestBuildsView", |
104 | + "OCIRecipeView", |
105 | +] |
106 | + |
107 | +from lazr.restful.interface import copy_field, use_template |
108 | from zope.component import getUtility |
109 | from zope.formlib.form import FormFields |
110 | from zope.formlib.textwidgets import TextAreaWidget |
111 | @@ -27,7 +24,7 @@ from zope.formlib.widget import ( |
112 | CustomWidgetFactory, |
113 | DisplayWidget, |
114 | renderElement, |
115 | - ) |
116 | +) |
117 | from zope.interface import Interface |
118 | from zope.schema import ( |
119 | Bool, |
120 | @@ -37,66 +34,63 @@ from zope.schema import ( |
121 | Text, |
122 | TextLine, |
123 | ValidationError, |
124 | - ) |
125 | +) |
126 | from zope.security.interfaces import Unauthorized |
127 | from zope.security.proxy import removeSecurityProxy |
128 | |
129 | from lp.app.browser.launchpadform import ( |
130 | - action, |
131 | LaunchpadEditFormView, |
132 | LaunchpadFormView, |
133 | - ) |
134 | + action, |
135 | +) |
136 | from lp.app.browser.lazrjs import InlinePersonEditPickerWidget |
137 | -from lp.app.browser.tales import ( |
138 | - format_link, |
139 | - GitRepositoryFormatterAPI, |
140 | - ) |
141 | +from lp.app.browser.tales import GitRepositoryFormatterAPI, format_link |
142 | from lp.app.errors import UnexpectedFormData |
143 | from lp.app.validators.validation import validate_oci_branch_name |
144 | from lp.app.vocabularies import InformationTypeVocabulary |
145 | from lp.app.widgets.itemswidgets import ( |
146 | LabeledMultiCheckBoxWidget, |
147 | LaunchpadRadioWidgetWithDescription, |
148 | - ) |
149 | +) |
150 | from lp.buildmaster.interfaces.processor import IProcessorSet |
151 | from lp.code.browser.widgets.gitref import GitRefWidget |
152 | from lp.code.interfaces.gitrepository import IGitRepositorySet |
153 | from lp.oci.interfaces.ocipushrule import ( |
154 | IOCIPushRuleSet, |
155 | OCIPushRuleAlreadyExists, |
156 | - ) |
157 | +) |
158 | from lp.oci.interfaces.ocirecipe import ( |
159 | + OCI_RECIPE_ALLOW_CREATE, |
160 | + OCI_RECIPE_WEBHOOKS_FEATURE_FLAG, |
161 | IOCIRecipe, |
162 | IOCIRecipeSet, |
163 | NoSuchOCIRecipe, |
164 | - OCI_RECIPE_ALLOW_CREATE, |
165 | - OCI_RECIPE_WEBHOOKS_FEATURE_FLAG, |
166 | OCIRecipeFeatureDisabled, |
167 | - ) |
168 | +) |
169 | from lp.oci.interfaces.ocirecipebuild import ( |
170 | IOCIRecipeBuildSet, |
171 | OCIRecipeBuildRegistryUploadStatus, |
172 | - ) |
173 | +) |
174 | from lp.oci.interfaces.ocirecipejob import IOCIRecipeRequestBuildsJobSource |
175 | from lp.oci.interfaces.ociregistrycredentials import ( |
176 | IOCIRegistryCredentialsSet, |
177 | OCIRegistryCredentialsAlreadyExist, |
178 | user_can_edit_credentials_for_owner, |
179 | - ) |
180 | +) |
181 | from lp.registry.interfaces.person import IPersonSet |
182 | from lp.services.features import getFeatureFlag |
183 | from lp.services.propertycache import cachedproperty |
184 | from lp.services.webapp import ( |
185 | - canonical_url, |
186 | ContextMenu, |
187 | - enabled_with_permission, |
188 | LaunchpadView, |
189 | Link, |
190 | Navigation, |
191 | NavigationMenu, |
192 | + canonical_url, |
193 | + enabled_with_permission, |
194 | stepthrough, |
195 | structured, |
196 | - ) |
197 | +) |
198 | from lp.services.webapp.authorization import check_permission |
199 | from lp.services.webapp.batching import BatchNavigator |
200 | from lp.services.webapp.breadcrumb import NameBreadcrumb |
201 | @@ -110,7 +104,7 @@ class OCIRecipeNavigation(WebhookTargetNavigationMixin, Navigation): |
202 | |
203 | usedfor = IOCIRecipe |
204 | |
205 | - @stepthrough('+build-request') |
206 | + @stepthrough("+build-request") |
207 | def traverse_build_request(self, name): |
208 | try: |
209 | job_id = int(name) |
210 | @@ -118,14 +112,14 @@ class OCIRecipeNavigation(WebhookTargetNavigationMixin, Navigation): |
211 | return None |
212 | return self.context.getBuildRequest(job_id) |
213 | |
214 | - @stepthrough('+build') |
215 | + @stepthrough("+build") |
216 | def traverse_build(self, name): |
217 | build = get_build_by_id_str(IOCIRecipeBuildSet, name) |
218 | if build is None or build.recipe != self.context: |
219 | return None |
220 | return build |
221 | |
222 | - @stepthrough('+push-rule') |
223 | + @stepthrough("+push-rule") |
224 | def traverse_pushrule(self, id): |
225 | id = int(id) |
226 | return getUtility(IOCIPushRuleSet).getByID(id) |
227 | @@ -139,7 +133,6 @@ class OCIRecipeNavigation(WebhookTargetNavigationMixin, Navigation): |
228 | |
229 | |
230 | class OCIRecipeBreadcrumb(NameBreadcrumb): |
231 | - |
232 | @property |
233 | def inside(self): |
234 | return self.context.oci_project |
235 | @@ -162,11 +155,14 @@ class OCIRecipeNavigationMenu(NavigationMenu): |
236 | def edit(self): |
237 | return Link("+edit", "Edit OCI recipe", icon="edit") |
238 | |
239 | - @enabled_with_permission('launchpad.Edit') |
240 | + @enabled_with_permission("launchpad.Edit") |
241 | def webhooks(self): |
242 | return Link( |
243 | - '+webhooks', 'Manage webhooks', icon='edit', |
244 | - enabled=bool(getFeatureFlag(OCI_RECIPE_WEBHOOKS_FEATURE_FLAG))) |
245 | + "+webhooks", |
246 | + "Manage webhooks", |
247 | + icon="edit", |
248 | + enabled=bool(getFeatureFlag(OCI_RECIPE_WEBHOOKS_FEATURE_FLAG)), |
249 | + ) |
250 | |
251 | @enabled_with_permission("launchpad.Edit") |
252 | def delete(self): |
253 | @@ -178,19 +174,22 @@ class OCIRecipeContextMenu(ContextMenu): |
254 | |
255 | usedfor = IOCIRecipe |
256 | |
257 | - facet = 'overview' |
258 | + facet = "overview" |
259 | |
260 | - links = ('request_builds', 'edit_push_rules', |
261 | - 'add_subscriber', 'subscription') |
262 | + links = ( |
263 | + "request_builds", |
264 | + "edit_push_rules", |
265 | + "add_subscriber", |
266 | + "subscription", |
267 | + ) |
268 | |
269 | - @enabled_with_permission('launchpad.Edit') |
270 | + @enabled_with_permission("launchpad.Edit") |
271 | def request_builds(self): |
272 | - return Link('+request-builds', 'Request builds', icon='add') |
273 | + return Link("+request-builds", "Request builds", icon="add") |
274 | |
275 | - @enabled_with_permission('launchpad.Edit') |
276 | + @enabled_with_permission("launchpad.Edit") |
277 | def edit_push_rules(self): |
278 | - return Link( |
279 | - '+edit-push-rules', 'Edit push rules', icon='edit') |
280 | + return Link("+edit-push-rules", "Edit push rules", icon="edit") |
281 | |
282 | @enabled_with_permission("launchpad.AnyPerson") |
283 | def subscription(self): |
284 | @@ -214,11 +213,12 @@ class OCIRecipeListingView(LaunchpadView): |
285 | """Default view for the list of OCI recipes of a context (OCI project |
286 | or Person). |
287 | """ |
288 | - page_title = 'Recipes' |
289 | + |
290 | + page_title = "Recipes" |
291 | |
292 | @property |
293 | def label(self): |
294 | - return 'OCI recipes for %s' % self.context.name |
295 | + return "OCI recipes for %s" % self.context.name |
296 | |
297 | @property |
298 | def title(self): |
299 | @@ -227,8 +227,9 @@ class OCIRecipeListingView(LaunchpadView): |
300 | @property |
301 | def recipes(self): |
302 | recipes = getUtility(IOCIRecipeSet).findByContext( |
303 | - self.context, visible_by_user=self.user) |
304 | - return recipes.order_by('name') |
305 | + self.context, visible_by_user=self.user |
306 | + ) |
307 | + return recipes.order_by("name") |
308 | |
309 | @property |
310 | def recipes_navigator(self): |
311 | @@ -266,8 +267,7 @@ class OCIRecipeView(LaunchpadView): |
312 | # We're still not sure how this plays into |
313 | # plans for internal registry announcements and such, so we |
314 | # land redaction first and think about this later. |
315 | - return list( |
316 | - getUtility(IOCIPushRuleSet).findByRecipe(self.context)) |
317 | + return list(getUtility(IOCIPushRuleSet).findByRecipe(self.context)) |
318 | |
319 | @property |
320 | def has_push_rules(self): |
321 | @@ -284,10 +284,15 @@ class OCIRecipeView(LaunchpadView): |
322 | def person_picker(self): |
323 | field = copy_field( |
324 | IOCIRecipe["owner"], |
325 | - vocabularyName="AllUserTeamsParticipationPlusSelfSimpleDisplay") |
326 | + vocabularyName="AllUserTeamsParticipationPlusSelfSimpleDisplay", |
327 | + ) |
328 | return InlinePersonEditPickerWidget( |
329 | - self.context, field, format_link(self.context.owner), |
330 | - header="Change owner", step_title="Select a new owner") |
331 | + self.context, |
332 | + field, |
333 | + format_link(self.context.owner), |
334 | + header="Change owner", |
335 | + step_title="Select a new owner", |
336 | + ) |
337 | |
338 | @property |
339 | def build_frequency(self): |
340 | @@ -300,11 +305,12 @@ class OCIRecipeView(LaunchpadView): |
341 | def build_args(self): |
342 | return "\n".join( |
343 | "%s=%s" % (k, v) |
344 | - for k, v in sorted(self.context.build_args.items())) |
345 | + for k, v in sorted(self.context.build_args.items()) |
346 | + ) |
347 | |
348 | @property |
349 | def distribution_has_credentials(self): |
350 | - if hasattr(self.context, 'oci_project'): |
351 | + if hasattr(self.context, "oci_project"): |
352 | oci_project = self.context.oci_project |
353 | else: |
354 | oci_project = self.context |
355 | @@ -313,13 +319,12 @@ class OCIRecipeView(LaunchpadView): |
356 | |
357 | def getImageForStatus(self, status): |
358 | image_map = { |
359 | - BuildSetStatus.NEEDSBUILD: '/@@/build-needed', |
360 | - BuildSetStatus.FULLYBUILT_PENDING: '/@@/build-success-publishing', |
361 | - BuildSetStatus.FAILEDTOBUILD: '/@@/no', |
362 | - BuildSetStatus.BUILDING: '/@@/processing', |
363 | - } |
364 | - return image_map.get( |
365 | - status, '/@@/yes') |
366 | + BuildSetStatus.NEEDSBUILD: "/@@/build-needed", |
367 | + BuildSetStatus.FULLYBUILT_PENDING: "/@@/build-success-publishing", |
368 | + BuildSetStatus.FAILEDTOBUILD: "/@@/no", |
369 | + BuildSetStatus.BUILDING: "/@@/processing", |
370 | + } |
371 | + return image_map.get(status, "/@@/yes") |
372 | |
373 | def _convertBuildJobToStatus(self, build_job): |
374 | recipe_set = getUtility(IOCIRecipeSet) |
375 | @@ -327,7 +332,8 @@ class OCIRecipeView(LaunchpadView): |
376 | upload_status = build_job.registry_upload_status |
377 | # This is just a dict, but zope wraps it as RecipeSet is secured |
378 | status = removeSecurityProxy( |
379 | - recipe_set.getStatusSummaryForBuilds([build_job])) |
380 | + recipe_set.getStatusSummaryForBuilds([build_job]) |
381 | + ) |
382 | # Add the registry job status |
383 | status["upload_requested"] = upload_status != unscheduled_upload |
384 | status["upload"] = upload_status |
385 | @@ -338,7 +344,7 @@ class OCIRecipeView(LaunchpadView): |
386 | "job_id": "build{}".format(build_job.id), |
387 | "date_created": build_job.date_created, |
388 | "date_finished": build_job.date_finished, |
389 | - "build_status": status |
390 | + "build_status": status, |
391 | } |
392 | |
393 | def build_requests(self): |
394 | @@ -352,7 +358,7 @@ class OCIRecipeView(LaunchpadView): |
395 | if len(build_requests) < 10: |
396 | recipe = self.context |
397 | no_request_builds = recipe.completed_builds_without_build_request |
398 | - for build in no_request_builds[:10 - len(build_requests)]: |
399 | + for build in no_request_builds[: 10 - len(build_requests)]: |
400 | build_requests.append(self._convertBuildJobToStatus(build)) |
401 | return build_requests[:10] |
402 | |
403 | @@ -372,7 +378,7 @@ def builds_for_recipe(recipe): |
404 | """ |
405 | builds = list(recipe.pending_builds) |
406 | if len(builds) < 10: |
407 | - builds.extend(recipe.completed_builds[:10 - len(builds)]) |
408 | + builds.extend(recipe.completed_builds[: 10 - len(builds)]) |
409 | return builds |
410 | |
411 | |
412 | @@ -408,8 +414,7 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
413 | |
414 | @cachedproperty |
415 | def push_rules(self): |
416 | - return list( |
417 | - getUtility(IOCIPushRuleSet).findByRecipe(self.context)) |
418 | + return list(getUtility(IOCIPushRuleSet).findByRecipe(self.context)) |
419 | |
420 | @property |
421 | def has_push_rules(self): |
422 | @@ -418,7 +423,8 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
423 | @cachedproperty |
424 | def can_edit_credentials(self): |
425 | return user_can_edit_credentials_for_owner( |
426 | - self.context.owner, self.user) |
427 | + self.context.owner, self.user |
428 | + ) |
429 | |
430 | def _getFieldName(self, name, rule_id): |
431 | """Get the combined field name for an `OCIPushRule` ID. |
432 | @@ -437,13 +443,15 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
433 | field_bits = field_name.split(".") |
434 | if len(field_bits) != 2: |
435 | raise UnexpectedFormData( |
436 | - "Cannot parse field name: %s" % field_name) |
437 | + "Cannot parse field name: %s" % field_name |
438 | + ) |
439 | field_type = field_bits[0] |
440 | try: |
441 | rule_id = int(field_bits[1]) |
442 | except ValueError: |
443 | raise UnexpectedFormData( |
444 | - "Cannot parse field name: %s" % field_name) |
445 | + "Cannot parse field name: %s" % field_name |
446 | + ) |
447 | return field_type, rule_id |
448 | |
449 | def setUpFields(self): |
450 | @@ -460,26 +468,38 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
451 | for elem in list(self.context.push_rules): |
452 | image_name_fields.append( |
453 | TextLine( |
454 | - __name__=self._getFieldName('image_name', elem.id), |
455 | + __name__=self._getFieldName("image_name", elem.id), |
456 | default=elem.image_name, |
457 | - required=True, readonly=False)) |
458 | - if check_permission('launchpad.View', elem.registry_credentials): |
459 | + required=True, |
460 | + readonly=False, |
461 | + ) |
462 | + ) |
463 | + if check_permission("launchpad.View", elem.registry_credentials): |
464 | url_fields.append( |
465 | TextLine( |
466 | - __name__=self._getFieldName('url', elem.id), |
467 | + __name__=self._getFieldName("url", elem.id), |
468 | default=elem.registry_credentials.url, |
469 | - required=True, readonly=True)) |
470 | + required=True, |
471 | + readonly=True, |
472 | + ) |
473 | + ) |
474 | region = elem.registry_credentials.region |
475 | region_fields.append( |
476 | TextLine( |
477 | - __name__=self._getFieldName('region', elem.id), |
478 | + __name__=self._getFieldName("region", elem.id), |
479 | default=region, |
480 | - required=False, readonly=True)) |
481 | + required=False, |
482 | + readonly=True, |
483 | + ) |
484 | + ) |
485 | username_fields.append( |
486 | TextLine( |
487 | - __name__=self._getFieldName('username', elem.id), |
488 | + __name__=self._getFieldName("username", elem.id), |
489 | default=elem.registry_credentials.username, |
490 | - required=True, readonly=True)) |
491 | + required=True, |
492 | + readonly=True, |
493 | + ) |
494 | + ) |
495 | else: |
496 | # XXX cjwatson 2020-08-27: Ideally we'd be able to just show |
497 | # the URL, and maybe the username too, but the |
498 | @@ -492,71 +512,89 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
499 | # viewer's recipes. |
500 | private_url_fields.append( |
501 | TextLine( |
502 | - __name__=self._getFieldName('url', elem.id), |
503 | - default='', required=True, readonly=True)) |
504 | + __name__=self._getFieldName("url", elem.id), |
505 | + default="", |
506 | + required=True, |
507 | + readonly=True, |
508 | + ) |
509 | + ) |
510 | private_region_fields.append( |
511 | TextLine( |
512 | - __name__=self._getFieldName('region', elem.id), |
513 | - default='', required=False, readonly=True)) |
514 | + __name__=self._getFieldName("region", elem.id), |
515 | + default="", |
516 | + required=False, |
517 | + readonly=True, |
518 | + ) |
519 | + ) |
520 | private_username_fields.append( |
521 | TextLine( |
522 | - __name__=self._getFieldName('username', elem.id), |
523 | - default='', required=True, readonly=True)) |
524 | + __name__=self._getFieldName("username", elem.id), |
525 | + default="", |
526 | + required=True, |
527 | + readonly=True, |
528 | + ) |
529 | + ) |
530 | delete_fields.append( |
531 | Bool( |
532 | - __name__=self._getFieldName('delete', elem.id), |
533 | + __name__=self._getFieldName("delete", elem.id), |
534 | default=False, |
535 | - required=True, readonly=False)) |
536 | + required=True, |
537 | + readonly=False, |
538 | + ) |
539 | + ) |
540 | image_name_fields.append( |
541 | - TextLine( |
542 | - __name__='add_image_name', |
543 | - required=False, readonly=False)) |
544 | + TextLine(__name__="add_image_name", required=False, readonly=False) |
545 | + ) |
546 | add_credentials = Choice( |
547 | - __name__='add_credentials', |
548 | - default='existing', values=('existing', 'new'), |
549 | - required=False, readonly=False) |
550 | + __name__="add_credentials", |
551 | + default="existing", |
552 | + values=("existing", "new"), |
553 | + required=False, |
554 | + readonly=False, |
555 | + ) |
556 | existing_credentials = Choice( |
557 | - vocabulary='OCIRegistryCredentials', |
558 | + vocabulary="OCIRegistryCredentials", |
559 | required=False, |
560 | - __name__='existing_credentials') |
561 | + __name__="existing_credentials", |
562 | + ) |
563 | url_fields.append( |
564 | - TextLine( |
565 | - __name__='add_url', |
566 | - required=False, readonly=False)) |
567 | + TextLine(__name__="add_url", required=False, readonly=False) |
568 | + ) |
569 | region_fields.append( |
570 | - TextLine( |
571 | - __name__='add_region', |
572 | - required=False, readonly=False)) |
573 | + TextLine(__name__="add_region", required=False, readonly=False) |
574 | + ) |
575 | username_fields.append( |
576 | - TextLine( |
577 | - __name__='add_username', |
578 | - required=False, readonly=False)) |
579 | + TextLine(__name__="add_username", required=False, readonly=False) |
580 | + ) |
581 | password_fields.append( |
582 | - Password( |
583 | - __name__='add_password', |
584 | - required=False, readonly=False)) |
585 | + Password(__name__="add_password", required=False, readonly=False) |
586 | + ) |
587 | password_fields.append( |
588 | Password( |
589 | - __name__='add_confirm_password', |
590 | - required=False, readonly=False)) |
591 | + __name__="add_confirm_password", required=False, readonly=False |
592 | + ) |
593 | + ) |
594 | |
595 | self.form_fields = ( |
596 | - FormFields(*image_name_fields) + |
597 | - FormFields(*url_fields) + |
598 | - FormFields( |
599 | - *private_url_fields, |
600 | - custom_widget=InvisibleCredentialsWidget) + |
601 | - FormFields(*region_fields) + |
602 | - FormFields( |
603 | + FormFields(*image_name_fields) |
604 | + + FormFields(*url_fields) |
605 | + + FormFields( |
606 | + *private_url_fields, custom_widget=InvisibleCredentialsWidget |
607 | + ) |
608 | + + FormFields(*region_fields) |
609 | + + FormFields( |
610 | *private_region_fields, |
611 | - custom_widget=InvisibleCredentialsWidget) + |
612 | - FormFields(*username_fields) + |
613 | - FormFields( |
614 | + custom_widget=InvisibleCredentialsWidget, |
615 | + ) |
616 | + + FormFields(*username_fields) |
617 | + + FormFields( |
618 | *private_username_fields, |
619 | - custom_widget=InvisibleCredentialsWidget) + |
620 | - FormFields(*password_fields) + |
621 | - FormFields(*delete_fields) + |
622 | - FormFields(add_credentials, existing_credentials)) |
623 | + custom_widget=InvisibleCredentialsWidget, |
624 | + ) |
625 | + + FormFields(*password_fields) |
626 | + + FormFields(*delete_fields) |
627 | + + FormFields(add_credentials, existing_credentials) |
628 | + ) |
629 | |
630 | def setUpWidgets(self, context=None): |
631 | """See `LaunchpadFormView`.""" |
632 | @@ -567,9 +605,9 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
633 | |
634 | @property |
635 | def label(self): |
636 | - return 'Edit OCI push rules for %s' % self.context.name |
637 | + return "Edit OCI push rules for %s" % self.context.name |
638 | |
639 | - page_title = 'Edit OCI push rules' |
640 | + page_title = "Edit OCI push rules" |
641 | |
642 | @property |
643 | def cancel_url(self): |
644 | @@ -577,16 +615,13 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
645 | |
646 | def getRuleWidgets(self, rule): |
647 | widgets_by_name = {widget.name: widget for widget in self.widgets} |
648 | - url_field_name = ( |
649 | - "field." + self._getFieldName("url", rule.id)) |
650 | - region_field_name = ( |
651 | - "field." + self._getFieldName("region", rule.id)) |
652 | - image_field_name = ( |
653 | - "field." + self._getFieldName("image_name", rule.id)) |
654 | - username_field_name = ( |
655 | - "field." + self._getFieldName("username", rule.id)) |
656 | - delete_field_name = ( |
657 | - "field." + self._getFieldName("delete", rule.id)) |
658 | + url_field_name = "field." + self._getFieldName("url", rule.id) |
659 | + region_field_name = "field." + self._getFieldName("region", rule.id) |
660 | + image_field_name = "field." + self._getFieldName("image_name", rule.id) |
661 | + username_field_name = "field." + self._getFieldName( |
662 | + "username", rule.id |
663 | + ) |
664 | + delete_field_name = "field." + self._getFieldName("delete", rule.id) |
665 | return { |
666 | "url": widgets_by_name[url_field_name], |
667 | "region": widgets_by_name[region_field_name], |
668 | @@ -599,14 +634,15 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
669 | widgets_by_name = {widget.name: widget for widget in self.widgets} |
670 | return { |
671 | "image_name": widgets_by_name["field.add_image_name"], |
672 | - "existing_credentials": |
673 | - widgets_by_name["field.existing_credentials"], |
674 | + "existing_credentials": widgets_by_name[ |
675 | + "field.existing_credentials" |
676 | + ], |
677 | "url": widgets_by_name["field.add_url"], |
678 | "region": widgets_by_name["field.add_region"], |
679 | "username": widgets_by_name["field.add_username"], |
680 | "password": widgets_by_name["field.add_password"], |
681 | "confirm_password": widgets_by_name["field.add_confirm_password"], |
682 | - } |
683 | + } |
684 | |
685 | def parseData(self, data): |
686 | """Rearrange form data to make it easier to process.""" |
687 | @@ -620,23 +656,33 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
688 | add_existing_credentials = data.get("existing_credentials") |
689 | |
690 | # parse data from the Add new rule section of the form |
691 | - if (add_url or add_region or add_username or add_password or |
692 | - add_confirm_password or add_image_name or |
693 | - add_existing_credentials): |
694 | - parsed_data.setdefault(None, { |
695 | - "image_name": add_image_name, |
696 | - "url": add_url, |
697 | - "region": add_region, |
698 | - "username": add_username, |
699 | - "password": add_password, |
700 | - "confirm_password": add_confirm_password, |
701 | - "existing_credentials": data["existing_credentials"], |
702 | - "add_credentials": data["add_credentials"], |
703 | - "action": "add", |
704 | - }) |
705 | + if ( |
706 | + add_url |
707 | + or add_region |
708 | + or add_username |
709 | + or add_password |
710 | + or add_confirm_password |
711 | + or add_image_name |
712 | + or add_existing_credentials |
713 | + ): |
714 | + parsed_data.setdefault( |
715 | + None, |
716 | + { |
717 | + "image_name": add_image_name, |
718 | + "url": add_url, |
719 | + "region": add_region, |
720 | + "username": add_username, |
721 | + "password": add_password, |
722 | + "confirm_password": add_confirm_password, |
723 | + "existing_credentials": data["existing_credentials"], |
724 | + "add_credentials": data["add_credentials"], |
725 | + "action": "add", |
726 | + }, |
727 | + ) |
728 | # parse data from the Edit existing rule section of the form |
729 | for field_name in sorted( |
730 | - name for name in data if name.split(".")[0] == "image_name"): |
731 | + name for name in data if name.split(".")[0] == "image_name" |
732 | + ): |
733 | _, rule_id = self._parseFieldName(field_name) |
734 | image_field_name = self._getFieldName("image_name", rule_id) |
735 | delete_field_name = self._getFieldName("delete", rule_id) |
736 | @@ -644,10 +690,13 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
737 | action = "delete" |
738 | else: |
739 | action = "change" |
740 | - parsed_data.setdefault(rule_id, { |
741 | - "image_name": data.get(image_field_name), |
742 | - "action": action, |
743 | - }) |
744 | + parsed_data.setdefault( |
745 | + rule_id, |
746 | + { |
747 | + "image_name": data.get(image_field_name), |
748 | + "action": action, |
749 | + }, |
750 | + ) |
751 | |
752 | return parsed_data |
753 | |
754 | @@ -678,22 +727,27 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
755 | return |
756 | if password != confirm_password: |
757 | self.setFieldError( |
758 | - "add_confirm_password", "Passwords do not match.") |
759 | + "add_confirm_password", "Passwords do not match." |
760 | + ) |
761 | return |
762 | |
763 | credentials_set = getUtility(IOCIRegistryCredentialsSet) |
764 | try: |
765 | - credential_data = {'username': username, 'password': password} |
766 | + credential_data = {"username": username, "password": password} |
767 | if region is not None: |
768 | - credential_data['region'] = region |
769 | + credential_data["region"] = region |
770 | credentials = credentials_set.getOrCreate( |
771 | - registrant=self.user, owner=self.context.owner, url=url, |
772 | - credentials=credential_data) |
773 | + registrant=self.user, |
774 | + owner=self.context.owner, |
775 | + url=url, |
776 | + credentials=credential_data, |
777 | + ) |
778 | except OCIRegistryCredentialsAlreadyExist: |
779 | self.setFieldError( |
780 | "add_url", |
781 | "Credentials already exist with the same URL, username, " |
782 | - "and region.") |
783 | + "and region.", |
784 | + ) |
785 | return |
786 | except ValidationError: |
787 | self.setFieldError("add_url", "Not a valid URL.") |
788 | @@ -701,17 +755,17 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
789 | |
790 | try: |
791 | getUtility(IOCIPushRuleSet).new( |
792 | - self.context, credentials, image_name) |
793 | + self.context, credentials, image_name |
794 | + ) |
795 | except OCIPushRuleAlreadyExists: |
796 | self.setFieldError( |
797 | "add_image_name", |
798 | "A push rule already exists with the same URL, image name, " |
799 | - "and credentials.") |
800 | + "and credentials.", |
801 | + ) |
802 | |
803 | def updatePushRulesFromData(self, parsed_data): |
804 | - rules_map = { |
805 | - rule.id: rule |
806 | - for rule in self.context.push_rules} |
807 | + rules_map = {rule.id: rule for rule in self.context.push_rules} |
808 | for rule_id, parsed_rules in parsed_data.items(): |
809 | rule = rules_map.get(rule_id) |
810 | action = parsed_rules["action"] |
811 | @@ -720,9 +774,9 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView): |
812 | image_name = parsed_rules["image_name"] |
813 | if not image_name: |
814 | self.setFieldError( |
815 | - self._getFieldName( |
816 | - "image_name", rule_id), |
817 | - "Image name must be set.") |
818 | + self._getFieldName("image_name", rule_id), |
819 | + "Image name must be set.", |
820 | + ) |
821 | else: |
822 | if rule.image_name != image_name: |
823 | rule.setNewImageName(image_name) |
824 | @@ -748,16 +802,18 @@ class OCIRecipeRequestBuildsView(LaunchpadFormView): |
825 | |
826 | @property |
827 | def label(self): |
828 | - return 'Request builds for %s' % self.context.name |
829 | + return "Request builds for %s" % self.context.name |
830 | |
831 | - page_title = 'Request builds' |
832 | + page_title = "Request builds" |
833 | |
834 | class schema(Interface): |
835 | """Schema for requesting a build.""" |
836 | |
837 | distro_arch_series = List( |
838 | - Choice(vocabulary='OCIRecipeDistroArchSeries'), |
839 | - title='Architectures', required=True) |
840 | + Choice(vocabulary="OCIRecipeDistroArchSeries"), |
841 | + title="Architectures", |
842 | + required=True, |
843 | + ) |
844 | |
845 | custom_widget_distro_arch_series = LabeledMultiCheckBoxWidget |
846 | |
847 | @@ -768,61 +824,71 @@ class OCIRecipeRequestBuildsView(LaunchpadFormView): |
848 | @property |
849 | def initial_values(self): |
850 | """See `LaunchpadFormView`.""" |
851 | - return {'distro_arch_series': self.context.getAllowedArchitectures()} |
852 | + return {"distro_arch_series": self.context.getAllowedArchitectures()} |
853 | |
854 | def validate(self, data): |
855 | """See `LaunchpadFormView`.""" |
856 | - arches = data.get('distro_arch_series', []) |
857 | + arches = data.get("distro_arch_series", []) |
858 | if not arches: |
859 | self.setFieldError( |
860 | - 'distro_arch_series', |
861 | - 'You need to select at least one architecture.') |
862 | + "distro_arch_series", |
863 | + "You need to select at least one architecture.", |
864 | + ) |
865 | |
866 | - @action('Request builds', name='request') |
867 | + @action("Request builds", name="request") |
868 | def request_action(self, action, data): |
869 | - if data.get('distro_arch_series'): |
870 | + if data.get("distro_arch_series"): |
871 | architectures = [ |
872 | - arch.architecturetag for arch in data['distro_arch_series']] |
873 | + arch.architecturetag for arch in data["distro_arch_series"] |
874 | + ] |
875 | else: |
876 | architectures = None |
877 | self.context.requestBuilds(self.user, architectures) |
878 | self.request.response.addNotification( |
879 | "Your builds were scheduled and should start soon. " |
880 | - "Refresh this page for details.") |
881 | + "Refresh this page for details." |
882 | + ) |
883 | self.next_url = self.cancel_url |
884 | |
885 | |
886 | class IOCIRecipeEditSchema(Interface): |
887 | """Schema for adding or editing an OCI recipe.""" |
888 | |
889 | - use_template(IOCIRecipe, include=[ |
890 | - "name", |
891 | - "owner", |
892 | - "information_type", |
893 | - "description", |
894 | - "git_ref", |
895 | - "build_file", |
896 | - "build_args", |
897 | - "build_path", |
898 | - "build_daily", |
899 | - "require_virtualized", |
900 | - "allow_internet", |
901 | - ]) |
902 | + use_template( |
903 | + IOCIRecipe, |
904 | + include=[ |
905 | + "name", |
906 | + "owner", |
907 | + "information_type", |
908 | + "description", |
909 | + "git_ref", |
910 | + "build_file", |
911 | + "build_args", |
912 | + "build_path", |
913 | + "build_daily", |
914 | + "require_virtualized", |
915 | + "allow_internet", |
916 | + ], |
917 | + ) |
918 | |
919 | |
920 | class OCIRecipeFormMixin: |
921 | """Mixin with common processing for both edit and add views.""" |
922 | + |
923 | custom_widget_build_args = CustomWidgetFactory( |
924 | - TextAreaWidget, height=5, width=100) |
925 | + TextAreaWidget, height=5, width=100 |
926 | + ) |
927 | |
928 | custom_widget_information_type = CustomWidgetFactory( |
929 | LaunchpadRadioWidgetWithDescription, |
930 | - vocabulary=InformationTypeVocabulary(types=[])) |
931 | + vocabulary=InformationTypeVocabulary(types=[]), |
932 | + ) |
933 | |
934 | def setUpInformationTypeWidget(self): |
935 | - info_type_widget = self.widgets['information_type'] |
936 | + info_type_widget = self.widgets["information_type"] |
937 | info_type_widget.vocabulary = InformationTypeVocabulary( |
938 | - types=self.getInformationTypesToShow()) |
939 | + types=self.getInformationTypesToShow() |
940 | + ) |
941 | |
942 | def getInformationTypesToShow(self): |
943 | """Get the information types to display on the edit form. |
944 | @@ -840,36 +906,45 @@ class OCIRecipeFormMixin: |
945 | if IOCIRecipe.providedBy(self.context): |
946 | default = "\n".join( |
947 | "%s=%s" % (k, v) |
948 | - for k, v in sorted(self.context.build_args.items())) |
949 | + for k, v in sorted(self.context.build_args.items()) |
950 | + ) |
951 | else: |
952 | default = "" |
953 | - return FormFields(Text( |
954 | - __name__='build_args', |
955 | - title='Build-time ARG variables', |
956 | - description=("One per line. Each ARG should be in the format " |
957 | - "of ARG_KEY=arg_value."), |
958 | - default=default, |
959 | - required=False, readonly=False)) |
960 | + return FormFields( |
961 | + Text( |
962 | + __name__="build_args", |
963 | + title="Build-time ARG variables", |
964 | + description=( |
965 | + "One per line. Each ARG should be in the format " |
966 | + "of ARG_KEY=arg_value." |
967 | + ), |
968 | + default=default, |
969 | + required=False, |
970 | + readonly=False, |
971 | + ) |
972 | + ) |
973 | |
974 | def validateBuildArgs(self, data): |
975 | - field_value = data.get('build_args') |
976 | + field_value = data.get("build_args") |
977 | if not field_value: |
978 | return |
979 | build_args = {} |
980 | for i, line in enumerate(field_value.split("\n")): |
981 | - if '=' not in line: |
982 | - msg = ("'%s' at line %s is not a valid KEY=value pair." % |
983 | - (line, i + 1)) |
984 | + if "=" not in line: |
985 | + msg = "'%s' at line %s is not a valid KEY=value pair." % ( |
986 | + line, |
987 | + i + 1, |
988 | + ) |
989 | self.setFieldError("build_args", str(msg)) |
990 | return |
991 | - k, v = line.split('=', 1) |
992 | + k, v = line.split("=", 1) |
993 | build_args[k] = v |
994 | - data['build_args'] = build_args |
995 | + data["build_args"] = build_args |
996 | |
997 | def userIsRecipeAdmin(self): |
998 | if check_permission("launchpad.Admin", self.context): |
999 | return True |
1000 | - person = getattr(self.request.principal, 'person', None) |
1001 | + person = getattr(self.request.principal, "person", None) |
1002 | if not person: |
1003 | return False |
1004 | # Edit context = OCIRecipe, New context = OCIProject |
1005 | @@ -890,7 +965,7 @@ class OCIRecipeFormMixin: |
1006 | |
1007 | @property |
1008 | def distribution_has_credentials(self): |
1009 | - if hasattr(self.context, 'oci_project'): |
1010 | + if hasattr(self.context, "oci_project"): |
1011 | oci_project = self.context.oci_project |
1012 | else: |
1013 | oci_project = self.context |
1014 | @@ -898,8 +973,9 @@ class OCIRecipeFormMixin: |
1015 | return bool(distro and distro.oci_registry_credentials) |
1016 | |
1017 | |
1018 | -class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin, |
1019 | - OCIRecipeFormMixin): |
1020 | +class OCIRecipeAddView( |
1021 | + LaunchpadFormView, EnableProcessorsMixin, OCIRecipeFormMixin |
1022 | +): |
1023 | """View for creating OCI recipes.""" |
1024 | |
1025 | page_title = label = "Create a new OCI recipe" |
1026 | @@ -914,7 +990,7 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin, |
1027 | "build_file", |
1028 | "build_path", |
1029 | "build_daily", |
1030 | - ) |
1031 | + ) |
1032 | custom_widget_git_ref = GitRefWidget |
1033 | |
1034 | def initialize(self): |
1035 | @@ -930,25 +1006,36 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin, |
1036 | getUtility(IProcessorSet).getAll(), |
1037 | "The architectures that this OCI recipe builds for. Some " |
1038 | "architectures are restricted and may only be enabled or " |
1039 | - "disabled by administrators.") |
1040 | - self.form_fields += FormFields(Bool( |
1041 | - __name__="official_recipe", |
1042 | - title="Official recipe", |
1043 | - description=( |
1044 | - "Mark this recipe as official for this OCI Project. " |
1045 | - "Allows use of distribution registry credentials " |
1046 | - "and the default git repository routing. " |
1047 | - "May only be enabled by the owner of the OCI Project."), |
1048 | - default=False, |
1049 | - required=False, readonly=False)) |
1050 | - if self.distribution_has_credentials: |
1051 | - self.form_fields += FormFields(TextLine( |
1052 | - __name__='image_name', |
1053 | - title="Image name", |
1054 | + "disabled by administrators.", |
1055 | + ) |
1056 | + self.form_fields += FormFields( |
1057 | + Bool( |
1058 | + __name__="official_recipe", |
1059 | + title="Official recipe", |
1060 | description=( |
1061 | - "Name to use for registry upload. " |
1062 | - "Defaults to the name of the recipe."), |
1063 | - required=False, readonly=False)) |
1064 | + "Mark this recipe as official for this OCI Project. " |
1065 | + "Allows use of distribution registry credentials " |
1066 | + "and the default git repository routing. " |
1067 | + "May only be enabled by the owner of the OCI Project." |
1068 | + ), |
1069 | + default=False, |
1070 | + required=False, |
1071 | + readonly=False, |
1072 | + ) |
1073 | + ) |
1074 | + if self.distribution_has_credentials: |
1075 | + self.form_fields += FormFields( |
1076 | + TextLine( |
1077 | + __name__="image_name", |
1078 | + title="Image name", |
1079 | + description=( |
1080 | + "Name to use for registry upload. " |
1081 | + "Defaults to the name of the recipe." |
1082 | + ), |
1083 | + required=False, |
1084 | + readonly=False, |
1085 | + ) |
1086 | + ) |
1087 | |
1088 | def setUpGitRefWidget(self): |
1089 | """Setup GitRef widget indicating the user to use the default |
1090 | @@ -963,13 +1050,15 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin, |
1091 | # Do not override more important git_ref errors. |
1092 | return |
1093 | default_repo = getUtility(IGitRepositorySet).getDefaultRepository( |
1094 | - self.context) |
1095 | + self.context |
1096 | + ) |
1097 | if default_repo is None: |
1098 | msg = ( |
1099 | - 'The default git repository for this OCI project was not ' |
1100 | - 'created yet.<br/>' |
1101 | + "The default git repository for this OCI project was not " |
1102 | + "created yet.<br/>" |
1103 | 'Check the <a href="%s">OCI project page</a> for instructions ' |
1104 | - 'on how to create one.') |
1105 | + "on how to create one." |
1106 | + ) |
1107 | msg = structured(msg, canonical_url(self.context)) |
1108 | self.widget_errors["git_ref"] = msg.escapedtext |
1109 | |
1110 | @@ -981,7 +1070,7 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin, |
1111 | self.setUpGitRefWidget() |
1112 | # disable the official recipe button if the user doesn't have |
1113 | # permissions to change it |
1114 | - widget = self.widgets['official_recipe'] |
1115 | + widget = self.widgets["official_recipe"] |
1116 | if not self.userIsRecipeAdmin(): |
1117 | widget.extra = "disabled='disabled'" |
1118 | |
1119 | @@ -998,9 +1087,11 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin, |
1120 | "build_file": "Dockerfile", |
1121 | "build_path": ".", |
1122 | "processors": [ |
1123 | - p for p in getUtility(IProcessorSet).getAll() |
1124 | - if p.build_by_default], |
1125 | - } |
1126 | + p |
1127 | + for p in getUtility(IProcessorSet).getAll() |
1128 | + if p.build_by_default |
1129 | + ], |
1130 | + } |
1131 | |
1132 | def validate(self, data): |
1133 | """See `LaunchpadFormView`.""" |
1134 | @@ -1012,27 +1103,36 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin, |
1135 | self.setFieldError( |
1136 | "name", |
1137 | "There is already an OCI recipe owned by %s in %s with " |
1138 | - "this name." % ( |
1139 | - owner.display_name, self.context.display_name)) |
1140 | + "this name." |
1141 | + % (owner.display_name, self.context.display_name), |
1142 | + ) |
1143 | self.validateBuildArgs(data) |
1144 | official = data.get("official_recipe", None) |
1145 | if official and not self.userIsRecipeAdmin(): |
1146 | self.setFieldError( |
1147 | "official_recipe", |
1148 | "You do not have permission to set the official status " |
1149 | - "of this recipe.") |
1150 | + "of this recipe.", |
1151 | + ) |
1152 | |
1153 | @action("Create OCI recipe", name="create") |
1154 | def create_action(self, action, data): |
1155 | recipe = getUtility(IOCIRecipeSet).new( |
1156 | - name=data["name"], registrant=self.user, owner=data["owner"], |
1157 | - oci_project=self.context, git_ref=data["git_ref"], |
1158 | - build_file=data["build_file"], description=data["description"], |
1159 | - build_daily=data["build_daily"], build_args=data["build_args"], |
1160 | - build_path=data["build_path"], processors=data["processors"], |
1161 | - official=data.get('official_recipe', False), |
1162 | + name=data["name"], |
1163 | + registrant=self.user, |
1164 | + owner=data["owner"], |
1165 | + oci_project=self.context, |
1166 | + git_ref=data["git_ref"], |
1167 | + build_file=data["build_file"], |
1168 | + description=data["description"], |
1169 | + build_daily=data["build_daily"], |
1170 | + build_args=data["build_args"], |
1171 | + build_path=data["build_path"], |
1172 | + processors=data["processors"], |
1173 | + official=data.get("official_recipe", False), |
1174 | # image_name is only available if using distribution credentials. |
1175 | - image_name=data.get("image_name")) |
1176 | + image_name=data.get("image_name"), |
1177 | + ) |
1178 | self.next_url = canonical_url(recipe) |
1179 | |
1180 | |
1181 | @@ -1055,12 +1155,14 @@ class BaseOCIRecipeEditView(LaunchpadEditFormView): |
1182 | if new_processors is not None: |
1183 | if set(self.context.processors) != set(new_processors): |
1184 | self.context.setProcessors( |
1185 | - new_processors, check_permissions=True, user=self.user) |
1186 | + new_processors, check_permissions=True, user=self.user |
1187 | + ) |
1188 | del data["processors"] |
1189 | - official = data.pop('official_recipe', None) |
1190 | + official = data.pop("official_recipe", None) |
1191 | if official is not None and self.userIsRecipeAdmin(): |
1192 | self.context.oci_project.setOfficialRecipeStatus( |
1193 | - self.context, official) |
1194 | + self.context, official |
1195 | + ) |
1196 | |
1197 | self.updateContextFromData(data) |
1198 | self.next_url = canonical_url(self.context) |
1199 | @@ -1083,8 +1185,9 @@ class OCIRecipeAdminView(BaseOCIRecipeEditView): |
1200 | field_names = ("require_virtualized", "allow_internet") |
1201 | |
1202 | |
1203 | -class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin, |
1204 | - OCIRecipeFormMixin): |
1205 | +class OCIRecipeEditView( |
1206 | + BaseOCIRecipeEditView, EnableProcessorsMixin, OCIRecipeFormMixin |
1207 | +): |
1208 | """View for editing OCI recipes.""" |
1209 | |
1210 | @property |
1211 | @@ -1102,7 +1205,7 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin, |
1212 | "build_file", |
1213 | "build_path", |
1214 | "build_daily", |
1215 | - ) |
1216 | + ) |
1217 | custom_widget_git_ref = GitRefWidget |
1218 | |
1219 | def setUpGitRefWidget(self): |
1220 | @@ -1119,17 +1222,21 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin, |
1221 | return |
1222 | msg = None |
1223 | if self.context.git_ref.namespace.target != self.context.oci_project: |
1224 | - msg = ("This recipe's git repository is not in the " |
1225 | - "correct namespace.<br/>") |
1226 | + msg = ( |
1227 | + "This recipe's git repository is not in the " |
1228 | + "correct namespace.<br/>" |
1229 | + ) |
1230 | default_repo = getUtility(IGitRepositorySet).getDefaultRepository( |
1231 | - oci_proj) |
1232 | + oci_proj |
1233 | + ) |
1234 | if default_repo: |
1235 | - link = GitRepositoryFormatterAPI(default_repo).link('') |
1236 | + link = GitRepositoryFormatterAPI(default_repo).link("") |
1237 | msg += "Consider using %s instead." % link |
1238 | else: |
1239 | msg += ( |
1240 | 'Check the <a href="%(oci_proj_url)s">OCI project page</a>' |
1241 | - ' for instructions on how to create it correctly.') |
1242 | + " for instructions on how to create it correctly." |
1243 | + ) |
1244 | if msg: |
1245 | msg = structured(msg, oci_proj_url=oci_proj_url) |
1246 | self.widget_errors["git_ref"] = msg.escapedtext |
1247 | @@ -1141,7 +1248,7 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin, |
1248 | self.setUpGitRefWidget() |
1249 | # disable the official recipe button if the user doesn't have |
1250 | # permissions to change it |
1251 | - widget = self.widgets['official_recipe'] |
1252 | + widget = self.widgets["official_recipe"] |
1253 | if not self.userIsRecipeAdmin(): |
1254 | widget.extra = "disabled='disabled'" |
1255 | |
1256 | @@ -1153,26 +1260,37 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin, |
1257 | self.context.available_processors, |
1258 | "The architectures that this OCI recipe builds for. Some " |
1259 | "architectures are restricted and may only be enabled or " |
1260 | - "disabled by administrators.") |
1261 | - self.form_fields += FormFields(Bool( |
1262 | - __name__="official_recipe", |
1263 | - title="Official recipe", |
1264 | - description=( |
1265 | - "Mark this recipe as official for this OCI Project. " |
1266 | - "Allows use of distribution registry credentials " |
1267 | - "and the default git repository routing. " |
1268 | - "May only be enabled by the owner of the OCI Project."), |
1269 | - default=self.context.official, |
1270 | - required=False, readonly=False)) |
1271 | - if self.distribution_has_credentials: |
1272 | - self.form_fields += FormFields(TextLine( |
1273 | - __name__='image_name', |
1274 | - title="Image name", |
1275 | + "disabled by administrators.", |
1276 | + ) |
1277 | + self.form_fields += FormFields( |
1278 | + Bool( |
1279 | + __name__="official_recipe", |
1280 | + title="Official recipe", |
1281 | description=( |
1282 | - "Name to use for registry upload. " |
1283 | - "Defaults to the name of the recipe."), |
1284 | - default=self.context.image_name, |
1285 | - required=False, readonly=False)) |
1286 | + "Mark this recipe as official for this OCI Project. " |
1287 | + "Allows use of distribution registry credentials " |
1288 | + "and the default git repository routing. " |
1289 | + "May only be enabled by the owner of the OCI Project." |
1290 | + ), |
1291 | + default=self.context.official, |
1292 | + required=False, |
1293 | + readonly=False, |
1294 | + ) |
1295 | + ) |
1296 | + if self.distribution_has_credentials: |
1297 | + self.form_fields += FormFields( |
1298 | + TextLine( |
1299 | + __name__="image_name", |
1300 | + title="Image name", |
1301 | + description=( |
1302 | + "Name to use for registry upload. " |
1303 | + "Defaults to the name of the recipe." |
1304 | + ), |
1305 | + default=self.context.image_name, |
1306 | + required=False, |
1307 | + readonly=False, |
1308 | + ) |
1309 | + ) |
1310 | |
1311 | def validate(self, data): |
1312 | """See `LaunchpadFormView`.""" |
1313 | @@ -1184,14 +1302,18 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin, |
1314 | if owner and name: |
1315 | try: |
1316 | recipe = getUtility(IOCIRecipeSet).getByName( |
1317 | - owner, self.context.oci_project, name) |
1318 | + owner, self.context.oci_project, name |
1319 | + ) |
1320 | if recipe != self.context: |
1321 | self.setFieldError( |
1322 | "name", |
1323 | "There is already an OCI recipe owned by %s in %s " |
1324 | - "with this name." % ( |
1325 | + "with this name." |
1326 | + % ( |
1327 | owner.display_name, |
1328 | - self.context.oci_project.display_name)) |
1329 | + self.context.oci_project.display_name, |
1330 | + ), |
1331 | + ) |
1332 | except NoSuchOCIRecipe: |
1333 | pass |
1334 | if "processors" in data: |
1335 | @@ -1208,14 +1330,15 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin, |
1336 | # enabled. Leave it untouched. |
1337 | data["processors"].append(processor) |
1338 | self.validateBuildArgs(data) |
1339 | - official = data.get('official_recipe') |
1340 | + official = data.get("official_recipe") |
1341 | official_change = self.context.official != official |
1342 | is_admin = self.userIsRecipeAdmin() |
1343 | if official is not None and official_change and not is_admin: |
1344 | self.setFieldError( |
1345 | "official_recipe", |
1346 | "You do not have permission to change the official status " |
1347 | - "of this recipe.") |
1348 | + "of this recipe.", |
1349 | + ) |
1350 | |
1351 | |
1352 | class OCIRecipeDeleteView(BaseOCIRecipeEditView): |
1353 | diff --git a/lib/lp/oci/browser/ocirecipebuild.py b/lib/lp/oci/browser/ocirecipebuild.py |
1354 | index 9f1d55e..7aaad43 100644 |
1355 | --- a/lib/lp/oci/browser/ocirecipebuild.py |
1356 | +++ b/lib/lp/oci/browser/ocirecipebuild.py |
1357 | @@ -4,35 +4,32 @@ |
1358 | """OCI recipe build views.""" |
1359 | |
1360 | __all__ = [ |
1361 | - 'OCIRecipeBuildCancelView', |
1362 | - 'OCIRecipeBuildContextMenu', |
1363 | - 'OCIRecipeBuildNavigation', |
1364 | - 'OCIRecipeBuildRescoreView', |
1365 | - 'OCIRecipeBuildView', |
1366 | - ] |
1367 | + "OCIRecipeBuildCancelView", |
1368 | + "OCIRecipeBuildContextMenu", |
1369 | + "OCIRecipeBuildNavigation", |
1370 | + "OCIRecipeBuildRescoreView", |
1371 | + "OCIRecipeBuildView", |
1372 | +] |
1373 | |
1374 | from zope.interface import Interface |
1375 | |
1376 | -from lp.app.browser.launchpadform import ( |
1377 | - action, |
1378 | - LaunchpadFormView, |
1379 | - ) |
1380 | +from lp.app.browser.launchpadform import LaunchpadFormView, action |
1381 | from lp.oci.interfaces.ocirecipebuild import ( |
1382 | CannotScheduleRegistryUpload, |
1383 | IOCIRecipeBuild, |
1384 | - ) |
1385 | +) |
1386 | from lp.services.librarian.browser import ( |
1387 | FileNavigationMixin, |
1388 | ProxiedLibraryFileAlias, |
1389 | - ) |
1390 | +) |
1391 | from lp.services.propertycache import cachedproperty |
1392 | from lp.services.webapp import ( |
1393 | - canonical_url, |
1394 | ContextMenu, |
1395 | - enabled_with_permission, |
1396 | Link, |
1397 | Navigation, |
1398 | - ) |
1399 | + canonical_url, |
1400 | + enabled_with_permission, |
1401 | +) |
1402 | from lp.soyuz.interfaces.binarypackagebuild import IBuildRescoreForm |
1403 | |
1404 | |
1405 | @@ -53,20 +50,29 @@ class OCIRecipeBuildContextMenu(ContextMenu): |
1406 | @enabled_with_permission("launchpad.Edit") |
1407 | def retry(self): |
1408 | return Link( |
1409 | - "+retry", "Retry this build", icon="retry", |
1410 | - enabled=self.context.can_be_retried) |
1411 | + "+retry", |
1412 | + "Retry this build", |
1413 | + icon="retry", |
1414 | + enabled=self.context.can_be_retried, |
1415 | + ) |
1416 | |
1417 | @enabled_with_permission("launchpad.Edit") |
1418 | def cancel(self): |
1419 | return Link( |
1420 | - "+cancel", "Cancel build", icon="remove", |
1421 | - enabled=self.context.can_be_cancelled) |
1422 | + "+cancel", |
1423 | + "Cancel build", |
1424 | + icon="remove", |
1425 | + enabled=self.context.can_be_cancelled, |
1426 | + ) |
1427 | |
1428 | @enabled_with_permission("launchpad.Admin") |
1429 | def rescore(self): |
1430 | return Link( |
1431 | - "+rescore", "Rescore build", icon="edit", |
1432 | - enabled=self.context.can_be_rescored) |
1433 | + "+rescore", |
1434 | + "Rescore build", |
1435 | + icon="edit", |
1436 | + enabled=self.context.can_be_rescored, |
1437 | + ) |
1438 | |
1439 | |
1440 | class OCIRecipeBuildView(LaunchpadFormView): |
1441 | @@ -89,7 +95,9 @@ class OCIRecipeBuildView(LaunchpadFormView): |
1442 | |
1443 | return [ |
1444 | ProxiedLibraryFileAlias(alias, self.context) |
1445 | - for _, alias, _ in self.context.getFiles() if not alias.deleted] |
1446 | + for _, alias, _ in self.context.getFiles() |
1447 | + if not alias.deleted |
1448 | + ] |
1449 | |
1450 | @cachedproperty |
1451 | def has_files(self): |
1452 | @@ -109,7 +117,8 @@ class OCIRecipeBuildView(LaunchpadFormView): |
1453 | else: |
1454 | self.request.response.addInfoNotification( |
1455 | "An upload has been scheduled and will run as soon as " |
1456 | - "possible.") |
1457 | + "possible." |
1458 | + ) |
1459 | |
1460 | |
1461 | class OCIRecipeBuildRetryView(LaunchpadFormView): |
1462 | @@ -123,6 +132,7 @@ class OCIRecipeBuildRetryView(LaunchpadFormView): |
1463 | @property |
1464 | def cancel_url(self): |
1465 | return canonical_url(self.context) |
1466 | + |
1467 | next_url = cancel_url |
1468 | |
1469 | @action("Retry build", name="retry") |
1470 | @@ -130,7 +140,8 @@ class OCIRecipeBuildRetryView(LaunchpadFormView): |
1471 | """Retry the build.""" |
1472 | if not self.context.can_be_retried: |
1473 | self.request.response.addErrorNotification( |
1474 | - "Build cannot be retried") |
1475 | + "Build cannot be retried" |
1476 | + ) |
1477 | else: |
1478 | self.context.retry() |
1479 | self.request.response.addInfoNotification("Build has been queued") |
1480 | @@ -149,6 +160,7 @@ class OCIRecipeBuildCancelView(LaunchpadFormView): |
1481 | @property |
1482 | def cancel_url(self): |
1483 | return canonical_url(self.context) |
1484 | + |
1485 | next_url = cancel_url |
1486 | |
1487 | @action("Cancel build", name="cancel") |
1488 | @@ -168,12 +180,14 @@ class OCIRecipeBuildRescoreView(LaunchpadFormView): |
1489 | if self.context.can_be_rescored: |
1490 | return super().__call__() |
1491 | self.request.response.addWarningNotification( |
1492 | - "Cannot rescore this build because it is not queued.") |
1493 | + "Cannot rescore this build because it is not queued." |
1494 | + ) |
1495 | self.request.response.redirect(canonical_url(self.context)) |
1496 | |
1497 | @property |
1498 | def cancel_url(self): |
1499 | return canonical_url(self.context) |
1500 | + |
1501 | next_url = cancel_url |
1502 | |
1503 | @action("Rescore build", name="rescore") |
1504 | diff --git a/lib/lp/oci/browser/ocirecipesubscription.py b/lib/lp/oci/browser/ocirecipesubscription.py |
1505 | index c4f016f..65643ba 100644 |
1506 | --- a/lib/lp/oci/browser/ocirecipesubscription.py |
1507 | +++ b/lib/lp/oci/browser/ocirecipesubscription.py |
1508 | @@ -3,9 +3,7 @@ |
1509 | |
1510 | """OCI recipe subscription views.""" |
1511 | |
1512 | -__all__ = [ |
1513 | - 'OCIRecipePortletSubscribersContent' |
1514 | -] |
1515 | +__all__ = ["OCIRecipePortletSubscribersContent"] |
1516 | |
1517 | from zope.component import getUtility |
1518 | from zope.formlib.form import action |
1519 | @@ -14,17 +12,14 @@ from zope.security.interfaces import ForbiddenAttribute |
1520 | from lp.app.browser.launchpadform import ( |
1521 | LaunchpadEditFormView, |
1522 | LaunchpadFormView, |
1523 | - ) |
1524 | +) |
1525 | from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription |
1526 | from lp.registry.interfaces.person import IPersonSet |
1527 | -from lp.services.webapp import ( |
1528 | - canonical_url, |
1529 | - LaunchpadView, |
1530 | - ) |
1531 | +from lp.services.webapp import LaunchpadView, canonical_url |
1532 | from lp.services.webapp.authorization import ( |
1533 | check_permission, |
1534 | precache_permission_for_objects, |
1535 | - ) |
1536 | +) |
1537 | |
1538 | |
1539 | class OCIRecipePortletSubscribersContent(LaunchpadView): |
1540 | @@ -38,20 +33,28 @@ class OCIRecipePortletSubscribersContent(LaunchpadView): |
1541 | # need the expense of running several complex SQL queries. |
1542 | subscriptions = list(self.context.subscriptions) |
1543 | person_ids = [sub.person.id for sub in subscriptions] |
1544 | - list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
1545 | - person_ids, need_validity=True)) |
1546 | + list( |
1547 | + getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
1548 | + person_ids, need_validity=True |
1549 | + ) |
1550 | + ) |
1551 | if self.user is not None: |
1552 | subscribers = [ |
1553 | - subscription.person for subscription in subscriptions] |
1554 | + subscription.person for subscription in subscriptions |
1555 | + ] |
1556 | precache_permission_for_objects( |
1557 | - self.request, "launchpad.LimitedView", subscribers) |
1558 | + self.request, "launchpad.LimitedView", subscribers |
1559 | + ) |
1560 | |
1561 | visible_subscriptions = [ |
1562 | - subscription for subscription in subscriptions |
1563 | - if check_permission("launchpad.LimitedView", subscription.person)] |
1564 | + subscription |
1565 | + for subscription in subscriptions |
1566 | + if check_permission("launchpad.LimitedView", subscription.person) |
1567 | + ] |
1568 | return sorted( |
1569 | visible_subscriptions, |
1570 | - key=lambda subscription: subscription.person.displayname) |
1571 | + key=lambda subscription: subscription.person.displayname, |
1572 | + ) |
1573 | |
1574 | |
1575 | class RedirectToOCIRecipeMixin: |
1576 | @@ -73,23 +76,25 @@ class RedirectToOCIRecipeMixin: |
1577 | cancel_url = next_url |
1578 | |
1579 | |
1580 | -class OCIRecipeSubscriptionEditView(RedirectToOCIRecipeMixin, |
1581 | - LaunchpadEditFormView): |
1582 | +class OCIRecipeSubscriptionEditView( |
1583 | + RedirectToOCIRecipeMixin, LaunchpadEditFormView |
1584 | +): |
1585 | """The view for editing OCI recipe subscriptions.""" |
1586 | + |
1587 | schema = IOCIRecipeSubscription |
1588 | field_names = [] |
1589 | |
1590 | @property |
1591 | def page_title(self): |
1592 | return ( |
1593 | - "Edit subscription to OCI recipe %s" % |
1594 | - self.ocirecipe.displayname) |
1595 | + "Edit subscription to OCI recipe %s" % self.ocirecipe.displayname |
1596 | + ) |
1597 | |
1598 | @property |
1599 | def label(self): |
1600 | return ( |
1601 | - "Edit subscription to OCI recipe for %s" % |
1602 | - self.person.displayname) |
1603 | + "Edit subscription to OCI recipe for %s" % self.person.displayname |
1604 | + ) |
1605 | |
1606 | def initialize(self): |
1607 | self.ocirecipe = self.context.recipe |
1608 | @@ -102,11 +107,13 @@ class OCIRecipeSubscriptionEditView(RedirectToOCIRecipeMixin, |
1609 | self.ocirecipe.unsubscribe(self.person, self.user) |
1610 | self.request.response.addNotification( |
1611 | "%s has been unsubscribed from this OCI recipe." |
1612 | - % self.person.displayname) |
1613 | + % self.person.displayname |
1614 | + ) |
1615 | |
1616 | |
1617 | -class _OCIRecipeSubscriptionCreationView(RedirectToOCIRecipeMixin, |
1618 | - LaunchpadFormView): |
1619 | +class _OCIRecipeSubscriptionCreationView( |
1620 | + RedirectToOCIRecipeMixin, LaunchpadFormView |
1621 | +): |
1622 | """Contains the common functionality of the Add and Edit views.""" |
1623 | |
1624 | schema = IOCIRecipeSubscription |
1625 | @@ -127,11 +134,13 @@ class OCIRecipeSubscriptionAddView(_OCIRecipeSubscriptionCreationView): |
1626 | # subscribed before continuing. |
1627 | if self.context.getSubscription(self.user) is not None: |
1628 | self.request.response.addNotification( |
1629 | - "You are already subscribed to this OCI recipe.") |
1630 | + "You are already subscribed to this OCI recipe." |
1631 | + ) |
1632 | else: |
1633 | self.context.subscribe(self.user, self.user) |
1634 | self.request.response.addNotification( |
1635 | - "You have subscribed to this OCI recipe.") |
1636 | + "You have subscribed to this OCI recipe." |
1637 | + ) |
1638 | |
1639 | |
1640 | class OCIRecipeSubscriptionAddOtherView(_OCIRecipeSubscriptionCreationView): |
1641 | @@ -150,12 +159,14 @@ class OCIRecipeSubscriptionAddOtherView(_OCIRecipeSubscriptionCreationView): |
1642 | if "person" in data: |
1643 | person = data["person"] |
1644 | subscription = self.context.getSubscription(person) |
1645 | - if (subscription is None |
1646 | - and not self.context.userCanBeSubscribed(person)): |
1647 | + if subscription is None and not self.context.userCanBeSubscribed( |
1648 | + person |
1649 | + ): |
1650 | self.setFieldError( |
1651 | "person", |
1652 | "Open and delegated teams cannot be subscribed to " |
1653 | - "private OCI recipes.") |
1654 | + "private OCI recipes.", |
1655 | + ) |
1656 | |
1657 | @action("Subscribe", name="subscribe_action") |
1658 | def subscribe_action(self, action, data): |
1659 | @@ -165,9 +176,11 @@ class OCIRecipeSubscriptionAddOtherView(_OCIRecipeSubscriptionCreationView): |
1660 | if subscription is None: |
1661 | self.context.subscribe(person, self.user) |
1662 | self.request.response.addNotification( |
1663 | - "%s has been subscribed to this OCI recipe." % |
1664 | - person.displayname) |
1665 | + "%s has been subscribed to this OCI recipe." |
1666 | + % person.displayname |
1667 | + ) |
1668 | else: |
1669 | self.request.response.addNotification( |
1670 | - "%s was already subscribed to this OCI recipe." % |
1671 | - person.displayname) |
1672 | + "%s was already subscribed to this OCI recipe." |
1673 | + % person.displayname |
1674 | + ) |
1675 | diff --git a/lib/lp/oci/browser/tests/test_ocirecipe.py b/lib/lp/oci/browser/tests/test_ocirecipe.py |
1676 | index b135ed8..3050f61 100644 |
1677 | --- a/lib/lp/oci/browser/tests/test_ocirecipe.py |
1678 | +++ b/lib/lp/oci/browser/tests/test_ocirecipe.py |
1679 | @@ -3,16 +3,13 @@ |
1680 | |
1681 | """Test OCI recipe views.""" |
1682 | |
1683 | -from datetime import ( |
1684 | - datetime, |
1685 | - timedelta, |
1686 | - ) |
1687 | +from datetime import datetime, timedelta |
1688 | from operator import attrgetter |
1689 | from urllib.parse import quote |
1690 | |
1691 | -from fixtures import FakeLogger |
1692 | import pytz |
1693 | import soupmatchers |
1694 | +from fixtures import FakeLogger |
1695 | from storm.locals import Store |
1696 | from testtools.matchers import ( |
1697 | Equals, |
1698 | @@ -21,7 +18,7 @@ from testtools.matchers import ( |
1699 | MatchesSetwise, |
1700 | MatchesStructure, |
1701 | Not, |
1702 | - ) |
1703 | +) |
1704 | from zope.component import getUtility |
1705 | from zope.publisher.interfaces import NotFound |
1706 | from zope.security.interfaces import Unauthorized |
1707 | @@ -38,20 +35,18 @@ from lp.oci.browser.ocirecipe import ( |
1708 | OCIRecipeAdminView, |
1709 | OCIRecipeEditView, |
1710 | OCIRecipeView, |
1711 | - ) |
1712 | +) |
1713 | from lp.oci.interfaces.ocipushrule import ( |
1714 | IOCIPushRuleSet, |
1715 | OCIPushRuleAlreadyExists, |
1716 | - ) |
1717 | +) |
1718 | from lp.oci.interfaces.ocirecipe import ( |
1719 | + OCI_RECIPE_ALLOW_CREATE, |
1720 | CannotModifyOCIRecipeProcessor, |
1721 | IOCIRecipeSet, |
1722 | - OCI_RECIPE_ALLOW_CREATE, |
1723 | - ) |
1724 | +) |
1725 | from lp.oci.interfaces.ocirecipejob import IOCIRecipeRequestBuildsJobSource |
1726 | -from lp.oci.interfaces.ociregistrycredentials import ( |
1727 | - IOCIRegistryCredentialsSet, |
1728 | - ) |
1729 | +from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentialsSet |
1730 | from lp.oci.tests.helpers import OCIConfigHelperMixin |
1731 | from lp.registry.enums import BranchSharingPolicy |
1732 | from lp.registry.interfaces.person import IPersonSet |
1733 | @@ -64,36 +59,27 @@ from lp.services.propertycache import get_property_cache |
1734 | from lp.services.webapp import canonical_url |
1735 | from lp.services.webapp.servers import LaunchpadTestRequest |
1736 | from lp.testing import ( |
1737 | + BrowserTestCase, |
1738 | + TestCaseWithFactory, |
1739 | admin_logged_in, |
1740 | anonymous_logged_in, |
1741 | - BrowserTestCase, |
1742 | login, |
1743 | login_person, |
1744 | person_logged_in, |
1745 | record_two_runs, |
1746 | - TestCaseWithFactory, |
1747 | time_counter, |
1748 | - ) |
1749 | +) |
1750 | from lp.testing.dbuser import dbuser |
1751 | -from lp.testing.layers import ( |
1752 | - DatabaseFunctionalLayer, |
1753 | - LaunchpadFunctionalLayer, |
1754 | - ) |
1755 | -from lp.testing.matchers import ( |
1756 | - MatchesPickerText, |
1757 | - MatchesTagText, |
1758 | - ) |
1759 | +from lp.testing.layers import DatabaseFunctionalLayer, LaunchpadFunctionalLayer |
1760 | +from lp.testing.matchers import MatchesPickerText, MatchesTagText |
1761 | from lp.testing.pages import ( |
1762 | extract_text, |
1763 | find_main_content, |
1764 | find_tag_by_id, |
1765 | find_tags_by_class, |
1766 | - ) |
1767 | +) |
1768 | from lp.testing.publication import test_traverse |
1769 | -from lp.testing.views import ( |
1770 | - create_initialized_view, |
1771 | - create_view, |
1772 | - ) |
1773 | +from lp.testing.views import create_initialized_view, create_view |
1774 | |
1775 | |
1776 | class TestOCIRecipeNavigation(TestCaseWithFactory): |
1777 | @@ -102,19 +88,25 @@ class TestOCIRecipeNavigation(TestCaseWithFactory): |
1778 | |
1779 | def setUp(self): |
1780 | super().setUp() |
1781 | - self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'})) |
1782 | + self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"})) |
1783 | |
1784 | def test_canonical_url(self): |
1785 | owner = self.factory.makePerson(name="person") |
1786 | distribution = self.factory.makeDistribution(name="distro") |
1787 | oci_project = self.factory.makeOCIProject( |
1788 | - pillar=distribution, ociprojectname="oci-project") |
1789 | + pillar=distribution, ociprojectname="oci-project" |
1790 | + ) |
1791 | recipe = self.factory.makeOCIRecipe( |
1792 | - name="recipe", registrant=owner, owner=owner, |
1793 | - oci_project=oci_project) |
1794 | + name="recipe", |
1795 | + registrant=owner, |
1796 | + owner=owner, |
1797 | + oci_project=oci_project, |
1798 | + ) |
1799 | self.assertEqual( |
1800 | "http://launchpad.test/~person/distro/+oci/oci-project/" |
1801 | - "+recipe/recipe", canonical_url(recipe)) |
1802 | + "+recipe/recipe", |
1803 | + canonical_url(recipe), |
1804 | + ) |
1805 | |
1806 | def test_recipe_traverse_distribution(self): |
1807 | # Make sure we can reach recipe of distro-based OCI projects. |
1808 | @@ -122,9 +114,14 @@ class TestOCIRecipeNavigation(TestCaseWithFactory): |
1809 | oci_project = self.factory.makeOCIProject(pillar=distro) |
1810 | recipe = self.factory.makeOCIRecipe(oci_project=oci_project) |
1811 | obj, _, _ = test_traverse( |
1812 | - "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s" % ( |
1813 | - recipe.owner.name, recipe.oci_project.pillar.name, |
1814 | - recipe.oci_project.name, recipe.name)) |
1815 | + "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s" |
1816 | + % ( |
1817 | + recipe.owner.name, |
1818 | + recipe.oci_project.pillar.name, |
1819 | + recipe.oci_project.name, |
1820 | + recipe.name, |
1821 | + ) |
1822 | + ) |
1823 | self.assertEqual(recipe, obj) |
1824 | |
1825 | def test_recipe_traverse_project(self): |
1826 | @@ -133,9 +130,14 @@ class TestOCIRecipeNavigation(TestCaseWithFactory): |
1827 | oci_project = self.factory.makeOCIProject(pillar=project) |
1828 | recipe = self.factory.makeOCIRecipe(oci_project=oci_project) |
1829 | obj, _, _ = test_traverse( |
1830 | - "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s" % ( |
1831 | - recipe.owner.name, recipe.oci_project.pillar.name, |
1832 | - recipe.oci_project.name, recipe.name)) |
1833 | + "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s" |
1834 | + % ( |
1835 | + recipe.owner.name, |
1836 | + recipe.oci_project.pillar.name, |
1837 | + recipe.oci_project.name, |
1838 | + recipe.name, |
1839 | + ) |
1840 | + ) |
1841 | self.assertEqual(recipe, obj) |
1842 | |
1843 | |
1844 | @@ -147,20 +149,24 @@ class BaseTestOCIRecipeView(BrowserTestCase): |
1845 | super().setUp() |
1846 | self.useFixture(FakeLogger()) |
1847 | self.person = self.factory.makePerson( |
1848 | - name="test-person", displayname="Test Person") |
1849 | + name="test-person", displayname="Test Person" |
1850 | + ) |
1851 | |
1852 | |
1853 | class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
1854 | - |
1855 | def setUp(self): |
1856 | super().setUp() |
1857 | self.distroseries = self.factory.makeDistroSeries() |
1858 | self.distribution = self.distroseries.distribution |
1859 | - self.useFixture(FeatureFixture({ |
1860 | - OCI_RECIPE_ALLOW_CREATE: "on", |
1861 | - "oci.build_series.%s" % self.distribution.name: |
1862 | - self.distroseries.name, |
1863 | - })) |
1864 | + self.useFixture( |
1865 | + FeatureFixture( |
1866 | + { |
1867 | + OCI_RECIPE_ALLOW_CREATE: "on", |
1868 | + "oci.build_series.%s" |
1869 | + % self.distribution.name: self.distroseries.name, |
1870 | + } |
1871 | + ) |
1872 | + ) |
1873 | self.setConfig() |
1874 | |
1875 | def setUpDistroSeries(self): |
1876 | @@ -169,138 +175,166 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
1877 | for name in processor_names: |
1878 | processor = getUtility(IProcessorSet).getByName(name) |
1879 | self.factory.makeDistroArchSeries( |
1880 | - distroseries=self.distroseries, architecturetag=name, |
1881 | - processor=processor) |
1882 | + distroseries=self.distroseries, |
1883 | + architecturetag=name, |
1884 | + processor=processor, |
1885 | + ) |
1886 | |
1887 | def assertProcessorControls(self, processors_control, enabled, disabled): |
1888 | matchers = [ |
1889 | MatchesStructure.byEquality(optionValue=name, disabled=False) |
1890 | - for name in enabled] |
1891 | - matchers.extend([ |
1892 | - MatchesStructure.byEquality(optionValue=name, disabled=True) |
1893 | - for name in disabled]) |
1894 | + for name in enabled |
1895 | + ] |
1896 | + matchers.extend( |
1897 | + [ |
1898 | + MatchesStructure.byEquality(optionValue=name, disabled=True) |
1899 | + for name in disabled |
1900 | + ] |
1901 | + ) |
1902 | self.assertThat(processors_control.controls, MatchesSetwise(*matchers)) |
1903 | |
1904 | def test_create_new_recipe_not_logged_in(self): |
1905 | oci_project = self.factory.makeOCIProject() |
1906 | self.assertRaises( |
1907 | - Unauthorized, self.getViewBrowser, oci_project, |
1908 | - view_name="+new-recipe", no_login=True) |
1909 | + Unauthorized, |
1910 | + self.getViewBrowser, |
1911 | + oci_project, |
1912 | + view_name="+new-recipe", |
1913 | + no_login=True, |
1914 | + ) |
1915 | |
1916 | def test_create_new_recipe(self): |
1917 | oci_project = self.factory.makeOCIProject() |
1918 | oci_project_display = oci_project.display_name |
1919 | - [git_ref] = self.factory.makeGitRefs( |
1920 | - paths=['refs/heads/v2.0-20.04']) |
1921 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
1922 | source_display = git_ref.display_name |
1923 | browser = self.getViewBrowser( |
1924 | - oci_project, view_name="+new-recipe", user=self.person) |
1925 | + oci_project, view_name="+new-recipe", user=self.person |
1926 | + ) |
1927 | browser.getControl(name="field.name").value = "recipe-name" |
1928 | browser.getControl("Description").value = "Recipe description" |
1929 | - browser.getControl(name="field.git_ref.repository").value = ( |
1930 | - git_ref.repository.identity) |
1931 | + browser.getControl( |
1932 | + name="field.git_ref.repository" |
1933 | + ).value = git_ref.repository.identity |
1934 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
1935 | browser.getControl("Create OCI recipe").click() |
1936 | |
1937 | content = find_main_content(browser.contents) |
1938 | self.assertEqual("recipe-name", extract_text(content.h1)) |
1939 | self.assertThat( |
1940 | - "Recipe description", |
1941 | - MatchesTagText(content, "recipe-description")) |
1942 | + "Recipe description", MatchesTagText(content, "recipe-description") |
1943 | + ) |
1944 | self.assertThat( |
1945 | - "Test Person", MatchesPickerText(content, "edit-owner")) |
1946 | + "Test Person", MatchesPickerText(content, "edit-owner") |
1947 | + ) |
1948 | self.assertThat( |
1949 | "OCI project:\n%s" % oci_project_display, |
1950 | - MatchesTagText(content, "oci-project")) |
1951 | + MatchesTagText(content, "oci-project"), |
1952 | + ) |
1953 | self.assertThat( |
1954 | "Source:\n%s\nEdit OCI recipe" % source_display, |
1955 | - MatchesTagText(content, "source")) |
1956 | + MatchesTagText(content, "source"), |
1957 | + ) |
1958 | self.assertThat( |
1959 | "Build file path:\nDockerfile\n" |
1960 | "Edit OCI recipe\n" |
1961 | "Build context directory:\n.\n" |
1962 | "Edit OCI recipe", |
1963 | - MatchesTagText(content, "build-file")) |
1964 | + MatchesTagText(content, "build-file"), |
1965 | + ) |
1966 | self.assertThat( |
1967 | "Build schedule:\nBuilt on request\nEdit OCI recipe\n", |
1968 | - MatchesTagText(content, "build-schedule")) |
1969 | + MatchesTagText(content, "build-schedule"), |
1970 | + ) |
1971 | self.assertThat( |
1972 | - "Official recipe:\nNo", |
1973 | - MatchesTagText(content, "official-recipe")) |
1974 | + "Official recipe:\nNo", MatchesTagText(content, "official-recipe") |
1975 | + ) |
1976 | |
1977 | def test_create_new_available_information_types(self): |
1978 | public_pillar = self.factory.makeProduct(owner=self.person) |
1979 | private_pillar = self.factory.makeProduct( |
1980 | owner=self.person, |
1981 | information_type=InformationType.PROPRIETARY, |
1982 | - branch_sharing_policy=BranchSharingPolicy.PROPRIETARY) |
1983 | + branch_sharing_policy=BranchSharingPolicy.PROPRIETARY, |
1984 | + ) |
1985 | public_oci_project = self.factory.makeOCIProject( |
1986 | - registrant=self.person, pillar=public_pillar) |
1987 | + registrant=self.person, pillar=public_pillar |
1988 | + ) |
1989 | private_oci_project = self.factory.makeOCIProject( |
1990 | - registrant=self.person, pillar=private_pillar) |
1991 | + registrant=self.person, pillar=private_pillar |
1992 | + ) |
1993 | |
1994 | # Public pillar. |
1995 | browser = self.getViewBrowser( |
1996 | - public_oci_project, view_name="+new-recipe", user=self.person) |
1997 | + public_oci_project, view_name="+new-recipe", user=self.person |
1998 | + ) |
1999 | self.assertContentEqual( |
2000 | - ['PUBLIC', 'PUBLICSECURITY', 'PRIVATESECURITY', 'USERDATA'], |
2001 | - browser.getControl(name="field.information_type").options) |
2002 | + ["PUBLIC", "PUBLICSECURITY", "PRIVATESECURITY", "USERDATA"], |
2003 | + browser.getControl(name="field.information_type").options, |
2004 | + ) |
2005 | |
2006 | # Proprietary pillar. |
2007 | browser = self.getViewBrowser( |
2008 | - private_oci_project, view_name="+new-recipe", user=self.person) |
2009 | + private_oci_project, view_name="+new-recipe", user=self.person |
2010 | + ) |
2011 | self.assertContentEqual( |
2012 | - ['PROPRIETARY'], |
2013 | - browser.getControl(name="field.information_type").options) |
2014 | + ["PROPRIETARY"], |
2015 | + browser.getControl(name="field.information_type").options, |
2016 | + ) |
2017 | |
2018 | def test_create_new_recipe_invalid_format(self): |
2019 | oci_project = self.factory.makeOCIProject() |
2020 | - [git_ref] = self.factory.makeGitRefs( |
2021 | - paths=['refs/heads/invalid']) |
2022 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/invalid"]) |
2023 | browser = self.getViewBrowser( |
2024 | - oci_project, view_name="+new-recipe", user=self.person) |
2025 | + oci_project, view_name="+new-recipe", user=self.person |
2026 | + ) |
2027 | browser.getControl(name="field.name").value = "recipe-name" |
2028 | browser.getControl("Description").value = "Recipe description" |
2029 | - browser.getControl(name="field.git_ref.repository").value = ( |
2030 | - git_ref.repository.identity) |
2031 | + browser.getControl( |
2032 | + name="field.git_ref.repository" |
2033 | + ).value = git_ref.repository.identity |
2034 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
2035 | browser.getControl("Create OCI recipe").click() |
2036 | self.assertIn("Branch does not match format", browser.contents) |
2037 | |
2038 | def test_create_new_recipe_with_build_args(self): |
2039 | oci_project = self.factory.makeOCIProject() |
2040 | - [git_ref] = self.factory.makeGitRefs( |
2041 | - paths=['refs/heads/v2.0-20.04']) |
2042 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
2043 | browser = self.getViewBrowser( |
2044 | - oci_project, view_name="+new-recipe", user=self.person) |
2045 | + oci_project, view_name="+new-recipe", user=self.person |
2046 | + ) |
2047 | browser.getControl(name="field.name").value = "recipe-name" |
2048 | browser.getControl("Description").value = "Recipe description" |
2049 | - browser.getControl(name="field.git_ref.repository").value = ( |
2050 | - git_ref.repository.identity) |
2051 | + browser.getControl( |
2052 | + name="field.git_ref.repository" |
2053 | + ).value = git_ref.repository.identity |
2054 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
2055 | - browser.getControl("Build-time ARG variables").value = ( |
2056 | - "VAR1=10\nVAR2=20") |
2057 | + browser.getControl( |
2058 | + "Build-time ARG variables" |
2059 | + ).value = "VAR1=10\nVAR2=20" |
2060 | browser.getControl("Create OCI recipe").click() |
2061 | |
2062 | content = find_main_content(browser.contents) |
2063 | self.assertEqual("recipe-name", extract_text(content.h1)) |
2064 | self.assertThat( |
2065 | "Build-time\nARG variables:\nVAR1=10\nVAR2=20", |
2066 | - MatchesTagText(content, "build-args")) |
2067 | + MatchesTagText(content, "build-args"), |
2068 | + ) |
2069 | |
2070 | def test_create_new_recipe_with_image_name(self): |
2071 | oci_project = self.factory.makeOCIProject() |
2072 | credentials = self.factory.makeOCIRegistryCredentials() |
2073 | with person_logged_in(oci_project.distribution.owner): |
2074 | oci_project.distribution.oci_registry_credentials = credentials |
2075 | - [git_ref] = self.factory.makeGitRefs( |
2076 | - paths=['refs/heads/v2.0-20.04']) |
2077 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
2078 | browser = self.getViewBrowser( |
2079 | - oci_project, view_name="+new-recipe", user=self.person) |
2080 | + oci_project, view_name="+new-recipe", user=self.person |
2081 | + ) |
2082 | browser.getControl(name="field.name").value = "recipe-name" |
2083 | browser.getControl("Description").value = "Recipe description" |
2084 | - browser.getControl(name="field.git_ref.repository").value = ( |
2085 | - git_ref.repository.identity) |
2086 | + browser.getControl( |
2087 | + name="field.git_ref.repository" |
2088 | + ).value = git_ref.repository.identity |
2089 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
2090 | |
2091 | image_name = self.factory.getUniqueUnicode() |
2092 | @@ -309,29 +343,35 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2093 | content = find_main_content(browser.contents) |
2094 | self.assertThat( |
2095 | "Registry image name:\n{}".format(image_name), |
2096 | - MatchesTagText(content, "image-name")) |
2097 | + MatchesTagText(content, "image-name"), |
2098 | + ) |
2099 | |
2100 | def test_create_new_recipe_users_teams_as_owner_options(self): |
2101 | # Teams that the user is in are options for the OCI recipe owner. |
2102 | self.factory.makeTeam( |
2103 | - name="test-team", displayname="Test Team", members=[self.person]) |
2104 | + name="test-team", displayname="Test Team", members=[self.person] |
2105 | + ) |
2106 | oci_project = self.factory.makeOCIProject() |
2107 | browser = self.getViewBrowser( |
2108 | - oci_project, view_name="+new-recipe", user=self.person) |
2109 | + oci_project, view_name="+new-recipe", user=self.person |
2110 | + ) |
2111 | options = browser.getControl("Owner").displayOptions |
2112 | self.assertEqual( |
2113 | ["Test Person (test-person)", "Test Team (test-team)"], |
2114 | - sorted(str(option) for option in options)) |
2115 | + sorted(str(option) for option in options), |
2116 | + ) |
2117 | |
2118 | def test_create_new_recipe_display_processors(self): |
2119 | self.setUpDistroSeries() |
2120 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2121 | browser = self.getViewBrowser( |
2122 | - oci_project, view_name="+new-recipe", user=self.person) |
2123 | + oci_project, view_name="+new-recipe", user=self.person |
2124 | + ) |
2125 | processors = browser.getControl(name="field.processors") |
2126 | self.assertContentEqual( |
2127 | ["Intel 386 (386)", "AMD 64bit (amd64)", "HPPA Processor (hppa)"], |
2128 | - [extract_text(option) for option in processors.displayOptions]) |
2129 | + [extract_text(option) for option in processors.displayOptions], |
2130 | + ) |
2131 | self.assertContentEqual(["386", "amd64", "hppa"], processors.options) |
2132 | self.assertContentEqual(["386", "amd64", "hppa"], processors.value) |
2133 | |
2134 | @@ -341,35 +381,43 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2135 | self.setUpDistroSeries() |
2136 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2137 | proc_armhf = self.factory.makeProcessor( |
2138 | - name="armhf", restricted=True, build_by_default=False) |
2139 | + name="armhf", restricted=True, build_by_default=False |
2140 | + ) |
2141 | self.factory.makeDistroArchSeries( |
2142 | - distroseries=self.distroseries, architecturetag="armhf", |
2143 | - processor=proc_armhf) |
2144 | + distroseries=self.distroseries, |
2145 | + architecturetag="armhf", |
2146 | + processor=proc_armhf, |
2147 | + ) |
2148 | browser = self.getViewBrowser( |
2149 | - oci_project, view_name="+new-recipe", user=self.person) |
2150 | + oci_project, view_name="+new-recipe", user=self.person |
2151 | + ) |
2152 | processors = browser.getControl(name="field.processors") |
2153 | self.assertProcessorControls( |
2154 | - processors, ["386", "amd64", "hppa"], ["armhf"]) |
2155 | + processors, ["386", "amd64", "hppa"], ["armhf"] |
2156 | + ) |
2157 | |
2158 | def test_create_new_recipe_processors(self): |
2159 | self.setUpDistroSeries() |
2160 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2161 | - [git_ref] = self.factory.makeGitRefs( |
2162 | - paths=['refs/heads/v2.0-20.04']) |
2163 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
2164 | browser = self.getViewBrowser( |
2165 | - oci_project, view_name="+new-recipe", user=self.person) |
2166 | + oci_project, view_name="+new-recipe", user=self.person |
2167 | + ) |
2168 | processors = browser.getControl(name="field.processors") |
2169 | processors.value = ["386", "amd64"] |
2170 | browser.getControl(name="field.name").value = "recipe-name" |
2171 | - browser.getControl(name="field.git_ref.repository").value = ( |
2172 | - git_ref.repository.identity) |
2173 | + browser.getControl( |
2174 | + name="field.git_ref.repository" |
2175 | + ).value = git_ref.repository.identity |
2176 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
2177 | browser.getControl("Create OCI recipe").click() |
2178 | login_person(self.person) |
2179 | recipe = getUtility(IOCIRecipeSet).getByName( |
2180 | - self.person, oci_project, "recipe-name") |
2181 | + self.person, oci_project, "recipe-name" |
2182 | + ) |
2183 | self.assertContentEqual( |
2184 | - ["386", "amd64"], [proc.name for proc in recipe.processors]) |
2185 | + ["386", "amd64"], [proc.name for proc in recipe.processors] |
2186 | + ) |
2187 | |
2188 | def test_create_new_recipe_no_default_repo_warning(self): |
2189 | self.setUpDistroSeries() |
2190 | @@ -377,12 +425,14 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2191 | with admin_logged_in(): |
2192 | oci_project_url = canonical_url(oci_project) |
2193 | browser = self.getViewBrowser( |
2194 | - oci_project, view_name="+new-recipe", user=self.person) |
2195 | + oci_project, view_name="+new-recipe", user=self.person |
2196 | + ) |
2197 | error_message = ( |
2198 | - 'The default git repository for this OCI project was not created ' |
2199 | - 'yet.<br/>' |
2200 | + "The default git repository for this OCI project was not created " |
2201 | + "yet.<br/>" |
2202 | 'Check the <a href="{url}">OCI project page</a> for instructions ' |
2203 | - 'on how to create one.').format(url=oci_project_url) |
2204 | + "on how to create one." |
2205 | + ).format(url=oci_project_url) |
2206 | self.assertIn(error_message, browser.contents) |
2207 | |
2208 | def test_create_new_recipe_with_default_repo_already_created(self): |
2209 | @@ -390,52 +440,73 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2210 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2211 | repository = self.factory.makeGitRepository( |
2212 | name=oci_project.name, |
2213 | - target=oci_project, owner=self.person, registrant=self.person) |
2214 | + target=oci_project, |
2215 | + owner=self.person, |
2216 | + registrant=self.person, |
2217 | + ) |
2218 | with person_logged_in(self.distribution.owner): |
2219 | getUtility(IGitRepositorySet).setDefaultRepository( |
2220 | - oci_project, repository) |
2221 | + oci_project, repository |
2222 | + ) |
2223 | default_repo_path = "%s/+oci/%s" % ( |
2224 | - self.distribution.name, oci_project.name) |
2225 | + self.distribution.name, |
2226 | + oci_project.name, |
2227 | + ) |
2228 | browser = self.getViewBrowser( |
2229 | - oci_project, view_name="+new-recipe", user=self.person) |
2230 | + oci_project, view_name="+new-recipe", user=self.person |
2231 | + ) |
2232 | error_message = ( |
2233 | - 'The default git repository for this OCI project was not created ' |
2234 | - 'yet.') |
2235 | + "The default git repository for this OCI project was not created " |
2236 | + "yet." |
2237 | + ) |
2238 | self.assertNotIn(error_message, browser.contents) |
2239 | - self.assertThat(browser.contents, soupmatchers.HTMLContains( |
2240 | - soupmatchers.Tag( |
2241 | - 'Repository pre-filled', 'input', attrs={ |
2242 | - "id": "field.git_ref.repository", |
2243 | - "value": default_repo_path}))) |
2244 | + self.assertThat( |
2245 | + browser.contents, |
2246 | + soupmatchers.HTMLContains( |
2247 | + soupmatchers.Tag( |
2248 | + "Repository pre-filled", |
2249 | + "input", |
2250 | + attrs={ |
2251 | + "id": "field.git_ref.repository", |
2252 | + "value": default_repo_path, |
2253 | + }, |
2254 | + ) |
2255 | + ), |
2256 | + ) |
2257 | |
2258 | def test_official_is_disabled(self): |
2259 | oci_project = self.factory.makeOCIProject() |
2260 | browser = self.getViewBrowser( |
2261 | - oci_project, view_name="+new-recipe", user=self.person) |
2262 | + oci_project, view_name="+new-recipe", user=self.person |
2263 | + ) |
2264 | official_control = browser.getControl("Official recipe") |
2265 | self.assertTrue(official_control.disabled) |
2266 | |
2267 | def test_official_is_enabled(self): |
2268 | distribution = self.factory.makeDistribution( |
2269 | - oci_project_admin=self.person) |
2270 | + oci_project_admin=self.person |
2271 | + ) |
2272 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
2273 | browser = self.getViewBrowser( |
2274 | - oci_project, view_name="+new-recipe", user=self.person) |
2275 | + oci_project, view_name="+new-recipe", user=self.person |
2276 | + ) |
2277 | official_control = browser.getControl("Official recipe") |
2278 | self.assertFalse(official_control.disabled) |
2279 | |
2280 | def test_set_official(self): |
2281 | distribution = self.factory.makeDistribution( |
2282 | - oci_project_admin=self.person) |
2283 | + oci_project_admin=self.person |
2284 | + ) |
2285 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
2286 | - [git_ref] = self.factory.makeGitRefs( |
2287 | - paths=['refs/heads/v2.0-20.04']) |
2288 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
2289 | browser = self.getViewBrowser( |
2290 | - oci_project, view_name="+new-recipe", user=self.person) |
2291 | + oci_project, view_name="+new-recipe", user=self.person |
2292 | + ) |
2293 | browser.getControl(name="field.name").value = "recipe-name" |
2294 | browser.getControl("Description").value = "Recipe description" |
2295 | - browser.getControl(name="field.git_ref.repository").value = ( |
2296 | - git_ref.repository.identity) |
2297 | + browser.getControl( |
2298 | + name="field.git_ref.repository" |
2299 | + ).value = git_ref.repository.identity |
2300 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
2301 | official_control = browser.getControl("Official recipe") |
2302 | official_control.selected = True |
2303 | @@ -443,28 +514,29 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2304 | |
2305 | content = find_main_content(browser.contents) |
2306 | self.assertThat( |
2307 | - "Official recipe:\nYes", |
2308 | - MatchesTagText(content, "official-recipe")) |
2309 | + "Official recipe:\nYes", MatchesTagText(content, "official-recipe") |
2310 | + ) |
2311 | |
2312 | def test_set_official_multiple(self): |
2313 | distribution = self.factory.makeDistribution( |
2314 | - oci_project_admin=self.person) |
2315 | + oci_project_admin=self.person |
2316 | + ) |
2317 | |
2318 | # do it once |
2319 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
2320 | - [git_ref] = self.factory.makeGitRefs( |
2321 | - paths=['refs/heads/v2.0-20.04']) |
2322 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
2323 | |
2324 | # and then do it again |
2325 | oci_project2 = self.factory.makeOCIProject(pillar=distribution) |
2326 | - [git_ref2] = self.factory.makeGitRefs( |
2327 | - paths=['refs/heads/v3.0-20.04']) |
2328 | + [git_ref2] = self.factory.makeGitRefs(paths=["refs/heads/v3.0-20.04"]) |
2329 | browser = self.getViewBrowser( |
2330 | - oci_project, view_name="+new-recipe", user=self.person) |
2331 | + oci_project, view_name="+new-recipe", user=self.person |
2332 | + ) |
2333 | browser.getControl(name="field.name").value = "recipe-name" |
2334 | browser.getControl("Description").value = "Recipe description" |
2335 | - browser.getControl(name="field.git_ref.repository").value = ( |
2336 | - git_ref.repository.identity) |
2337 | + browser.getControl( |
2338 | + name="field.git_ref.repository" |
2339 | + ).value = git_ref.repository.identity |
2340 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
2341 | official_control = browser.getControl("Official recipe") |
2342 | official_control.selected = True |
2343 | @@ -472,15 +544,17 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2344 | |
2345 | content = find_main_content(browser.contents) |
2346 | self.assertThat( |
2347 | - "Official recipe:\nYes", |
2348 | - MatchesTagText(content, "official-recipe")) |
2349 | + "Official recipe:\nYes", MatchesTagText(content, "official-recipe") |
2350 | + ) |
2351 | |
2352 | browser2 = self.getViewBrowser( |
2353 | - oci_project2, view_name="+new-recipe", user=self.person) |
2354 | + oci_project2, view_name="+new-recipe", user=self.person |
2355 | + ) |
2356 | browser2.getControl(name="field.name").value = "recipe-name" |
2357 | browser2.getControl("Description").value = "Recipe description" |
2358 | - browser2.getControl(name="field.git_ref.repository").value = ( |
2359 | - git_ref2.repository.identity) |
2360 | + browser2.getControl( |
2361 | + name="field.git_ref.repository" |
2362 | + ).value = git_ref2.repository.identity |
2363 | browser2.getControl(name="field.git_ref.path").value = git_ref2.path |
2364 | official_control = browser2.getControl("Official recipe") |
2365 | official_control.selected = True |
2366 | @@ -488,28 +562,30 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2367 | |
2368 | content = find_main_content(browser2.contents) |
2369 | self.assertThat( |
2370 | - "Official recipe:\nYes", |
2371 | - MatchesTagText(content, "official-recipe")) |
2372 | + "Official recipe:\nYes", MatchesTagText(content, "official-recipe") |
2373 | + ) |
2374 | |
2375 | browser.reload() |
2376 | content = find_main_content(browser.contents) |
2377 | self.assertThat( |
2378 | - "Official recipe:\nYes", |
2379 | - MatchesTagText(content, "official-recipe")) |
2380 | + "Official recipe:\nYes", MatchesTagText(content, "official-recipe") |
2381 | + ) |
2382 | |
2383 | def test_set_official_no_permissions(self): |
2384 | distro_owner = self.factory.makePerson() |
2385 | distribution = self.factory.makeDistribution( |
2386 | - oci_project_admin=distro_owner) |
2387 | + oci_project_admin=distro_owner |
2388 | + ) |
2389 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
2390 | - [git_ref] = self.factory.makeGitRefs( |
2391 | - paths=['refs/heads/v2.0-20.04']) |
2392 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
2393 | browser = self.getViewBrowser( |
2394 | - oci_project, view_name="+new-recipe", user=self.person) |
2395 | + oci_project, view_name="+new-recipe", user=self.person |
2396 | + ) |
2397 | browser.getControl(name="field.name").value = "recipe-name" |
2398 | browser.getControl("Description").value = "Recipe description" |
2399 | - browser.getControl(name="field.git_ref.repository").value = ( |
2400 | - git_ref.repository.identity) |
2401 | + browser.getControl( |
2402 | + name="field.git_ref.repository" |
2403 | + ).value = git_ref.repository.identity |
2404 | browser.getControl(name="field.git_ref.path").value = git_ref.path |
2405 | official_control = browser.getControl("Official recipe") |
2406 | official_control.selected = True |
2407 | @@ -517,19 +593,21 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2408 | |
2409 | error_message = ( |
2410 | "You do not have permission to set the official status " |
2411 | - "of this recipe.") |
2412 | + "of this recipe." |
2413 | + ) |
2414 | self.assertIn(error_message, browser.contents) |
2415 | |
2416 | def test_create_recipe_doesnt_override_gitref_errors(self): |
2417 | oci_project = self.factory.makeOCIProject() |
2418 | - [git_ref] = self.factory.makeGitRefs( |
2419 | - paths=['refs/heads/v2.0-20.04']) |
2420 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"]) |
2421 | browser = self.getViewBrowser( |
2422 | - oci_project, view_name="+new-recipe", user=self.person) |
2423 | + oci_project, view_name="+new-recipe", user=self.person |
2424 | + ) |
2425 | browser.getControl(name="field.name").value = "recipe-name" |
2426 | browser.getControl("Description").value = "Recipe description" |
2427 | - browser.getControl(name="field.git_ref.repository").value = ( |
2428 | - git_ref.repository.identity) |
2429 | + browser.getControl( |
2430 | + name="field.git_ref.repository" |
2431 | + ).value = git_ref.repository.identity |
2432 | browser.getControl(name="field.git_ref.path").value = "non-exist" |
2433 | browser.getControl("Create OCI recipe").click() |
2434 | |
2435 | @@ -538,10 +616,9 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2436 | |
2437 | |
2438 | class TestOCIRecipeAdminView(BaseTestOCIRecipeView): |
2439 | - |
2440 | def setUp(self): |
2441 | super().setUp() |
2442 | - self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'})) |
2443 | + self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"})) |
2444 | |
2445 | def test_unauthorized(self): |
2446 | # A non-admin user cannot administer an OCI recipe. |
2447 | @@ -550,16 +627,21 @@ class TestOCIRecipeAdminView(BaseTestOCIRecipeView): |
2448 | recipe_url = canonical_url(recipe) |
2449 | browser = self.getViewBrowser(recipe, user=self.person) |
2450 | self.assertRaises( |
2451 | - LinkNotFoundError, browser.getLink, "Administer OCI recipe") |
2452 | + LinkNotFoundError, browser.getLink, "Administer OCI recipe" |
2453 | + ) |
2454 | self.assertRaises( |
2455 | - Unauthorized, self.getUserBrowser, recipe_url + "/+admin", |
2456 | - user=self.person) |
2457 | + Unauthorized, |
2458 | + self.getUserBrowser, |
2459 | + recipe_url + "/+admin", |
2460 | + user=self.person, |
2461 | + ) |
2462 | |
2463 | def test_admin_recipe(self): |
2464 | # Admins can change require_virtualized. |
2465 | login("admin@canonical.com") |
2466 | commercial_admin = self.factory.makePerson( |
2467 | - member_of=[getUtility(ILaunchpadCelebrities).commercial_admin]) |
2468 | + member_of=[getUtility(ILaunchpadCelebrities).commercial_admin] |
2469 | + ) |
2470 | login_person(self.person) |
2471 | recipe = self.factory.makeOCIRecipe(registrant=self.person) |
2472 | self.assertTrue(recipe.require_virtualized) |
2473 | @@ -579,30 +661,36 @@ class TestOCIRecipeAdminView(BaseTestOCIRecipeView): |
2474 | # Administering an OCI recipe sets the date_last_modified property. |
2475 | login("admin@canonical.com") |
2476 | ppa_admin = self.factory.makePerson( |
2477 | - member_of=[getUtility(ILaunchpadCelebrities).ppa_admin]) |
2478 | + member_of=[getUtility(ILaunchpadCelebrities).ppa_admin] |
2479 | + ) |
2480 | login_person(self.person) |
2481 | date_created = datetime(2000, 1, 1, tzinfo=pytz.UTC) |
2482 | recipe = self.factory.makeOCIRecipe( |
2483 | - registrant=self.person, date_created=date_created) |
2484 | + registrant=self.person, date_created=date_created |
2485 | + ) |
2486 | login_person(ppa_admin) |
2487 | view = OCIRecipeAdminView(recipe, LaunchpadTestRequest()) |
2488 | view.initialize() |
2489 | view.request_action.success({"require_virtualized": False}) |
2490 | self.assertSqlAttributeEqualsDate( |
2491 | - recipe, "date_last_modified", UTC_NOW) |
2492 | + recipe, "date_last_modified", UTC_NOW |
2493 | + ) |
2494 | |
2495 | |
2496 | class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2497 | - |
2498 | def setUp(self): |
2499 | super().setUp() |
2500 | self.distroseries = self.factory.makeDistroSeries() |
2501 | self.distribution = self.distroseries.distribution |
2502 | - self.useFixture(FeatureFixture({ |
2503 | - OCI_RECIPE_ALLOW_CREATE: "on", |
2504 | - "oci.build_series.%s" % self.distribution.name: |
2505 | - self.distroseries.name, |
2506 | - })) |
2507 | + self.useFixture( |
2508 | + FeatureFixture( |
2509 | + { |
2510 | + OCI_RECIPE_ALLOW_CREATE: "on", |
2511 | + "oci.build_series.%s" |
2512 | + % self.distribution.name: self.distroseries.name, |
2513 | + } |
2514 | + ) |
2515 | + ) |
2516 | self.setConfig() |
2517 | |
2518 | def setUpDistroSeries(self): |
2519 | @@ -611,34 +699,45 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2520 | for name in processor_names: |
2521 | processor = getUtility(IProcessorSet).getByName(name) |
2522 | self.factory.makeDistroArchSeries( |
2523 | - distroseries=self.distroseries, architecturetag=name, |
2524 | - processor=processor) |
2525 | + distroseries=self.distroseries, |
2526 | + architecturetag=name, |
2527 | + processor=processor, |
2528 | + ) |
2529 | |
2530 | def assertRecipeProcessors(self, recipe, names): |
2531 | self.assertContentEqual( |
2532 | - names, [processor.name for processor in recipe.processors]) |
2533 | + names, [processor.name for processor in recipe.processors] |
2534 | + ) |
2535 | |
2536 | def assertProcessorControls(self, processors_control, enabled, disabled): |
2537 | matchers = [ |
2538 | MatchesStructure.byEquality(optionValue=name, disabled=False) |
2539 | - for name in enabled] |
2540 | - matchers.extend([ |
2541 | - MatchesStructure.byEquality(optionValue=name, disabled=True) |
2542 | - for name in disabled]) |
2543 | + for name in enabled |
2544 | + ] |
2545 | + matchers.extend( |
2546 | + [ |
2547 | + MatchesStructure.byEquality(optionValue=name, disabled=True) |
2548 | + for name in disabled |
2549 | + ] |
2550 | + ) |
2551 | self.assertThat(processors_control.controls, MatchesSetwise(*matchers)) |
2552 | |
2553 | def assertShowsPrivateBanner(self, browser): |
2554 | banners = find_tags_by_class( |
2555 | - browser.contents, "private_banner_container") |
2556 | + browser.contents, "private_banner_container" |
2557 | + ) |
2558 | self.assertEqual(1, len(banners)) |
2559 | self.assertEqual( |
2560 | - 'The information on this page is private.', |
2561 | - extract_text(banners[0])) |
2562 | + "The information on this page is private.", |
2563 | + extract_text(banners[0]), |
2564 | + ) |
2565 | |
2566 | def test_edit_private_recipe_shows_banner(self): |
2567 | recipe = self.factory.makeOCIRecipe( |
2568 | - registrant=self.person, owner=self.person, |
2569 | - information_type=InformationType.USERDATA) |
2570 | + registrant=self.person, |
2571 | + owner=self.person, |
2572 | + information_type=InformationType.USERDATA, |
2573 | + ) |
2574 | browser = self.getViewBrowser(recipe, user=self.person) |
2575 | browser.getLink("Edit OCI recipe").click() |
2576 | self.assertShowsPrivateBanner(browser) |
2577 | @@ -647,14 +746,20 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2578 | oci_project = self.factory.makeOCIProject() |
2579 | oci_project_display = oci_project.display_name |
2580 | [old_git_ref] = self.factory.makeGitRefs( |
2581 | - paths=['refs/heads/v1.0-20.04']) |
2582 | + paths=["refs/heads/v1.0-20.04"] |
2583 | + ) |
2584 | recipe = self.factory.makeOCIRecipe( |
2585 | - registrant=self.person, owner=self.person, |
2586 | - oci_project=oci_project, git_ref=old_git_ref) |
2587 | + registrant=self.person, |
2588 | + owner=self.person, |
2589 | + oci_project=oci_project, |
2590 | + git_ref=old_git_ref, |
2591 | + ) |
2592 | self.factory.makeTeam( |
2593 | - name="new-team", displayname="New Team", members=[self.person]) |
2594 | + name="new-team", displayname="New Team", members=[self.person] |
2595 | + ) |
2596 | [new_git_ref] = self.factory.makeGitRefs( |
2597 | - paths=['refs/heads/v2.0-20.04']) |
2598 | + paths=["refs/heads/v2.0-20.04"] |
2599 | + ) |
2600 | self.factory.makeOCIPushRule(recipe=recipe) |
2601 | |
2602 | browser = self.getViewBrowser(recipe, user=self.person) |
2603 | @@ -662,8 +767,9 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2604 | browser.getControl("Owner").value = ["new-team"] |
2605 | browser.getControl(name="field.name").value = "new-name" |
2606 | browser.getControl("Description").value = "New description" |
2607 | - browser.getControl(name="field.git_ref.repository").value = ( |
2608 | - new_git_ref.repository.identity) |
2609 | + browser.getControl( |
2610 | + name="field.git_ref.repository" |
2611 | + ).value = new_git_ref.repository.identity |
2612 | browser.getControl(name="field.git_ref.path").value = new_git_ref.path |
2613 | browser.getControl("Build file path").value = "Dockerfile-2" |
2614 | browser.getControl("Build directory context").value = "apath" |
2615 | @@ -675,32 +781,42 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2616 | self.assertThat("New Team", MatchesPickerText(content, "edit-owner")) |
2617 | self.assertThat( |
2618 | "OCI project:\n%s" % oci_project_display, |
2619 | - MatchesTagText(content, "oci-project")) |
2620 | + MatchesTagText(content, "oci-project"), |
2621 | + ) |
2622 | self.assertThat( |
2623 | "Source:\n%s\nEdit OCI recipe" % new_git_ref.display_name, |
2624 | - MatchesTagText(content, "source")) |
2625 | + MatchesTagText(content, "source"), |
2626 | + ) |
2627 | self.assertThat( |
2628 | "Build file path:\nDockerfile-2\n" |
2629 | "Edit OCI recipe\n" |
2630 | "Build context directory:\napath\n" |
2631 | "Edit OCI recipe", |
2632 | - MatchesTagText(content, "build-file")) |
2633 | + MatchesTagText(content, "build-file"), |
2634 | + ) |
2635 | self.assertThat( |
2636 | "Build schedule:\nBuilt daily\nEdit OCI recipe\n", |
2637 | - MatchesTagText(content, "build-schedule")) |
2638 | + MatchesTagText(content, "build-schedule"), |
2639 | + ) |
2640 | |
2641 | def test_edit_recipe_invalid_branch(self): |
2642 | oci_project = self.factory.makeOCIProject() |
2643 | repository = self.factory.makeGitRepository() |
2644 | [old_git_ref] = self.factory.makeGitRefs( |
2645 | - paths=['refs/heads/v1.0-20.04'], repository=repository) |
2646 | + paths=["refs/heads/v1.0-20.04"], repository=repository |
2647 | + ) |
2648 | recipe = self.factory.makeOCIRecipe( |
2649 | - registrant=self.person, owner=self.person, |
2650 | - oci_project=oci_project, git_ref=old_git_ref) |
2651 | + registrant=self.person, |
2652 | + owner=self.person, |
2653 | + oci_project=oci_project, |
2654 | + git_ref=old_git_ref, |
2655 | + ) |
2656 | self.factory.makeTeam( |
2657 | - name="new-team", displayname="New Team", members=[self.person]) |
2658 | + name="new-team", displayname="New Team", members=[self.person] |
2659 | + ) |
2660 | [new_git_ref] = self.factory.makeGitRefs( |
2661 | - repository=repository, paths=['refs/heads/invalid']) |
2662 | + repository=repository, paths=["refs/heads/invalid"] |
2663 | + ) |
2664 | self.factory.makeOCIPushRule(recipe=recipe) |
2665 | |
2666 | browser = self.getViewBrowser(recipe, user=self.person) |
2667 | @@ -713,25 +829,36 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2668 | pillar = self.factory.makeProduct( |
2669 | owner=self.person, |
2670 | information_type=InformationType.PUBLIC, |
2671 | - branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY) |
2672 | + branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY, |
2673 | + ) |
2674 | oci_project = self.factory.makeOCIProject( |
2675 | - registrant=self.person, pillar=pillar) |
2676 | + registrant=self.person, pillar=pillar |
2677 | + ) |
2678 | [git_ref] = self.factory.makeGitRefs( |
2679 | - owner=self.person, |
2680 | - paths=['refs/heads/v2.0-20.04']) |
2681 | + owner=self.person, paths=["refs/heads/v2.0-20.04"] |
2682 | + ) |
2683 | recipe = self.factory.makeOCIRecipe( |
2684 | - registrant=self.person, owner=self.person, |
2685 | - oci_project=oci_project, git_ref=git_ref, |
2686 | - information_type=InformationType.PUBLIC) |
2687 | + registrant=self.person, |
2688 | + owner=self.person, |
2689 | + oci_project=oci_project, |
2690 | + git_ref=git_ref, |
2691 | + information_type=InformationType.PUBLIC, |
2692 | + ) |
2693 | |
2694 | - browser = self.getViewBrowser(recipe, '+edit', user=self.person) |
2695 | + browser = self.getViewBrowser(recipe, "+edit", user=self.person) |
2696 | |
2697 | # Make sure we are showing all available information types: |
2698 | info_type_field = browser.getControl(name="field.information_type") |
2699 | - self.assertContentEqual([ |
2700 | - 'PUBLIC', 'PUBLICSECURITY', 'PRIVATESECURITY', 'USERDATA', |
2701 | - 'PROPRIETARY'], |
2702 | - info_type_field.options) |
2703 | + self.assertContentEqual( |
2704 | + [ |
2705 | + "PUBLIC", |
2706 | + "PUBLICSECURITY", |
2707 | + "PRIVATESECURITY", |
2708 | + "USERDATA", |
2709 | + "PROPRIETARY", |
2710 | + ], |
2711 | + info_type_field.options, |
2712 | + ) |
2713 | |
2714 | info_type_field.value = InformationType.PROPRIETARY.name |
2715 | browser.getControl("Update OCI recipe").click() |
2716 | @@ -739,39 +866,51 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2717 | |
2718 | def test_edit_recipe_on_public_pillar_information_types(self): |
2719 | recipe = self.factory.makeOCIRecipe( |
2720 | - registrant=self.person, owner=self.person) |
2721 | - browser = self.getViewBrowser(recipe, '+edit', user=self.person) |
2722 | + registrant=self.person, owner=self.person |
2723 | + ) |
2724 | + browser = self.getViewBrowser(recipe, "+edit", user=self.person) |
2725 | |
2726 | info_type_field = browser.getControl(name="field.information_type") |
2727 | self.assertContentEqual( |
2728 | - ['PUBLIC', 'PUBLICSECURITY', 'PRIVATESECURITY', 'USERDATA'], |
2729 | - info_type_field.options) |
2730 | + ["PUBLIC", "PUBLICSECURITY", "PRIVATESECURITY", "USERDATA"], |
2731 | + info_type_field.options, |
2732 | + ) |
2733 | |
2734 | def test_edit_recipe_sets_date_last_modified(self): |
2735 | # Editing an OCI recipe sets the date_last_modified property. |
2736 | date_created = datetime(2000, 1, 1, tzinfo=pytz.UTC) |
2737 | recipe = self.factory.makeOCIRecipe( |
2738 | - registrant=self.person, date_created=date_created) |
2739 | + registrant=self.person, date_created=date_created |
2740 | + ) |
2741 | with person_logged_in(self.person): |
2742 | view = OCIRecipeEditView(recipe, LaunchpadTestRequest()) |
2743 | view.initialize() |
2744 | - view.request_action.success({ |
2745 | - "owner": recipe.owner, |
2746 | - "name": "changed", |
2747 | - "description": "changed", |
2748 | - }) |
2749 | + view.request_action.success( |
2750 | + { |
2751 | + "owner": recipe.owner, |
2752 | + "name": "changed", |
2753 | + "description": "changed", |
2754 | + } |
2755 | + ) |
2756 | self.assertSqlAttributeEqualsDate( |
2757 | - recipe, "date_last_modified", UTC_NOW) |
2758 | + recipe, "date_last_modified", UTC_NOW |
2759 | + ) |
2760 | |
2761 | def test_edit_recipe_already_exists(self): |
2762 | oci_project = self.factory.makeOCIProject() |
2763 | oci_project_display = oci_project.display_name |
2764 | recipe = self.factory.makeOCIRecipe( |
2765 | - registrant=self.person, owner=self.person, |
2766 | - oci_project=oci_project, name="one") |
2767 | + registrant=self.person, |
2768 | + owner=self.person, |
2769 | + oci_project=oci_project, |
2770 | + name="one", |
2771 | + ) |
2772 | self.factory.makeOCIRecipe( |
2773 | - registrant=self.person, owner=self.person, |
2774 | - oci_project=oci_project, name="two") |
2775 | + registrant=self.person, |
2776 | + owner=self.person, |
2777 | + oci_project=oci_project, |
2778 | + name="two", |
2779 | + ) |
2780 | browser = self.getViewBrowser(recipe, user=self.person) |
2781 | browser.getLink("Edit OCI recipe").click() |
2782 | browser.getControl(name="field.name").value = "two" |
2783 | @@ -779,29 +918,35 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2784 | self.assertEqual( |
2785 | "There is already an OCI recipe owned by Test Person in %s with " |
2786 | "this name." % oci_project_display, |
2787 | - extract_text(find_tags_by_class(browser.contents, "message")[1])) |
2788 | + extract_text(find_tags_by_class(browser.contents, "message")[1]), |
2789 | + ) |
2790 | |
2791 | def test_display_processors(self): |
2792 | self.setUpDistroSeries() |
2793 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2794 | recipe = self.factory.makeOCIRecipe( |
2795 | - registrant=self.person, owner=self.person, oci_project=oci_project) |
2796 | + registrant=self.person, owner=self.person, oci_project=oci_project |
2797 | + ) |
2798 | browser = self.getViewBrowser( |
2799 | - recipe, view_name="+edit", user=recipe.owner) |
2800 | + recipe, view_name="+edit", user=recipe.owner |
2801 | + ) |
2802 | processors = browser.getControl(name="field.processors") |
2803 | self.assertContentEqual( |
2804 | ["Intel 386 (386)", "AMD 64bit (amd64)", "HPPA Processor (hppa)"], |
2805 | - [extract_text(option) for option in processors.displayOptions]) |
2806 | + [extract_text(option) for option in processors.displayOptions], |
2807 | + ) |
2808 | self.assertContentEqual(["386", "amd64", "hppa"], processors.options) |
2809 | |
2810 | def test_edit_processors(self): |
2811 | self.setUpDistroSeries() |
2812 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2813 | recipe = self.factory.makeOCIRecipe( |
2814 | - registrant=self.person, owner=self.person, oci_project=oci_project) |
2815 | + registrant=self.person, owner=self.person, oci_project=oci_project |
2816 | + ) |
2817 | self.assertRecipeProcessors(recipe, ["386", "amd64", "hppa"]) |
2818 | browser = self.getViewBrowser( |
2819 | - recipe, view_name="+edit", user=recipe.owner) |
2820 | + recipe, view_name="+edit", user=recipe.owner |
2821 | + ) |
2822 | processors = browser.getControl(name="field.processors") |
2823 | self.assertContentEqual(["386", "amd64", "hppa"], processors.value) |
2824 | processors.value = ["386", "amd64"] |
2825 | @@ -813,10 +958,14 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2826 | self.setUpDistroSeries() |
2827 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2828 | recipe = self.factory.makeOCIRecipe( |
2829 | - registrant=self.person, owner=self.person, |
2830 | - oci_project=oci_project, build_args={"VAR1": "xxx", "VAR2": "uu"}) |
2831 | + registrant=self.person, |
2832 | + owner=self.person, |
2833 | + oci_project=oci_project, |
2834 | + build_args={"VAR1": "xxx", "VAR2": "uu"}, |
2835 | + ) |
2836 | browser = self.getViewBrowser( |
2837 | - recipe, view_name="+edit", user=recipe.owner) |
2838 | + recipe, view_name="+edit", user=recipe.owner |
2839 | + ) |
2840 | args = browser.getControl(name="field.build_args") |
2841 | self.assertContentEqual("VAR1=xxx\r\nVAR2=uu", args.value) |
2842 | args.value = "VAR=aa\nANOTHER_VAR=bbb" |
2843 | @@ -824,16 +973,21 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2844 | login_person(self.person) |
2845 | IStore(recipe).reload(recipe) |
2846 | self.assertEqual( |
2847 | - {"VAR": "aa", "ANOTHER_VAR": "bbb"}, recipe.build_args) |
2848 | + {"VAR": "aa", "ANOTHER_VAR": "bbb"}, recipe.build_args |
2849 | + ) |
2850 | |
2851 | def test_edit_build_args_invalid_content(self): |
2852 | self.setUpDistroSeries() |
2853 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2854 | recipe = self.factory.makeOCIRecipe( |
2855 | - registrant=self.person, owner=self.person, |
2856 | - oci_project=oci_project, build_args={"VAR1": "xxx", "VAR2": "uu"}) |
2857 | + registrant=self.person, |
2858 | + owner=self.person, |
2859 | + oci_project=oci_project, |
2860 | + build_args={"VAR1": "xxx", "VAR2": "uu"}, |
2861 | + ) |
2862 | browser = self.getViewBrowser( |
2863 | - recipe, view_name="+edit", user=recipe.owner) |
2864 | + recipe, view_name="+edit", user=recipe.owner |
2865 | + ) |
2866 | args = browser.getControl(name="field.build_args") |
2867 | self.assertContentEqual("VAR1=xxx\r\nVAR2=uu", args.value) |
2868 | args.value = "VAR=aa\nmessed up text" |
2869 | @@ -843,7 +997,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2870 | content = find_main_content(browser.contents) |
2871 | self.assertIn( |
2872 | "'messed up text' at line 2 is not a valid KEY=value pair.", |
2873 | - extract_text(content)) |
2874 | + extract_text(content), |
2875 | + ) |
2876 | |
2877 | # Assert that recipe still have the original build_args. |
2878 | login_person(self.person) |
2879 | @@ -859,11 +1014,14 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2880 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2881 | recipe = self.factory.makeOCIRecipe( |
2882 | name=original_name, |
2883 | - registrant=self.person, owner=self.person, |
2884 | - oci_project=oci_project) |
2885 | + registrant=self.person, |
2886 | + owner=self.person, |
2887 | + oci_project=oci_project, |
2888 | + ) |
2889 | oci_project.setOfficialRecipeStatus(recipe, True) |
2890 | browser = self.getViewBrowser( |
2891 | - recipe, view_name="+edit", user=recipe.owner) |
2892 | + recipe, view_name="+edit", user=recipe.owner |
2893 | + ) |
2894 | image_name = self.factory.getUniqueUnicode() |
2895 | field = browser.getControl(name="field.image_name") |
2896 | # Default is the recipe name |
2897 | @@ -873,7 +1031,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2898 | content = find_main_content(browser.contents) |
2899 | self.assertThat( |
2900 | "Registry image name:\n{}".format(image_name), |
2901 | - MatchesTagText(content, "image-name")) |
2902 | + MatchesTagText(content, "image-name"), |
2903 | + ) |
2904 | |
2905 | def test_edit_with_invisible_processor(self): |
2906 | # It's possible for existing recipes to have an enabled processor |
2907 | @@ -885,14 +1044,17 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2908 | proc_386 = getUtility(IProcessorSet).getByName("386") |
2909 | proc_amd64 = getUtility(IProcessorSet).getByName("amd64") |
2910 | proc_armel = self.factory.makeProcessor( |
2911 | - name="armel", restricted=True, build_by_default=False) |
2912 | + name="armel", restricted=True, build_by_default=False |
2913 | + ) |
2914 | self.setUpDistroSeries() |
2915 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2916 | recipe = self.factory.makeOCIRecipe( |
2917 | - registrant=self.person, owner=self.person, oci_project=oci_project) |
2918 | + registrant=self.person, owner=self.person, oci_project=oci_project |
2919 | + ) |
2920 | recipe.setProcessors([proc_386, proc_amd64, proc_armel]) |
2921 | browser = self.getViewBrowser( |
2922 | - recipe, view_name="+edit", user=recipe.owner) |
2923 | + recipe, view_name="+edit", user=recipe.owner |
2924 | + ) |
2925 | processors = browser.getControl(name="field.processors") |
2926 | self.assertContentEqual(["386", "amd64"], processors.value) |
2927 | processors.value = ["amd64"] |
2928 | @@ -905,20 +1067,26 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2929 | # checkbox in the UI, and the processor cannot be enabled. |
2930 | self.setUpDistroSeries() |
2931 | proc_armhf = self.factory.makeProcessor( |
2932 | - name="armhf", restricted=True, build_by_default=False) |
2933 | + name="armhf", restricted=True, build_by_default=False |
2934 | + ) |
2935 | self.factory.makeDistroArchSeries( |
2936 | - distroseries=self.distroseries, architecturetag="armhf", |
2937 | - processor=proc_armhf) |
2938 | + distroseries=self.distroseries, |
2939 | + architecturetag="armhf", |
2940 | + processor=proc_armhf, |
2941 | + ) |
2942 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2943 | recipe = self.factory.makeOCIRecipe( |
2944 | - registrant=self.person, owner=self.person, oci_project=oci_project) |
2945 | + registrant=self.person, owner=self.person, oci_project=oci_project |
2946 | + ) |
2947 | self.assertRecipeProcessors(recipe, ["386", "amd64", "hppa"]) |
2948 | browser = self.getViewBrowser( |
2949 | - recipe, view_name="+edit", user=recipe.owner) |
2950 | + recipe, view_name="+edit", user=recipe.owner |
2951 | + ) |
2952 | processors = browser.getControl(name="field.processors") |
2953 | self.assertContentEqual(["386", "amd64", "hppa"], processors.value) |
2954 | self.assertProcessorControls( |
2955 | - processors, ["386", "amd64", "hppa"], ["armhf"]) |
2956 | + processors, ["386", "amd64", "hppa"], ["armhf"] |
2957 | + ) |
2958 | # Even if the user works around the disabled checkbox and forcibly |
2959 | # enables it, they can't enable the restricted processor. |
2960 | for control in processors.controls: |
2961 | @@ -927,7 +1095,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2962 | processors.value = ["386", "amd64", "armhf"] |
2963 | self.assertRaises( |
2964 | CannotModifyOCIRecipeProcessor, |
2965 | - browser.getControl("Update OCI recipe").click) |
2966 | + browser.getControl("Update OCI recipe").click, |
2967 | + ) |
2968 | |
2969 | def test_edit_processors_restricted_already_enabled(self): |
2970 | # A restricted processor that is already enabled is shown with a |
2971 | @@ -938,23 +1107,29 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
2972 | proc_386 = getUtility(IProcessorSet).getByName("386") |
2973 | proc_amd64 = getUtility(IProcessorSet).getByName("amd64") |
2974 | proc_armhf = self.factory.makeProcessor( |
2975 | - name="armhf", restricted=True, build_by_default=False) |
2976 | + name="armhf", restricted=True, build_by_default=False |
2977 | + ) |
2978 | self.setUpDistroSeries() |
2979 | self.factory.makeDistroArchSeries( |
2980 | - distroseries=self.distroseries, architecturetag="armhf", |
2981 | - processor=proc_armhf) |
2982 | + distroseries=self.distroseries, |
2983 | + architecturetag="armhf", |
2984 | + processor=proc_armhf, |
2985 | + ) |
2986 | oci_project = self.factory.makeOCIProject(pillar=self.distribution) |
2987 | recipe = self.factory.makeOCIRecipe( |
2988 | - registrant=self.person, owner=self.person, oci_project=oci_project) |
2989 | + registrant=self.person, owner=self.person, oci_project=oci_project |
2990 | + ) |
2991 | recipe.setProcessors([proc_386, proc_amd64, proc_armhf]) |
2992 | self.assertRecipeProcessors(recipe, ["386", "amd64", "armhf"]) |
2993 | browser = self.getUserBrowser( |
2994 | - canonical_url(recipe) + "/+edit", user=recipe.owner) |
2995 | + canonical_url(recipe) + "/+edit", user=recipe.owner |
2996 | + ) |
2997 | processors = browser.getControl(name="field.processors") |
2998 | # armhf is checked but disabled. |
2999 | self.assertContentEqual(["386", "amd64", "armhf"], processors.value) |
3000 | self.assertProcessorControls( |
3001 | - processors, ["386", "amd64", "hppa"], ["armhf"]) |
3002 | + processors, ["386", "amd64", "hppa"], ["armhf"] |
3003 | + ) |
3004 | processors.value = ["386"] |
3005 | browser.getControl("Update OCI recipe").click() |
3006 | login_person(self.person) |
3007 | @@ -963,93 +1138,126 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
3008 | def test_edit_without_default_repo_for_ociproject(self): |
3009 | self.setUpDistroSeries() |
3010 | repo = self.factory.makeGitRepository( |
3011 | - owner=self.person, registrant=self.person) |
3012 | + owner=self.person, registrant=self.person |
3013 | + ) |
3014 | [git_ref] = self.factory.makeGitRefs( |
3015 | - repository=repo, paths=['refs/heads/v1.0-20.04']) |
3016 | + repository=repo, paths=["refs/heads/v1.0-20.04"] |
3017 | + ) |
3018 | oci_project = self.factory.makeOCIProject( |
3019 | - registrant=self.person, pillar=self.distribution) |
3020 | + registrant=self.person, pillar=self.distribution |
3021 | + ) |
3022 | recipe = self.factory.makeOCIRecipe( |
3023 | - registrant=self.person, oci_project=oci_project, git_ref=git_ref) |
3024 | + registrant=self.person, oci_project=oci_project, git_ref=git_ref |
3025 | + ) |
3026 | with person_logged_in(self.person): |
3027 | oci_project_url = canonical_url(oci_project) |
3028 | browser = self.getViewBrowser( |
3029 | - recipe, view_name="+edit", user=self.person) |
3030 | + recipe, view_name="+edit", user=self.person |
3031 | + ) |
3032 | error_message = ( |
3033 | "This recipe's git repository is not in the correct " |
3034 | 'namespace.<br/>Check the <a href="{url}">OCI project page</a> ' |
3035 | - "for instructions on how to create it correctly.") |
3036 | + "for instructions on how to create it correctly." |
3037 | + ) |
3038 | self.assertIn( |
3039 | - error_message.format(url=oci_project_url), browser.contents) |
3040 | + error_message.format(url=oci_project_url), browser.contents |
3041 | + ) |
3042 | |
3043 | def test_edit_repository_is_not_default_for_ociproject(self): |
3044 | self.setUpDistroSeries() |
3045 | oci_project = self.factory.makeOCIProject( |
3046 | - registrant=self.person, pillar=self.distribution) |
3047 | + registrant=self.person, pillar=self.distribution |
3048 | + ) |
3049 | [random_git_ref] = self.factory.makeGitRefs( |
3050 | - paths=['refs/heads/v1.0-20.04']) |
3051 | + paths=["refs/heads/v1.0-20.04"] |
3052 | + ) |
3053 | recipe = self.factory.makeOCIRecipe( |
3054 | - registrant=self.person, owner=self.person, oci_project=oci_project, |
3055 | - git_ref=random_git_ref) |
3056 | + registrant=self.person, |
3057 | + owner=self.person, |
3058 | + oci_project=oci_project, |
3059 | + git_ref=random_git_ref, |
3060 | + ) |
3061 | |
3062 | # Make the default git repository that should have been used by the |
3063 | # recipe. |
3064 | default_repo = self.factory.makeGitRepository( |
3065 | name=oci_project.name, |
3066 | - target=oci_project, owner=self.person, registrant=self.person) |
3067 | + target=oci_project, |
3068 | + owner=self.person, |
3069 | + registrant=self.person, |
3070 | + ) |
3071 | with person_logged_in(self.distribution.owner): |
3072 | getUtility(IGitRepositorySet).setDefaultRepository( |
3073 | - oci_project, default_repo) |
3074 | + oci_project, default_repo |
3075 | + ) |
3076 | |
3077 | with person_logged_in(self.person): |
3078 | - repo_link = GitRepositoryFormatterAPI(default_repo).link('') |
3079 | + repo_link = GitRepositoryFormatterAPI(default_repo).link("") |
3080 | browser = self.getViewBrowser( |
3081 | - recipe, view_name="+edit", user=self.person) |
3082 | + recipe, view_name="+edit", user=self.person |
3083 | + ) |
3084 | error_message = ( |
3085 | "This recipe's git repository is not in the correct " |
3086 | - "namespace.<br/>Consider using {repo} instead.") |
3087 | - self.assertIn( |
3088 | - error_message.format(repo=repo_link), browser.contents) |
3089 | + "namespace.<br/>Consider using {repo} instead." |
3090 | + ) |
3091 | + self.assertIn(error_message.format(repo=repo_link), browser.contents) |
3092 | |
3093 | def test_edit_repository_in_the_correct_namespace(self): |
3094 | self.setUpDistroSeries() |
3095 | oci_project = self.factory.makeOCIProject( |
3096 | - registrant=self.person, pillar=self.distribution) |
3097 | + registrant=self.person, pillar=self.distribution |
3098 | + ) |
3099 | default_repo = self.factory.makeGitRepository( |
3100 | name=oci_project.name, |
3101 | - target=oci_project, owner=self.person, registrant=self.person) |
3102 | + target=oci_project, |
3103 | + owner=self.person, |
3104 | + registrant=self.person, |
3105 | + ) |
3106 | |
3107 | [git_ref] = self.factory.makeGitRefs( |
3108 | - repository=default_repo, paths=['refs/heads/v1.0-20.04']) |
3109 | + repository=default_repo, paths=["refs/heads/v1.0-20.04"] |
3110 | + ) |
3111 | recipe = self.factory.makeOCIRecipe( |
3112 | - registrant=self.person, owner=self.person, oci_project=oci_project, |
3113 | - git_ref=git_ref) |
3114 | + registrant=self.person, |
3115 | + owner=self.person, |
3116 | + oci_project=oci_project, |
3117 | + git_ref=git_ref, |
3118 | + ) |
3119 | |
3120 | with person_logged_in(self.person): |
3121 | browser = self.getViewBrowser( |
3122 | - recipe, view_name="+edit", user=self.person) |
3123 | + recipe, view_name="+edit", user=self.person |
3124 | + ) |
3125 | self.assertNotIn( |
3126 | "This recipe's git repository is not in the correct namespace", |
3127 | - browser.contents) |
3128 | + browser.contents, |
3129 | + ) |
3130 | |
3131 | def test_edit_repository_dont_override_important_msgs(self): |
3132 | self.setUpDistroSeries() |
3133 | oci_project = self.factory.makeOCIProject( |
3134 | - registrant=self.person, pillar=self.distribution) |
3135 | + registrant=self.person, pillar=self.distribution |
3136 | + ) |
3137 | |
3138 | - [git_ref] = self.factory.makeGitRefs(paths=['refs/heads/v1.0-20.04']) |
3139 | + [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v1.0-20.04"]) |
3140 | recipe = self.factory.makeOCIRecipe( |
3141 | - registrant=self.person, owner=self.person, oci_project=oci_project, |
3142 | - git_ref=git_ref) |
3143 | + registrant=self.person, |
3144 | + owner=self.person, |
3145 | + oci_project=oci_project, |
3146 | + git_ref=git_ref, |
3147 | + ) |
3148 | |
3149 | wrong_namespace_msg = ( |
3150 | - "This recipe's git repository is not in the correct namespace") |
3151 | + "This recipe's git repository is not in the correct namespace" |
3152 | + ) |
3153 | wrong_ref_path_msg = ( |
3154 | - "The repository at %s does not contain a branch named " |
3155 | - "'non-existing git-ref'." |
3156 | - ) % git_ref.repository.display_name |
3157 | + "The repository at %s does not contain a branch named " |
3158 | + "'non-existing git-ref'." |
3159 | + ) % git_ref.repository.display_name |
3160 | with person_logged_in(self.person): |
3161 | browser = self.getViewBrowser( |
3162 | - recipe, view_name="+edit", user=self.person) |
3163 | + recipe, view_name="+edit", user=self.person |
3164 | + ) |
3165 | self.assertIn(wrong_namespace_msg, browser.contents) |
3166 | args = browser.getControl(name="field.git_ref.path") |
3167 | args.value = "non-existing git-ref" |
3168 | @@ -1062,8 +1270,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
3169 | def test_official_is_disabled(self): |
3170 | oci_project = self.factory.makeOCIProject() |
3171 | recipe = self.factory.makeOCIRecipe( |
3172 | - registrant=self.person, owner=self.person, |
3173 | - oci_project=oci_project) |
3174 | + registrant=self.person, owner=self.person, oci_project=oci_project |
3175 | + ) |
3176 | |
3177 | browser = self.getViewBrowser(recipe, user=self.person) |
3178 | browser.getLink("Edit OCI recipe").click() |
3179 | @@ -1072,12 +1280,13 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
3180 | |
3181 | def test_official_is_set_while_disabled(self): |
3182 | distribution = self.factory.makeDistribution( |
3183 | - oci_project_admin=self.person) |
3184 | + oci_project_admin=self.person |
3185 | + ) |
3186 | non_admin = self.factory.makePerson() |
3187 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
3188 | recipe = self.factory.makeOCIRecipe( |
3189 | - registrant=non_admin, owner=non_admin, |
3190 | - oci_project=oci_project) |
3191 | + registrant=non_admin, owner=non_admin, oci_project=oci_project |
3192 | + ) |
3193 | with person_logged_in(self.person): |
3194 | oci_project.setOfficialRecipeStatus(recipe, True) |
3195 | browser = self.getViewBrowser(recipe, user=non_admin) |
3196 | @@ -1088,11 +1297,12 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
3197 | |
3198 | def test_official_is_enabled(self): |
3199 | distribution = self.factory.makeDistribution( |
3200 | - oci_project_admin=self.person) |
3201 | + oci_project_admin=self.person |
3202 | + ) |
3203 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
3204 | recipe = self.factory.makeOCIRecipe( |
3205 | - registrant=self.person, owner=self.person, |
3206 | - oci_project=oci_project) |
3207 | + registrant=self.person, owner=self.person, oci_project=oci_project |
3208 | + ) |
3209 | |
3210 | browser = self.getViewBrowser(recipe, user=self.person) |
3211 | browser.getLink("Edit OCI recipe").click() |
3212 | @@ -1101,11 +1311,12 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
3213 | |
3214 | def test_set_official(self): |
3215 | distribution = self.factory.makeDistribution( |
3216 | - oci_project_admin=self.person) |
3217 | + oci_project_admin=self.person |
3218 | + ) |
3219 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
3220 | recipe = self.factory.makeOCIRecipe( |
3221 | - registrant=self.person, owner=self.person, |
3222 | - oci_project=oci_project) |
3223 | + registrant=self.person, owner=self.person, oci_project=oci_project |
3224 | + ) |
3225 | |
3226 | browser = self.getViewBrowser(recipe, user=self.person) |
3227 | browser.getLink("Edit OCI recipe").click() |
3228 | @@ -1115,17 +1326,18 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
3229 | |
3230 | content = find_main_content(browser.contents) |
3231 | self.assertThat( |
3232 | - "Official recipe:\nYes", |
3233 | - MatchesTagText(content, "official-recipe")) |
3234 | + "Official recipe:\nYes", MatchesTagText(content, "official-recipe") |
3235 | + ) |
3236 | |
3237 | def test_set_official_no_permissions(self): |
3238 | distro_owner = self.factory.makePerson() |
3239 | distribution = self.factory.makeDistribution( |
3240 | - oci_project_admin=distro_owner) |
3241 | + oci_project_admin=distro_owner |
3242 | + ) |
3243 | oci_project = self.factory.makeOCIProject(pillar=distribution) |
3244 | recipe = self.factory.makeOCIRecipe( |
3245 | - registrant=self.person, owner=self.person, |
3246 | - oci_project=oci_project) |
3247 | + registrant=self.person, owner=self.person, oci_project=oci_project |
3248 | + ) |
3249 | |
3250 | browser = self.getViewBrowser(recipe, user=self.person) |
3251 | browser.getLink("Edit OCI recipe").click() |
3252 | @@ -1135,33 +1347,39 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView): |
3253 | |
3254 | error_message = ( |
3255 | "You do not have permission to change the official status " |
3256 | - "of this recipe.") |
3257 | + "of this recipe." |
3258 | + ) |
3259 | self.assertIn(error_message, browser.contents) |
3260 | |
3261 | |
3262 | class TestOCIRecipeDeleteView(BaseTestOCIRecipeView): |
3263 | - |
3264 | def setUp(self): |
3265 | super().setUp() |
3266 | - self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'})) |
3267 | + self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"})) |
3268 | |
3269 | def test_unauthorized(self): |
3270 | # A user without edit access cannot delete an OCI recipe. |
3271 | recipe = self.factory.makeOCIRecipe( |
3272 | - registrant=self.person, owner=self.person) |
3273 | + registrant=self.person, owner=self.person |
3274 | + ) |
3275 | recipe_url = canonical_url(recipe) |
3276 | other_person = self.factory.makePerson() |
3277 | browser = self.getViewBrowser(recipe, user=other_person) |
3278 | self.assertRaises( |
3279 | - LinkNotFoundError, browser.getLink, "Delete OCI recipe") |
3280 | + LinkNotFoundError, browser.getLink, "Delete OCI recipe" |
3281 | + ) |
3282 | self.assertRaises( |
3283 | - Unauthorized, self.getUserBrowser, recipe_url + "/+delete", |
3284 | - user=other_person) |
3285 | + Unauthorized, |
3286 | + self.getUserBrowser, |
3287 | + recipe_url + "/+delete", |
3288 | + user=other_person, |
3289 | + ) |
3290 | |
3291 | def test_delete_recipe_without_builds(self): |
3292 | # An OCI recipe without builds can be deleted. |
3293 | recipe = self.factory.makeOCIRecipe( |
3294 | - registrant=self.person, owner=self.person) |
3295 | + registrant=self.person, owner=self.person |
3296 | + ) |
3297 | recipe_url = canonical_url(recipe) |
3298 | oci_project_url = canonical_url(recipe.oci_project) |
3299 | browser = self.getViewBrowser(recipe, user=self.person) |
3300 | @@ -1173,7 +1391,8 @@ class TestOCIRecipeDeleteView(BaseTestOCIRecipeView): |
3301 | def test_delete_recipe_with_builds(self): |
3302 | # An OCI recipe with builds can be deleted. |
3303 | recipe = self.factory.makeOCIRecipe( |
3304 | - registrant=self.person, owner=self.person) |
3305 | + registrant=self.person, owner=self.person |
3306 | + ) |
3307 | ocibuild = self.factory.makeOCIRecipeBuild(recipe=recipe) |
3308 | job = self.factory.makeOCIRecipeBuildJob(build=ocibuild) |
3309 | ocifile = self.factory.makeOCIFile(build=ocibuild) |
3310 | @@ -1191,41 +1410,48 @@ class TestOCIRecipeDeleteView(BaseTestOCIRecipeView): |
3311 | self.assertRaises(NotFound, browser.open, recipe_url) |
3312 | |
3313 | # Checks that only the related artifacts were deleted too. |
3314 | - def obj_exists(obj, search_key='id'): |
3315 | + def obj_exists(obj, search_key="id"): |
3316 | obj = removeSecurityProxy(obj) |
3317 | store = IStore(obj) |
3318 | cls = obj.__class__ |
3319 | cls_attribute = getattr(cls, search_key) |
3320 | identifier = getattr(obj, search_key) |
3321 | return not store.find(cls, cls_attribute == identifier).is_empty() |
3322 | + |
3323 | self.assertFalse(obj_exists(ocibuild)) |
3324 | self.assertFalse(obj_exists(ocifile)) |
3325 | - self.assertFalse(obj_exists(job, 'job_id')) |
3326 | + self.assertFalse(obj_exists(job, "job_id")) |
3327 | |
3328 | self.assertTrue(obj_exists(unrelated_build)) |
3329 | self.assertTrue(obj_exists(unrelated_file)) |
3330 | - self.assertTrue(obj_exists(unrelated_job, 'job_id')) |
3331 | + self.assertTrue(obj_exists(unrelated_job, "job_id")) |
3332 | |
3333 | |
3334 | class TestOCIRecipeView(BaseTestOCIRecipeView): |
3335 | - |
3336 | def setUp(self): |
3337 | super().setUp() |
3338 | self.distroseries = self.factory.makeDistroSeries() |
3339 | processor = getUtility(IProcessorSet).getByName("386") |
3340 | self.distroarchseries = self.factory.makeDistroArchSeries( |
3341 | - distroseries=self.distroseries, architecturetag="i386", |
3342 | - processor=processor) |
3343 | + distroseries=self.distroseries, |
3344 | + architecturetag="i386", |
3345 | + processor=processor, |
3346 | + ) |
3347 | self.factory.makeBuilder(virtualized=True) |
3348 | - self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'})) |
3349 | + self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"})) |
3350 | |
3351 | def makeOCIRecipe(self, oci_project=None, **kwargs): |
3352 | if oci_project is None: |
3353 | oci_project = self.factory.makeOCIProject( |
3354 | - pillar=self.distroseries.distribution) |
3355 | + pillar=self.distroseries.distribution |
3356 | + ) |
3357 | return self.factory.makeOCIRecipe( |
3358 | - registrant=self.person, owner=self.person, name="recipe-name", |
3359 | - oci_project=oci_project, **kwargs) |
3360 | + registrant=self.person, |
3361 | + owner=self.person, |
3362 | + name="recipe-name", |
3363 | + oci_project=oci_project, |
3364 | + **kwargs, |
3365 | + ) |
3366 | |
3367 | def makeBuild(self, recipe=None, date_created=None, **kwargs): |
3368 | if recipe is None: |
3369 | @@ -1233,13 +1459,17 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3370 | if date_created is None: |
3371 | date_created = datetime.now(pytz.UTC) - timedelta(hours=1) |
3372 | return self.factory.makeOCIRecipeBuild( |
3373 | - requester=self.person, recipe=recipe, |
3374 | + requester=self.person, |
3375 | + recipe=recipe, |
3376 | distro_arch_series=self.distroarchseries, |
3377 | - date_created=date_created, **kwargs) |
3378 | + date_created=date_created, |
3379 | + **kwargs, |
3380 | + ) |
3381 | |
3382 | def test_breadcrumb_and_top_header(self): |
3383 | oci_project = self.factory.makeOCIProject( |
3384 | - pillar=self.distroseries.distribution) |
3385 | + pillar=self.distroseries.distribution |
3386 | + ) |
3387 | oci_project_name = oci_project.name |
3388 | oci_project_url = canonical_url(oci_project) |
3389 | pillar_name = oci_project.pillar.name |
3390 | @@ -1251,7 +1481,8 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3391 | view.initialize() |
3392 | content = view() |
3393 | breadcrumbs = soupmatchers.Tag( |
3394 | - "breadcrumbs", "ol", attrs={"class": "breadcrumbs"}) |
3395 | + "breadcrumbs", "ol", attrs={"class": "breadcrumbs"} |
3396 | + ) |
3397 | |
3398 | # Should not have a breadcrumbs (OCI project link should be at the |
3399 | # top of the page, close to project/distribution name). |
3400 | @@ -1259,27 +1490,50 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3401 | |
3402 | # OCI project should appear at the top header, right after pillar link. |
3403 | header = soupmatchers.Tag( |
3404 | - "subtitle", "h2", attrs={"id": "watermark-heading"}) |
3405 | - self.assertThat(content, soupmatchers.HTMLContains(soupmatchers.Within( |
3406 | - header, soupmatchers.Tag( |
3407 | - "pillar link", "a", |
3408 | - text=pillar_name.title(), attrs={"href": pillar_url})))) |
3409 | - self.assertThat(content, soupmatchers.HTMLContains(soupmatchers.Within( |
3410 | - header, soupmatchers.Tag( |
3411 | - "OCI project link", "a", |
3412 | - text="%s OCI project" % oci_project_name, |
3413 | - attrs={"href": oci_project_url})))) |
3414 | + "subtitle", "h2", attrs={"id": "watermark-heading"} |
3415 | + ) |
3416 | + self.assertThat( |
3417 | + content, |
3418 | + soupmatchers.HTMLContains( |
3419 | + soupmatchers.Within( |
3420 | + header, |
3421 | + soupmatchers.Tag( |
3422 | + "pillar link", |
3423 | + "a", |
3424 | + text=pillar_name.title(), |
3425 | + attrs={"href": pillar_url}, |
3426 | + ), |
3427 | + ) |
3428 | + ), |
3429 | + ) |
3430 | + self.assertThat( |
3431 | + content, |
3432 | + soupmatchers.HTMLContains( |
3433 | + soupmatchers.Within( |
3434 | + header, |
3435 | + soupmatchers.Tag( |
3436 | + "OCI project link", |
3437 | + "a", |
3438 | + text="%s OCI project" % oci_project_name, |
3439 | + attrs={"href": oci_project_url}, |
3440 | + ), |
3441 | + ) |
3442 | + ), |
3443 | + ) |
3444 | |
3445 | def makeRecipe(self, processor_names, **kwargs): |
3446 | recipe = self.factory.makeOCIRecipe(**kwargs) |
3447 | processors_list = [] |
3448 | distroseries = self.factory.makeDistroSeries( |
3449 | - distribution=recipe.oci_project.distribution) |
3450 | + distribution=recipe.oci_project.distribution |
3451 | + ) |
3452 | for proc_name in processor_names: |
3453 | proc = getUtility(IProcessorSet).getByName(proc_name) |
3454 | distro = self.factory.makeDistroArchSeries( |
3455 | - distroseries=distroseries, architecturetag=proc_name, |
3456 | - processor=proc) |
3457 | + distroseries=distroseries, |
3458 | + architecturetag=proc_name, |
3459 | + processor=proc, |
3460 | + ) |
3461 | distro.addOrUpdateChroot(self.factory.makeLibraryFileAlias()) |
3462 | processors_list.append(proc) |
3463 | recipe.setProcessors(processors_list) |
3464 | @@ -1287,15 +1541,23 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3465 | |
3466 | def test_index(self): |
3467 | oci_project = self.factory.makeOCIProject( |
3468 | - pillar=self.distroseries.distribution) |
3469 | + pillar=self.distroseries.distribution |
3470 | + ) |
3471 | oci_project_display = oci_project.display_name |
3472 | [ref] = self.factory.makeGitRefs( |
3473 | - owner=self.person, target=self.person, name="recipe-repository", |
3474 | - paths=["refs/heads/v1.0-20.04"]) |
3475 | + owner=self.person, |
3476 | + target=self.person, |
3477 | + name="recipe-repository", |
3478 | + paths=["refs/heads/v1.0-20.04"], |
3479 | + ) |
3480 | recipe = self.makeRecipe( |
3481 | processor_names=["amd64", "386"], |
3482 | - build_file="Dockerfile", git_ref=ref, |
3483 | - oci_project=oci_project, registrant=self.person, owner=self.person) |
3484 | + build_file="Dockerfile", |
3485 | + git_ref=ref, |
3486 | + oci_project=oci_project, |
3487 | + registrant=self.person, |
3488 | + owner=self.person, |
3489 | + ) |
3490 | build_request = recipe.requestBuilds(self.person) |
3491 | builds = recipe.requestBuildsFromJob(self.person, build_request) |
3492 | job = removeSecurityProxy(build_request).job |
3493 | @@ -1303,20 +1565,27 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3494 | |
3495 | for build in builds: |
3496 | removeSecurityProxy(build).updateStatus( |
3497 | - BuildStatus.BUILDING, builder=None, |
3498 | - date_started=build.date_created) |
3499 | + BuildStatus.BUILDING, |
3500 | + builder=None, |
3501 | + date_started=build.date_created, |
3502 | + ) |
3503 | removeSecurityProxy(build).updateStatus( |
3504 | - BuildStatus.FULLYBUILT, builder=None, |
3505 | - date_finished=build.date_started + timedelta(minutes=30)) |
3506 | + BuildStatus.FULLYBUILT, |
3507 | + builder=None, |
3508 | + date_finished=build.date_started + timedelta(minutes=30), |
3509 | + ) |
3510 | |
3511 | # We also need to account for builds that don't have a build_request |
3512 | build = self.makeBuild( |
3513 | - recipe=recipe, status=BuildStatus.FULLYBUILT, |
3514 | - duration=timedelta(minutes=30)) |
3515 | + recipe=recipe, |
3516 | + status=BuildStatus.FULLYBUILT, |
3517 | + duration=timedelta(minutes=30), |
3518 | + ) |
3519 | |
3520 | browser = self.getViewBrowser(build_request.recipe) |
3521 | login_person(self.person) |
3522 | - self.assertTextMatchesExpressionIgnoreWhitespace("""\ |
3523 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
3524 | + """\ |
3525 | .* |
3526 | OCI recipe information |
3527 | Owner: Test Person |
3528 | @@ -1354,26 +1623,37 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3529 | 30 minutes ago |
3530 | Recipe push rules |
3531 | This OCI recipe has no push rules defined yet. |
3532 | - """ % (oci_project_display, recipe.build_path), |
3533 | - extract_text(find_main_content(browser.contents))) |
3534 | + """ |
3535 | + % (oci_project_display, recipe.build_path), |
3536 | + extract_text(find_main_content(browser.contents)), |
3537 | + ) |
3538 | |
3539 | # Check portlet on side menu. |
3540 | privacy_tag = find_tag_by_id(browser.contents, "privacy") |
3541 | self.assertTextMatchesExpressionIgnoreWhitespace( |
3542 | "This OCI recipe contains Public information", |
3543 | - extract_text(privacy_tag)) |
3544 | + extract_text(privacy_tag), |
3545 | + ) |
3546 | |
3547 | def test_index_cancelled_build(self): |
3548 | oci_project = self.factory.makeOCIProject( |
3549 | - pillar=self.distroseries.distribution) |
3550 | + pillar=self.distroseries.distribution |
3551 | + ) |
3552 | oci_project_display = oci_project.display_name |
3553 | [ref] = self.factory.makeGitRefs( |
3554 | - owner=self.person, target=self.person, name="recipe-repository", |
3555 | - paths=["refs/heads/v1.0-20.04"]) |
3556 | + owner=self.person, |
3557 | + target=self.person, |
3558 | + name="recipe-repository", |
3559 | + paths=["refs/heads/v1.0-20.04"], |
3560 | + ) |
3561 | recipe = self.makeRecipe( |
3562 | processor_names=["amd64", "386"], |
3563 | - build_file="Dockerfile", git_ref=ref, |
3564 | - oci_project=oci_project, registrant=self.person, owner=self.person) |
3565 | + build_file="Dockerfile", |
3566 | + git_ref=ref, |
3567 | + oci_project=oci_project, |
3568 | + registrant=self.person, |
3569 | + owner=self.person, |
3570 | + ) |
3571 | build_request = recipe.requestBuilds(self.person) |
3572 | builds = recipe.requestBuildsFromJob(self.person, build_request) |
3573 | job = removeSecurityProxy(build_request).job |
3574 | @@ -1381,20 +1661,27 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3575 | |
3576 | for build in builds: |
3577 | removeSecurityProxy(build).updateStatus( |
3578 | - BuildStatus.BUILDING, builder=None, |
3579 | - date_started=build.date_created) |
3580 | + BuildStatus.BUILDING, |
3581 | + builder=None, |
3582 | + date_started=build.date_created, |
3583 | + ) |
3584 | removeSecurityProxy(build).updateStatus( |
3585 | - BuildStatus.CANCELLED, builder=None, |
3586 | - date_finished=build.date_started + timedelta(minutes=30)) |
3587 | + BuildStatus.CANCELLED, |
3588 | + builder=None, |
3589 | + date_finished=build.date_started + timedelta(minutes=30), |
3590 | + ) |
3591 | |
3592 | # We also need to account for builds that don't have a build_request |
3593 | build = self.makeBuild( |
3594 | - recipe=recipe, status=BuildStatus.FULLYBUILT, |
3595 | - duration=timedelta(minutes=30)) |
3596 | + recipe=recipe, |
3597 | + status=BuildStatus.FULLYBUILT, |
3598 | + duration=timedelta(minutes=30), |
3599 | + ) |
3600 | |
3601 | browser = self.getViewBrowser(build_request.recipe) |
3602 | login_person(self.person) |
3603 | - self.assertTextMatchesExpressionIgnoreWhitespace("""\ |
3604 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
3605 | + """\ |
3606 | .* |
3607 | OCI recipe information |
3608 | Owner: Test Person |
3609 | @@ -1432,25 +1719,36 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3610 | 30 minutes ago |
3611 | Recipe push rules |
3612 | This OCI recipe has no push rules defined yet. |
3613 | - """ % (oci_project_display, recipe.build_path), |
3614 | - extract_text(find_main_content(browser.contents))) |
3615 | + """ |
3616 | + % (oci_project_display, recipe.build_path), |
3617 | + extract_text(find_main_content(browser.contents)), |
3618 | + ) |
3619 | |
3620 | # Check portlet on side menu. |
3621 | privacy_tag = find_tag_by_id(browser.contents, "privacy") |
3622 | self.assertTextMatchesExpressionIgnoreWhitespace( |
3623 | "This OCI recipe contains Public information", |
3624 | - extract_text(privacy_tag)) |
3625 | + extract_text(privacy_tag), |
3626 | + ) |
3627 | |
3628 | def test_index_cancelling_build(self): |
3629 | oci_project = self.factory.makeOCIProject( |
3630 | - pillar=self.distroseries.distribution) |
3631 | + pillar=self.distroseries.distribution |
3632 | + ) |
3633 | [ref] = self.factory.makeGitRefs( |
3634 | - owner=self.person, target=self.person, name="recipe-repository", |
3635 | - paths=["refs/heads/v1.0-20.04"]) |
3636 | + owner=self.person, |
3637 | + target=self.person, |
3638 | + name="recipe-repository", |
3639 | + paths=["refs/heads/v1.0-20.04"], |
3640 | + ) |
3641 | recipe = self.makeRecipe( |
3642 | processor_names=["amd64", "386"], |
3643 | - build_file="Dockerfile", git_ref=ref, |
3644 | - oci_project=oci_project, registrant=self.person, owner=self.person) |
3645 | + build_file="Dockerfile", |
3646 | + git_ref=ref, |
3647 | + oci_project=oci_project, |
3648 | + registrant=self.person, |
3649 | + owner=self.person, |
3650 | + ) |
3651 | build_request = recipe.requestBuilds(self.person) |
3652 | builds = recipe.requestBuildsFromJob(self.person, build_request) |
3653 | job = removeSecurityProxy(build_request).job |
3654 | @@ -1458,15 +1756,20 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3655 | |
3656 | for build in builds: |
3657 | removeSecurityProxy(build).updateStatus( |
3658 | - BuildStatus.BUILDING, builder=None, |
3659 | - date_started=build.date_created) |
3660 | + BuildStatus.BUILDING, |
3661 | + builder=None, |
3662 | + date_started=build.date_created, |
3663 | + ) |
3664 | removeSecurityProxy(build).updateStatus( |
3665 | - BuildStatus.CANCELLING, builder=None, |
3666 | - date_finished=build.date_started + timedelta(minutes=30)) |
3667 | + BuildStatus.CANCELLING, |
3668 | + builder=None, |
3669 | + date_finished=build.date_started + timedelta(minutes=30), |
3670 | + ) |
3671 | |
3672 | browser = self.getViewBrowser(build_request.recipe) |
3673 | login_person(self.person) |
3674 | - self.assertTextMatchesExpressionIgnoreWhitespace("""\ |
3675 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
3676 | + """\ |
3677 | .* |
3678 | There were build failures. |
3679 | No registry upload requested. |
3680 | @@ -1483,49 +1786,66 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3681 | \\(estimated\\) |
3682 | .* |
3683 | """, |
3684 | - extract_text(find_main_content(browser.contents))) |
3685 | + extract_text(find_main_content(browser.contents)), |
3686 | + ) |
3687 | |
3688 | # Check portlet on side menu. |
3689 | privacy_tag = find_tag_by_id(browser.contents, "privacy") |
3690 | self.assertTextMatchesExpressionIgnoreWhitespace( |
3691 | "This OCI recipe contains Public information", |
3692 | - extract_text(privacy_tag)) |
3693 | + extract_text(privacy_tag), |
3694 | + ) |
3695 | |
3696 | def test_index_for_private_recipe_shows_banner(self): |
3697 | recipe = self.factory.makeOCIRecipe( |
3698 | - registrant=self.person, owner=self.person, |
3699 | - information_type=InformationType.USERDATA) |
3700 | + registrant=self.person, |
3701 | + owner=self.person, |
3702 | + information_type=InformationType.USERDATA, |
3703 | + ) |
3704 | browser = self.getViewBrowser(recipe, user=self.person) |
3705 | |
3706 | # Check top banner. |
3707 | banners = find_tags_by_class( |
3708 | - browser.contents, "private_banner_container") |
3709 | + browser.contents, "private_banner_container" |
3710 | + ) |
3711 | self.assertEqual(1, len(banners)) |
3712 | self.assertTextMatchesExpressionIgnoreWhitespace( |
3713 | - 'The information on this page is private.', |
3714 | - extract_text(banners[0])) |
3715 | + "The information on this page is private.", |
3716 | + extract_text(banners[0]), |
3717 | + ) |
3718 | |
3719 | # Check portlet on side menu. |
3720 | privacy_tag = find_tag_by_id(browser.contents, "privacy") |
3721 | self.assertTextMatchesExpressionIgnoreWhitespace( |
3722 | "This OCI recipe contains Private information", |
3723 | - extract_text(privacy_tag)) |
3724 | + extract_text(privacy_tag), |
3725 | + ) |
3726 | |
3727 | def test_index_with_build_args(self): |
3728 | oci_project = self.factory.makeOCIProject( |
3729 | - pillar=self.distroseries.distribution) |
3730 | + pillar=self.distroseries.distribution |
3731 | + ) |
3732 | oci_project_display = oci_project.display_name |
3733 | [ref] = self.factory.makeGitRefs( |
3734 | - owner=self.person, target=self.person, name="recipe-repository", |
3735 | - paths=["refs/heads/v1.0-20.04"]) |
3736 | + owner=self.person, |
3737 | + target=self.person, |
3738 | + name="recipe-repository", |
3739 | + paths=["refs/heads/v1.0-20.04"], |
3740 | + ) |
3741 | recipe = self.makeOCIRecipe( |
3742 | - oci_project=oci_project, git_ref=ref, build_file="Dockerfile", |
3743 | - build_args={"VAR1": "123", "VAR2": "XXX"}) |
3744 | + oci_project=oci_project, |
3745 | + git_ref=ref, |
3746 | + build_file="Dockerfile", |
3747 | + build_args={"VAR1": "123", "VAR2": "XXX"}, |
3748 | + ) |
3749 | build_path = recipe.build_path |
3750 | build = self.makeBuild( |
3751 | - recipe=recipe, status=BuildStatus.FULLYBUILT, |
3752 | - duration=timedelta(minutes=30)) |
3753 | - self.assertTextMatchesExpressionIgnoreWhitespace("""\ |
3754 | + recipe=recipe, |
3755 | + status=BuildStatus.FULLYBUILT, |
3756 | + duration=timedelta(minutes=30), |
3757 | + ) |
3758 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
3759 | + """\ |
3760 | recipe-name |
3761 | .* |
3762 | OCI recipe information |
3763 | @@ -1551,25 +1871,36 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3764 | Successfully built |
3765 | 386 |
3766 | 30 minutes ago |
3767 | - """ % (oci_project_display, build_path), |
3768 | - self.getMainText(build.recipe)) |
3769 | + """ |
3770 | + % (oci_project_display, build_path), |
3771 | + self.getMainText(build.recipe), |
3772 | + ) |
3773 | |
3774 | def test_index_for_subscriber_without_git_repo_access(self): |
3775 | oci_project = self.factory.makeOCIProject( |
3776 | - pillar=self.distroseries.distribution) |
3777 | + pillar=self.distroseries.distribution |
3778 | + ) |
3779 | oci_project_display = oci_project.display_name |
3780 | [ref] = self.factory.makeGitRefs( |
3781 | - owner=self.person, target=self.person, name="recipe-repository", |
3782 | + owner=self.person, |
3783 | + target=self.person, |
3784 | + name="recipe-repository", |
3785 | paths=["refs/heads/v1.0-20.04"], |
3786 | - information_type=InformationType.PRIVATESECURITY) |
3787 | + information_type=InformationType.PRIVATESECURITY, |
3788 | + ) |
3789 | recipe = self.makeOCIRecipe( |
3790 | - oci_project=oci_project, git_ref=ref, build_file="Dockerfile", |
3791 | - information_type=InformationType.PRIVATESECURITY) |
3792 | + oci_project=oci_project, |
3793 | + git_ref=ref, |
3794 | + build_file="Dockerfile", |
3795 | + information_type=InformationType.PRIVATESECURITY, |
3796 | + ) |
3797 | with admin_logged_in(): |
3798 | build_path = recipe.build_path |
3799 | self.makeBuild( |
3800 | - recipe=recipe, status=BuildStatus.FULLYBUILT, |
3801 | - duration=timedelta(minutes=30)) |
3802 | + recipe=recipe, |
3803 | + status=BuildStatus.FULLYBUILT, |
3804 | + duration=timedelta(minutes=30), |
3805 | + ) |
3806 | |
3807 | # Subscribe a user. |
3808 | subscriber = self.factory.makePerson() |
3809 | @@ -1578,7 +1909,8 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3810 | |
3811 | with person_logged_in(subscriber): |
3812 | main_text = self.getMainText(recipe, user=subscriber) |
3813 | - self.assertTextMatchesExpressionIgnoreWhitespace("""\ |
3814 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
3815 | + """\ |
3816 | recipe-name |
3817 | .* |
3818 | OCI recipe information |
3819 | @@ -1603,15 +1935,19 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3820 | Successfully built |
3821 | 386 |
3822 | 30 minutes ago |
3823 | - """ % (oci_project_display, build_path), |
3824 | - main_text) |
3825 | + """ |
3826 | + % (oci_project_display, build_path), |
3827 | + main_text, |
3828 | + ) |
3829 | |
3830 | def test_index_success_with_buildlog(self): |
3831 | # The build log is shown if it is there. |
3832 | build = self.makeBuild( |
3833 | - status=BuildStatus.FULLYBUILT, duration=timedelta(minutes=30)) |
3834 | + status=BuildStatus.FULLYBUILT, duration=timedelta(minutes=30) |
3835 | + ) |
3836 | build.setLog(self.factory.makeLibraryFileAlias()) |
3837 | - self.assertTextMatchesExpressionIgnoreWhitespace(r"""\ |
3838 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
3839 | + r"""\ |
3840 | Latest builds |
3841 | Build status |
3842 | Upload status |
3843 | @@ -1627,31 +1963,42 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3844 | Successfully built |
3845 | 386 |
3846 | 30 minutes ago |
3847 | - """, self.getMainText(build.recipe)) |
3848 | + """, |
3849 | + self.getMainText(build.recipe), |
3850 | + ) |
3851 | |
3852 | def test_index_no_builds(self): |
3853 | # A message is shown when there are no builds. |
3854 | recipe = self.factory.makeOCIRecipe() |
3855 | self.assertIn( |
3856 | - "This OCI recipe has not been built yet.", |
3857 | - self.getMainText(recipe)) |
3858 | + "This OCI recipe has not been built yet.", self.getMainText(recipe) |
3859 | + ) |
3860 | |
3861 | def test_index_pending_build(self): |
3862 | # A pending build is listed as such. |
3863 | oci_project = self.factory.makeOCIProject( |
3864 | - pillar=self.distroseries.distribution) |
3865 | + pillar=self.distroseries.distribution |
3866 | + ) |
3867 | [ref] = self.factory.makeGitRefs( |
3868 | - owner=self.person, target=self.person, name="recipe-repository", |
3869 | - paths=["refs/heads/v1.0-20.04"]) |
3870 | + owner=self.person, |
3871 | + target=self.person, |
3872 | + name="recipe-repository", |
3873 | + paths=["refs/heads/v1.0-20.04"], |
3874 | + ) |
3875 | recipe = self.makeRecipe( |
3876 | processor_names=["amd64", "386"], |
3877 | - build_file="Dockerfile", git_ref=ref, |
3878 | - oci_project=oci_project, registrant=self.person, owner=self.person) |
3879 | + build_file="Dockerfile", |
3880 | + git_ref=ref, |
3881 | + oci_project=oci_project, |
3882 | + registrant=self.person, |
3883 | + owner=self.person, |
3884 | + ) |
3885 | build_request = recipe.requestBuilds(self.person) |
3886 | builds = recipe.requestBuildsFromJob(self.person, build_request) |
3887 | job = removeSecurityProxy(build_request).job |
3888 | removeSecurityProxy(job).builds = builds |
3889 | - self.assertTextMatchesExpressionIgnoreWhitespace(r"""\ |
3890 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
3891 | + r"""\ |
3892 | Latest builds |
3893 | Build status |
3894 | Upload status |
3895 | @@ -1661,16 +2008,20 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3896 | Waiting for builds to start. |
3897 | a moment ago |
3898 | in .* \(estimated\) |
3899 | - """, self.getMainText(recipe)) |
3900 | + """, |
3901 | + self.getMainText(recipe), |
3902 | + ) |
3903 | |
3904 | def test_index_request_builds_link(self): |
3905 | # Recipe owners get a link to allow requesting builds. |
3906 | owner = self.factory.makePerson() |
3907 | distroseries = self.factory.makeDistroSeries() |
3908 | oci_project = self.factory.makeOCIProject( |
3909 | - pillar=distroseries.distribution) |
3910 | + pillar=distroseries.distribution |
3911 | + ) |
3912 | recipe = self.factory.makeOCIRecipe( |
3913 | - registrant=owner, owner=owner, oci_project=oci_project) |
3914 | + registrant=owner, owner=owner, oci_project=oci_project |
3915 | + ) |
3916 | recipe_name = recipe.name |
3917 | browser = self.getViewBrowser(recipe, user=owner) |
3918 | browser.getLink("Request builds").click() |
3919 | @@ -1681,30 +2032,38 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3920 | # requesting builds. |
3921 | distroseries = self.factory.makeDistroSeries() |
3922 | oci_project = self.factory.makeOCIProject( |
3923 | - pillar=distroseries.distribution) |
3924 | + pillar=distroseries.distribution |
3925 | + ) |
3926 | recipe = self.factory.makeOCIRecipe(oci_project=oci_project) |
3927 | recipe_url = canonical_url(recipe) |
3928 | browser = self.getViewBrowser(recipe, user=self.person) |
3929 | self.assertRaises(LinkNotFoundError, browser.getLink, "Request builds") |
3930 | self.assertRaises( |
3931 | - Unauthorized, self.getUserBrowser, recipe_url + "/+request-builds", |
3932 | - user=self.person) |
3933 | + Unauthorized, |
3934 | + self.getUserBrowser, |
3935 | + recipe_url + "/+request-builds", |
3936 | + user=self.person, |
3937 | + ) |
3938 | |
3939 | def setStatus(self, build, status): |
3940 | build.updateStatus( |
3941 | - BuildStatus.BUILDING, date_started=build.date_created) |
3942 | + BuildStatus.BUILDING, date_started=build.date_created |
3943 | + ) |
3944 | build.updateStatus( |
3945 | - status, date_finished=build.date_started + timedelta(minutes=30)) |
3946 | + status, date_finished=build.date_started + timedelta(minutes=30) |
3947 | + ) |
3948 | |
3949 | def test_builds(self): |
3950 | # OCIRecipeView.builds produces reasonable results. |
3951 | recipe = self.makeOCIRecipe() |
3952 | # Create oldest builds first so that they sort properly by id. |
3953 | date_gen = time_counter( |
3954 | - datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1)) |
3955 | + datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1) |
3956 | + ) |
3957 | builds = [ |
3958 | self.makeBuild(recipe=recipe, date_created=next(date_gen)) |
3959 | - for i in range(11)] |
3960 | + for i in range(11) |
3961 | + ] |
3962 | view = OCIRecipeView(recipe, None) |
3963 | self.assertEqual(list(reversed(builds)), view.builds) |
3964 | self.setStatus(builds[10], BuildStatus.FULLYBUILT) |
3965 | @@ -1713,7 +2072,8 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3966 | # When there are >= 9 pending builds, only the most recent of any |
3967 | # completed builds is returned. |
3968 | self.assertEqual( |
3969 | - list(reversed(builds[:9])) + [builds[10]], view.builds) |
3970 | + list(reversed(builds[:9])) + [builds[10]], view.builds |
3971 | + ) |
3972 | for build in builds[:9]: |
3973 | self.setStatus(build, BuildStatus.FULLYBUILT) |
3974 | del get_property_cache(view).builds |
3975 | @@ -1721,34 +2081,46 @@ class TestOCIRecipeView(BaseTestOCIRecipeView): |
3976 | |
3977 | |
3978 | class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView): |
3979 | - |
3980 | def setUp(self): |
3981 | super().setUp() |
3982 | self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
3983 | self.distroseries = self.factory.makeDistroSeries( |
3984 | - distribution=self.ubuntu, name="shiny", displayname="Shiny") |
3985 | + distribution=self.ubuntu, name="shiny", displayname="Shiny" |
3986 | + ) |
3987 | + distribution = self.distroseries.distribution |
3988 | self.architectures = [] |
3989 | for processor, architecture in ("386", "i386"), ("amd64", "amd64"): |
3990 | das = self.factory.makeDistroArchSeries( |
3991 | - distroseries=self.distroseries, architecturetag=architecture, |
3992 | - processor=getUtility(IProcessorSet).getByName(processor)) |
3993 | + distroseries=self.distroseries, |
3994 | + architecturetag=architecture, |
3995 | + processor=getUtility(IProcessorSet).getByName(processor), |
3996 | + ) |
3997 | das.addOrUpdateChroot(self.factory.makeLibraryFileAlias()) |
3998 | self.architectures.append(das) |
3999 | - self.useFixture(FeatureFixture({ |
4000 | - OCI_RECIPE_ALLOW_CREATE: "on", |
4001 | - "oci.build_series.%s" % self.distroseries.distribution.name: |
4002 | - self.distroseries.name, |
4003 | - })) |
4004 | + self.useFixture( |
4005 | + FeatureFixture( |
4006 | + { |
4007 | + OCI_RECIPE_ALLOW_CREATE: "on", |
4008 | + "oci.build_series.%s" |
4009 | + % distribution.name: self.distroseries.name, |
4010 | + } |
4011 | + ) |
4012 | + ) |
4013 | oci_project = self.factory.makeOCIProject( |
4014 | - pillar=self.distroseries.distribution, |
4015 | - ociprojectname="oci-project-name") |
4016 | + pillar=distribution, |
4017 | + ociprojectname="oci-project-name", |
4018 | + ) |
4019 | self.recipe = self.factory.makeOCIRecipe( |
4020 | - name="recipe-name", registrant=self.person, owner=self.person, |
4021 | - oci_project=oci_project) |
4022 | + name="recipe-name", |
4023 | + registrant=self.person, |
4024 | + owner=self.person, |
4025 | + oci_project=oci_project, |
4026 | + ) |
4027 | |
4028 | def test_request_builds_page(self): |
4029 | # The +request-builds page is sane. |
4030 | - self.assertTextMatchesExpressionIgnoreWhitespace(""" |
4031 | + self.assertTextMatchesExpressionIgnoreWhitespace( |
4032 | + """ |
4033 | Request builds for recipe-name |
4034 | recipe-name |
4035 | Request builds |
4036 | @@ -1758,12 +2130,14 @@ class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView): |
4037 | or |
4038 | Cancel |
4039 | """, |
4040 | - self.getMainText(self.recipe, "+request-builds", user=self.person)) |
4041 | + self.getMainText(self.recipe, "+request-builds", user=self.person), |
4042 | + ) |
4043 | |
4044 | def test_request_builds_not_owner(self): |
4045 | # A user without launchpad.Edit cannot request builds. |
4046 | self.assertRaises( |
4047 | - Unauthorized, self.getViewBrowser, self.recipe, "+request-builds") |
4048 | + Unauthorized, self.getViewBrowser, self.recipe, "+request-builds" |
4049 | + ) |
4050 | |
4051 | def runRequestBuildJobs(self): |
4052 | with admin_logged_in(): |
4053 | @@ -1774,7 +2148,8 @@ class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView): |
4054 | def test_request_builds_action(self): |
4055 | # Requesting a build creates pending builds. |
4056 | browser = self.getViewBrowser( |
4057 | - self.recipe, "+request-builds", user=self.person) |
4058 | + self.recipe, "+request-builds", user=self.person |
4059 | + ) |
4060 | self.assertTrue(browser.getControl("amd64").selected) |
4061 | self.assertTrue(browser.getControl("i386").selected) |
4062 | browser.getControl("Request builds").click() |
4063 | @@ -1785,146 +2160,206 @@ class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView): |
4064 | builds = self.recipe.pending_builds |
4065 | self.assertContentEqual( |
4066 | ["amd64", "i386"], |
4067 | - [build.distro_arch_series.architecturetag for build in builds]) |
4068 | + [build.distro_arch_series.architecturetag for build in builds], |
4069 | + ) |
4070 | self.assertContentEqual( |
4071 | - [2510], {build.buildqueue_record.lastscore for build in builds}) |
4072 | + [2510], {build.buildqueue_record.lastscore for build in builds} |
4073 | + ) |
4074 | |
4075 | def test_request_builds_no_architectures(self): |
4076 | # Selecting no architectures causes a validation failure. |
4077 | browser = self.getViewBrowser( |
4078 | - self.recipe, "+request-builds", user=self.person) |
4079 | + self.recipe, "+request-builds", user=self.person |
4080 | + ) |
4081 | browser.getControl("amd64").selected = False |
4082 | browser.getControl("i386").selected = False |
4083 | browser.getControl("Request builds").click() |
4084 | self.assertIn( |
4085 | "You need to select at least one architecture.", |
4086 | - extract_text(find_main_content(browser.contents))) |
4087 | + extract_text(find_main_content(browser.contents)), |
4088 | + ) |
4089 | |
4090 | |
4091 | -class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4092 | - BaseTestOCIRecipeView): |
4093 | +class TestOCIRecipeEditPushRulesView( |
4094 | + OCIConfigHelperMixin, BaseTestOCIRecipeView |
4095 | +): |
4096 | def setUp(self): |
4097 | super().setUp() |
4098 | self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
4099 | self.distroseries = self.factory.makeDistroSeries( |
4100 | - distribution=self.ubuntu, name="shiny", displayname="Shiny") |
4101 | - |
4102 | - self.useFixture(FeatureFixture({ |
4103 | - OCI_RECIPE_ALLOW_CREATE: "on", |
4104 | - "oci.build_series.%s" % self.distroseries.distribution.name: |
4105 | - self.distroseries.name, |
4106 | - })) |
4107 | + distribution=self.ubuntu, name="shiny", displayname="Shiny" |
4108 | + ) |
4109 | + distribution = self.distroseries.distribution |
4110 | + |
4111 | + self.useFixture( |
4112 | + FeatureFixture( |
4113 | + { |
4114 | + OCI_RECIPE_ALLOW_CREATE: "on", |
4115 | + "oci.build_series.%s" |
4116 | + % distribution.name: self.distroseries.name, |
4117 | + } |
4118 | + ) |
4119 | + ) |
4120 | self.oci_project = self.factory.makeOCIProject( |
4121 | - pillar=self.distroseries.distribution, |
4122 | - ociprojectname="oci-project-name") |
4123 | + pillar=distribution, |
4124 | + ociprojectname="oci-project-name", |
4125 | + ) |
4126 | |
4127 | self.member = self.factory.makePerson() |
4128 | self.team = self.factory.makeTeam(members=[self.person, self.member]) |
4129 | |
4130 | self.recipe = self.factory.makeOCIRecipe( |
4131 | - name="recipe-name", registrant=self.person, owner=self.person, |
4132 | - oci_project=self.oci_project) |
4133 | + name="recipe-name", |
4134 | + registrant=self.person, |
4135 | + owner=self.person, |
4136 | + oci_project=self.oci_project, |
4137 | + ) |
4138 | |
4139 | self.team_owned_recipe = self.factory.makeOCIRecipe( |
4140 | - name="recipe-name", registrant=self.person, owner=self.team, |
4141 | - oci_project=self.oci_project) |
4142 | + name="recipe-name", |
4143 | + registrant=self.person, |
4144 | + owner=self.team, |
4145 | + oci_project=self.oci_project, |
4146 | + ) |
4147 | |
4148 | self.setConfig() |
4149 | |
4150 | def test_view_oci_push_rules_owner(self): |
4151 | url = self.factory.getUniqueURL() |
4152 | - credentials = {'username': 'foo', 'password': 'bar'} |
4153 | + credentials = {"username": "foo", "password": "bar"} |
4154 | registry_credentials = getUtility(IOCIRegistryCredentialsSet).new( |
4155 | - registrant=self.person, owner=self.person, url=url, |
4156 | - credentials=credentials) |
4157 | + registrant=self.person, |
4158 | + owner=self.person, |
4159 | + url=url, |
4160 | + credentials=credentials, |
4161 | + ) |
4162 | image_name = self.factory.getUniqueUnicode() |
4163 | push_rule = getUtility(IOCIPushRuleSet).new( |
4164 | recipe=self.recipe, |
4165 | registry_credentials=registry_credentials, |
4166 | - image_name=image_name) |
4167 | + image_name=image_name, |
4168 | + ) |
4169 | view = create_initialized_view( |
4170 | - self.recipe, "+index", principal=self.person) |
4171 | + self.recipe, "+index", principal=self.person |
4172 | + ) |
4173 | |
4174 | # Display the Registry URL and the Username |
4175 | # for the credentials owner |
4176 | with person_logged_in(self.person): |
4177 | rendered_view = view.render() |
4178 | - row = soupmatchers.Tag("push rule row", "tr", |
4179 | - attrs={"id": "rule-%d" % push_rule.id}) |
4180 | - self.assertThat(rendered_view, soupmatchers.HTMLContains( |
4181 | - soupmatchers.Within( |
4182 | - row, |
4183 | - soupmatchers.Tag("Registry URL", "td", |
4184 | - text=registry_credentials.url)), |
4185 | - soupmatchers.Within( |
4186 | - row, |
4187 | - soupmatchers.Tag("Username", "td", |
4188 | - text=registry_credentials.username)), |
4189 | - soupmatchers.Within( |
4190 | - row, |
4191 | - soupmatchers.Tag( |
4192 | - "Image name", "td", text=image_name)))) |
4193 | + row = soupmatchers.Tag( |
4194 | + "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id} |
4195 | + ) |
4196 | + self.assertThat( |
4197 | + rendered_view, |
4198 | + soupmatchers.HTMLContains( |
4199 | + soupmatchers.Within( |
4200 | + row, |
4201 | + soupmatchers.Tag( |
4202 | + "Registry URL", "td", text=registry_credentials.url |
4203 | + ), |
4204 | + ), |
4205 | + soupmatchers.Within( |
4206 | + row, |
4207 | + soupmatchers.Tag( |
4208 | + "Username", |
4209 | + "td", |
4210 | + text=registry_credentials.username, |
4211 | + ), |
4212 | + ), |
4213 | + soupmatchers.Within( |
4214 | + row, |
4215 | + soupmatchers.Tag("Image name", "td", text=image_name), |
4216 | + ), |
4217 | + ), |
4218 | + ) |
4219 | |
4220 | def test_view_oci_push_rules_non_owner(self): |
4221 | url = self.factory.getUniqueURL() |
4222 | - credentials = {'username': 'foo', 'password': 'bar'} |
4223 | + credentials = {"username": "foo", "password": "bar"} |
4224 | registry_credentials = getUtility(IOCIRegistryCredentialsSet).new( |
4225 | - registrant=self.person, owner=self.person, url=url, |
4226 | - credentials=credentials) |
4227 | + registrant=self.person, |
4228 | + owner=self.person, |
4229 | + url=url, |
4230 | + credentials=credentials, |
4231 | + ) |
4232 | image_name = self.factory.getUniqueUnicode() |
4233 | push_rule = getUtility(IOCIPushRuleSet).new( |
4234 | recipe=self.recipe, |
4235 | registry_credentials=registry_credentials, |
4236 | - image_name=image_name) |
4237 | + image_name=image_name, |
4238 | + ) |
4239 | non_owner = self.factory.makePerson() |
4240 | admin = self.factory.makePerson( |
4241 | - member_of=[getUtility(IPersonSet).getByName('admins')]) |
4242 | + member_of=[getUtility(IPersonSet).getByName("admins")] |
4243 | + ) |
4244 | view = create_initialized_view( |
4245 | - self.recipe, "+index", principal=non_owner) |
4246 | + self.recipe, "+index", principal=non_owner |
4247 | + ) |
4248 | |
4249 | # Display only the image name for users |
4250 | # who are not the registry credentials owner |
4251 | with person_logged_in(non_owner): |
4252 | rendered_view = view.render() |
4253 | - row = soupmatchers.Tag("push rule row", "tr", |
4254 | - attrs={"id": "rule-%d" % push_rule.id}) |
4255 | - self.assertThat(rendered_view, soupmatchers.HTMLContains( |
4256 | - soupmatchers.Within( |
4257 | - row, |
4258 | - soupmatchers.Tag("Registry URL", "td", |
4259 | - text=soupmatchers._not_passed)), |
4260 | - soupmatchers.Within( |
4261 | - row, |
4262 | - soupmatchers.Tag("Username", "td", |
4263 | - text=soupmatchers._not_passed)), |
4264 | - soupmatchers.Within( |
4265 | - row, |
4266 | - soupmatchers.Tag( |
4267 | - "Image name", "td", text=image_name)))) |
4268 | + row = soupmatchers.Tag( |
4269 | + "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id} |
4270 | + ) |
4271 | + self.assertThat( |
4272 | + rendered_view, |
4273 | + soupmatchers.HTMLContains( |
4274 | + soupmatchers.Within( |
4275 | + row, |
4276 | + soupmatchers.Tag( |
4277 | + "Registry URL", "td", text=soupmatchers._not_passed |
4278 | + ), |
4279 | + ), |
4280 | + soupmatchers.Within( |
4281 | + row, |
4282 | + soupmatchers.Tag( |
4283 | + "Username", "td", text=soupmatchers._not_passed |
4284 | + ), |
4285 | + ), |
4286 | + soupmatchers.Within( |
4287 | + row, |
4288 | + soupmatchers.Tag("Image name", "td", text=image_name), |
4289 | + ), |
4290 | + ), |
4291 | + ) |
4292 | |
4293 | # Anonymous users can't see registry credentials |
4294 | # even though they can see the push rule |
4295 | with anonymous_logged_in(): |
4296 | rendered_view = view.render() |
4297 | - row = soupmatchers.Tag("push rule row", "tr", |
4298 | - attrs={"id": "rule-%d" % push_rule.id}) |
4299 | - self.assertThat(rendered_view, soupmatchers.HTMLContains( |
4300 | - soupmatchers.Within( |
4301 | - row, |
4302 | - soupmatchers.Tag("Registry URL", "td", |
4303 | - text=soupmatchers._not_passed)), |
4304 | - soupmatchers.Within( |
4305 | - row, |
4306 | - soupmatchers.Tag("Region", "td", |
4307 | - text=soupmatchers._not_passed)), |
4308 | - soupmatchers.Within( |
4309 | - row, |
4310 | - soupmatchers.Tag("Username", "td", |
4311 | - text=soupmatchers._not_passed)), |
4312 | - soupmatchers.Within( |
4313 | - row, |
4314 | - soupmatchers.Tag( |
4315 | - "Image name", "td", text=image_name)))) |
4316 | + row = soupmatchers.Tag( |
4317 | + "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id} |
4318 | + ) |
4319 | + self.assertThat( |
4320 | + rendered_view, |
4321 | + soupmatchers.HTMLContains( |
4322 | + soupmatchers.Within( |
4323 | + row, |
4324 | + soupmatchers.Tag( |
4325 | + "Registry URL", "td", text=soupmatchers._not_passed |
4326 | + ), |
4327 | + ), |
4328 | + soupmatchers.Within( |
4329 | + row, |
4330 | + soupmatchers.Tag( |
4331 | + "Region", "td", text=soupmatchers._not_passed |
4332 | + ), |
4333 | + ), |
4334 | + soupmatchers.Within( |
4335 | + row, |
4336 | + soupmatchers.Tag( |
4337 | + "Username", "td", text=soupmatchers._not_passed |
4338 | + ), |
4339 | + ), |
4340 | + soupmatchers.Within( |
4341 | + row, |
4342 | + soupmatchers.Tag("Image name", "td", text=image_name), |
4343 | + ), |
4344 | + ), |
4345 | + ) |
4346 | |
4347 | # Although not the owner of the registry credentials |
4348 | # the admin user has launchpad.View permission on |
4349 | @@ -1933,56 +2368,76 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4350 | # see ViewOCIRegistryCredentials |
4351 | with person_logged_in(admin): |
4352 | rendered_view = view.render() |
4353 | - row = soupmatchers.Tag("push rule row", "tr", |
4354 | - attrs={"id": "rule-%d" % push_rule.id}) |
4355 | - self.assertThat(rendered_view, soupmatchers.HTMLContains( |
4356 | - soupmatchers.Within( |
4357 | - row, |
4358 | - soupmatchers.Tag("Registry URL", "td", |
4359 | - text=registry_credentials.url)), |
4360 | - soupmatchers.Within( |
4361 | - row, |
4362 | - soupmatchers.Tag("Username", "td", |
4363 | - text=registry_credentials.username)), |
4364 | - soupmatchers.Within( |
4365 | - row, |
4366 | - soupmatchers.Tag( |
4367 | - "Image name", "td", text=image_name)))) |
4368 | + row = soupmatchers.Tag( |
4369 | + "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id} |
4370 | + ) |
4371 | + self.assertThat( |
4372 | + rendered_view, |
4373 | + soupmatchers.HTMLContains( |
4374 | + soupmatchers.Within( |
4375 | + row, |
4376 | + soupmatchers.Tag( |
4377 | + "Registry URL", "td", text=registry_credentials.url |
4378 | + ), |
4379 | + ), |
4380 | + soupmatchers.Within( |
4381 | + row, |
4382 | + soupmatchers.Tag( |
4383 | + "Username", |
4384 | + "td", |
4385 | + text=registry_credentials.username, |
4386 | + ), |
4387 | + ), |
4388 | + soupmatchers.Within( |
4389 | + row, |
4390 | + soupmatchers.Tag("Image name", "td", text=image_name), |
4391 | + ), |
4392 | + ), |
4393 | + ) |
4394 | |
4395 | def test_edit_oci_push_rules(self): |
4396 | url = self.factory.getUniqueURL() |
4397 | - credentials = {'username': 'foo', 'password': 'bar'} |
4398 | + credentials = {"username": "foo", "password": "bar"} |
4399 | registry_credentials = getUtility(IOCIRegistryCredentialsSet).new( |
4400 | - registrant=self.person, owner=self.person, url=url, |
4401 | - credentials=credentials) |
4402 | + registrant=self.person, |
4403 | + owner=self.person, |
4404 | + url=url, |
4405 | + credentials=credentials, |
4406 | + ) |
4407 | image_name = self.factory.getUniqueUnicode() |
4408 | push_rule = getUtility(IOCIPushRuleSet).new( |
4409 | recipe=self.recipe, |
4410 | registry_credentials=registry_credentials, |
4411 | - image_name=image_name) |
4412 | + image_name=image_name, |
4413 | + ) |
4414 | browser = self.getViewBrowser(self.recipe, user=self.person) |
4415 | browser.getLink("Edit push rules").click() |
4416 | # assert image name is displayed correctly |
4417 | with person_logged_in(self.person): |
4418 | - self.assertEqual(image_name, browser.getControl( |
4419 | - name="field.image_name.%d" % push_rule.id).value) |
4420 | + self.assertEqual( |
4421 | + image_name, |
4422 | + browser.getControl( |
4423 | + name="field.image_name.%d" % push_rule.id |
4424 | + ).value, |
4425 | + ) |
4426 | |
4427 | # assert image name is required |
4428 | with person_logged_in(self.person): |
4429 | browser.getControl( |
4430 | - name="field.image_name.%d" % push_rule.id).value = "" |
4431 | + name="field.image_name.%d" % push_rule.id |
4432 | + ).value = "" |
4433 | browser.getControl("Save").click() |
4434 | self.assertIn("Required input is missing", browser.contents) |
4435 | |
4436 | # set image name to valid string |
4437 | with person_logged_in(self.person): |
4438 | browser.getControl( |
4439 | - name="field.image_name.%d" % push_rule.id).value = "image1" |
4440 | + name="field.image_name.%d" % push_rule.id |
4441 | + ).value = "image1" |
4442 | browser.getControl("Save").click() |
4443 | # and assert model changed |
4444 | with person_logged_in(self.person): |
4445 | - self.assertEqual( |
4446 | - push_rule.image_name, "image1") |
4447 | + self.assertEqual(push_rule.image_name, "image1") |
4448 | # Create a second push rule and test we call setNewImageName only |
4449 | # in cases where image name is different than the one on the model |
4450 | # otherwise we get the exception on rows the user doesn't actually |
4451 | @@ -1992,16 +2447,17 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4452 | second_rule = getUtility(IOCIPushRuleSet).new( |
4453 | recipe=self.recipe, |
4454 | registry_credentials=registry_credentials, |
4455 | - image_name="second image") |
4456 | + image_name="second image", |
4457 | + ) |
4458 | browser = self.getViewBrowser(self.recipe, user=self.person) |
4459 | browser.getLink("Edit push rules").click() |
4460 | with person_logged_in(self.person): |
4461 | browser.getControl( |
4462 | - name="field.image_name.%d" % push_rule.id).value = "image2" |
4463 | + name="field.image_name.%d" % push_rule.id |
4464 | + ).value = "image2" |
4465 | browser.getControl("Save").click() |
4466 | with person_logged_in(self.person): |
4467 | - self.assertEqual( |
4468 | - push_rule.image_name, "image2") |
4469 | + self.assertEqual(push_rule.image_name, "image2") |
4470 | |
4471 | # Attempt to set the same name on the second rule |
4472 | # will result in expected exception |
4473 | @@ -2009,48 +2465,67 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4474 | browser.getLink("Edit push rules").click() |
4475 | with person_logged_in(self.person): |
4476 | browser.getControl( |
4477 | - name="field.image_name.%d" % second_rule.id).value = "image2" |
4478 | - self.assertRaises(OCIPushRuleAlreadyExists, |
4479 | - browser.getControl("Save").click) |
4480 | + name="field.image_name.%d" % second_rule.id |
4481 | + ).value = "image2" |
4482 | + self.assertRaises( |
4483 | + OCIPushRuleAlreadyExists, browser.getControl("Save").click |
4484 | + ) |
4485 | |
4486 | def test_edit_oci_push_rules_non_owner_of_credentials(self): |
4487 | url = self.factory.getUniqueURL() |
4488 | - credentials = {'username': 'foo', 'password': 'bar'} |
4489 | + credentials = {"username": "foo", "password": "bar"} |
4490 | registry_credentials = getUtility(IOCIRegistryCredentialsSet).new( |
4491 | - registrant=self.person, owner=self.person, url=url, |
4492 | - credentials=credentials) |
4493 | + registrant=self.person, |
4494 | + owner=self.person, |
4495 | + url=url, |
4496 | + credentials=credentials, |
4497 | + ) |
4498 | image_names = [self.factory.getUniqueUnicode() for _ in range(2)] |
4499 | push_rules = [ |
4500 | getUtility(IOCIPushRuleSet).new( |
4501 | recipe=self.team_owned_recipe, |
4502 | registry_credentials=registry_credentials, |
4503 | - image_name=image_name) |
4504 | - for image_name in image_names] |
4505 | + image_name=image_name, |
4506 | + ) |
4507 | + for image_name in image_names |
4508 | + ] |
4509 | Store.of(push_rules[-1]).flush() |
4510 | push_rule_ids = [push_rule.id for push_rule in push_rules] |
4511 | browser = self.getViewBrowser(self.team_owned_recipe, user=self.member) |
4512 | browser.getLink("Edit push rules").click() |
4513 | row = soupmatchers.Tag( |
4514 | - "push rule row", "tr", attrs={"class": "push-rule"}) |
4515 | - self.assertThat(browser.contents, soupmatchers.HTMLContains( |
4516 | - soupmatchers.Within( |
4517 | - row, |
4518 | - soupmatchers.Tag( |
4519 | - "username widget", "span", |
4520 | - attrs={ |
4521 | - "id": "field.username.%d" % push_rule_ids[0], |
4522 | - "class": "sprite private", |
4523 | - })), |
4524 | - soupmatchers.Within( |
4525 | - row, |
4526 | - soupmatchers.Tag( |
4527 | - "url widget", "span", |
4528 | - attrs={ |
4529 | - "id": "field.url.%d" % push_rule_ids[0], |
4530 | - "class": "sprite private", |
4531 | - })))) |
4532 | + "push rule row", "tr", attrs={"class": "push-rule"} |
4533 | + ) |
4534 | + self.assertThat( |
4535 | + browser.contents, |
4536 | + soupmatchers.HTMLContains( |
4537 | + soupmatchers.Within( |
4538 | + row, |
4539 | + soupmatchers.Tag( |
4540 | + "username widget", |
4541 | + "span", |
4542 | + attrs={ |
4543 | + "id": "field.username.%d" % push_rule_ids[0], |
4544 | + "class": "sprite private", |
4545 | + }, |
4546 | + ), |
4547 | + ), |
4548 | + soupmatchers.Within( |
4549 | + row, |
4550 | + soupmatchers.Tag( |
4551 | + "url widget", |
4552 | + "span", |
4553 | + attrs={ |
4554 | + "id": "field.url.%d" % push_rule_ids[0], |
4555 | + "class": "sprite private", |
4556 | + }, |
4557 | + ), |
4558 | + ), |
4559 | + ), |
4560 | + ) |
4561 | browser.getControl( |
4562 | - name="field.image_name.%d" % push_rule_ids[0]).value = "image1" |
4563 | + name="field.image_name.%d" % push_rule_ids[0] |
4564 | + ).value = "image1" |
4565 | browser.getControl("Save").click() |
4566 | with person_logged_in(self.member): |
4567 | self.assertEqual("image1", push_rules[0].image_name) |
4568 | @@ -2058,25 +2533,31 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4569 | |
4570 | def test_delete_oci_push_rules(self): |
4571 | url = self.factory.getUniqueURL() |
4572 | - credentials = {'username': 'foo', 'password': 'bar'} |
4573 | + credentials = {"username": "foo", "password": "bar"} |
4574 | registry_credentials = getUtility(IOCIRegistryCredentialsSet).new( |
4575 | - registrant=self.person, owner=self.person, url=url, |
4576 | - credentials=credentials) |
4577 | + registrant=self.person, |
4578 | + owner=self.person, |
4579 | + url=url, |
4580 | + credentials=credentials, |
4581 | + ) |
4582 | image_name = self.factory.getUniqueUnicode() |
4583 | push_rule = getUtility(IOCIPushRuleSet).new( |
4584 | recipe=self.recipe, |
4585 | registry_credentials=registry_credentials, |
4586 | - image_name=image_name) |
4587 | + image_name=image_name, |
4588 | + ) |
4589 | browser = self.getViewBrowser(self.recipe, user=self.person) |
4590 | browser.getLink("Edit push rules").click() |
4591 | with person_logged_in(self.person): |
4592 | browser.getControl( |
4593 | - name="field.delete.%d" % push_rule.id).value = True |
4594 | + name="field.delete.%d" % push_rule.id |
4595 | + ).value = True |
4596 | browser.getControl("Save").click() |
4597 | |
4598 | with person_logged_in(self.person): |
4599 | self.assertIsNone( |
4600 | - getUtility(IOCIPushRuleSet).getByID(push_rule.id)) |
4601 | + getUtility(IOCIPushRuleSet).getByID(push_rule.id) |
4602 | + ) |
4603 | |
4604 | def test_add_oci_push_rules_validations(self): |
4605 | # Add new rule works when there are no rules in the DB. |
4606 | @@ -2113,20 +2594,28 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4607 | browser.getControl(name="field.add_url").value = url |
4608 | browser.getControl("Save").click() |
4609 | with person_logged_in(self.person): |
4610 | - rules = list(removeSecurityProxy( |
4611 | - getUtility(IOCIPushRuleSet).findByRecipe(self.recipe))) |
4612 | + rules = list( |
4613 | + removeSecurityProxy( |
4614 | + getUtility(IOCIPushRuleSet).findByRecipe(self.recipe) |
4615 | + ) |
4616 | + ) |
4617 | self.assertEqual(len(rules), 1) |
4618 | rule = rules[0] |
4619 | - self.assertThat(rule, MatchesStructure( |
4620 | - image_name=Equals("imagename1"), |
4621 | - registry_url=Equals(url), |
4622 | - registry_credentials=MatchesStructure( |
4623 | - url=Equals(url), |
4624 | - username=Is(None)))) |
4625 | + self.assertThat( |
4626 | + rule, |
4627 | + MatchesStructure( |
4628 | + image_name=Equals("imagename1"), |
4629 | + registry_url=Equals(url), |
4630 | + registry_credentials=MatchesStructure( |
4631 | + url=Equals(url), username=Is(None) |
4632 | + ), |
4633 | + ), |
4634 | + ) |
4635 | |
4636 | with person_logged_in(self.person): |
4637 | self.assertEqual( |
4638 | - {"password": None}, rule.registry_credentials.getCredentials()) |
4639 | + {"password": None}, rule.registry_credentials.getCredentials() |
4640 | + ) |
4641 | |
4642 | def test_add_oci_push_rules_new_username_password(self): |
4643 | # Supplying an image name, registry URL, username, and password |
4644 | @@ -2142,24 +2631,36 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4645 | browser.getControl(name="field.add_username").value = "username" |
4646 | browser.getControl(name="field.add_password").value = "password" |
4647 | browser.getControl( |
4648 | - name="field.add_confirm_password").value = "password" |
4649 | + name="field.add_confirm_password" |
4650 | + ).value = "password" |
4651 | browser.getControl("Save").click() |
4652 | with person_logged_in(self.person): |
4653 | - rules = list(removeSecurityProxy( |
4654 | - getUtility(IOCIPushRuleSet).findByRecipe(self.recipe))) |
4655 | + rules = list( |
4656 | + removeSecurityProxy( |
4657 | + getUtility(IOCIPushRuleSet).findByRecipe(self.recipe) |
4658 | + ) |
4659 | + ) |
4660 | self.assertEqual(len(rules), 1) |
4661 | rule = rules[0] |
4662 | - self.assertThat(rule, MatchesStructure( |
4663 | - image_name=Equals("imagename3"), |
4664 | - registry_url=Equals(url), |
4665 | - registry_credentials=MatchesStructure.byEquality( |
4666 | - url=url, |
4667 | - username="username"))) |
4668 | + self.assertThat( |
4669 | + rule, |
4670 | + MatchesStructure( |
4671 | + image_name=Equals("imagename3"), |
4672 | + registry_url=Equals(url), |
4673 | + registry_credentials=MatchesStructure.byEquality( |
4674 | + url=url, username="username" |
4675 | + ), |
4676 | + ), |
4677 | + ) |
4678 | with person_logged_in(self.person): |
4679 | - self.assertEqual({ |
4680 | - "username": "username", "password": "password", |
4681 | - "region": "somewhere-02"}, |
4682 | - rule.registry_credentials.getCredentials()) |
4683 | + self.assertEqual( |
4684 | + { |
4685 | + "username": "username", |
4686 | + "password": "password", |
4687 | + "region": "somewhere-02", |
4688 | + }, |
4689 | + rule.registry_credentials.getCredentials(), |
4690 | + ) |
4691 | |
4692 | def test_add_oci_push_rules_existing_credentials_duplicate(self): |
4693 | # Adding a new push rule using existing credentials fails if a rule |
4694 | @@ -2167,21 +2668,30 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4695 | existing_rule = self.factory.makeOCIPushRule( |
4696 | recipe=self.recipe, |
4697 | registry_credentials=self.factory.makeOCIRegistryCredentials( |
4698 | - registrant=self.recipe.owner, owner=self.recipe.owner)) |
4699 | + registrant=self.recipe.owner, owner=self.recipe.owner |
4700 | + ), |
4701 | + ) |
4702 | existing_image_name = existing_rule.image_name |
4703 | existing_registry_url = existing_rule.registry_url |
4704 | existing_username = existing_rule.username |
4705 | browser = self.getViewBrowser(self.recipe, user=self.person) |
4706 | browser.getLink("Edit push rules").click() |
4707 | browser.getControl(name="field.add_credentials").value = "existing" |
4708 | - browser.getControl(name="field.add_image_name").value = ( |
4709 | - existing_image_name) |
4710 | - browser.getControl(name="field.existing_credentials").value = ( |
4711 | - "%s %s" % (quote(existing_registry_url), quote(existing_username))) |
4712 | + browser.getControl( |
4713 | + name="field.add_image_name" |
4714 | + ).value = existing_image_name |
4715 | + browser.getControl( |
4716 | + name="field.existing_credentials" |
4717 | + ).value = "%s %s" % ( |
4718 | + quote(existing_registry_url), |
4719 | + quote(existing_username), |
4720 | + ) |
4721 | browser.getControl("Save").click() |
4722 | self.assertIn( |
4723 | "A push rule already exists with the same URL, " |
4724 | - "image name, and credentials.", browser.contents) |
4725 | + "image name, and credentials.", |
4726 | + browser.contents, |
4727 | + ) |
4728 | |
4729 | def test_add_oci_push_rules_existing_credentials(self): |
4730 | # Previously added registry credentials can be chosen from the radio |
4731 | @@ -2192,27 +2702,38 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4732 | existing_rule = self.factory.makeOCIPushRule( |
4733 | recipe=self.recipe, |
4734 | registry_credentials=self.factory.makeOCIRegistryCredentials( |
4735 | - registrant=self.recipe.owner, owner=self.recipe.owner, |
4736 | - credentials={})) |
4737 | + registrant=self.recipe.owner, |
4738 | + owner=self.recipe.owner, |
4739 | + credentials={}, |
4740 | + ), |
4741 | + ) |
4742 | existing_registry_url = existing_rule.registry_url |
4743 | browser = self.getViewBrowser(self.recipe, user=self.person) |
4744 | browser.getLink("Edit push rules").click() |
4745 | browser.getControl(name="field.add_credentials").value = "existing" |
4746 | browser.getControl(name="field.add_image_name").value = "imagename2" |
4747 | - browser.getControl(name="field.existing_credentials").value = ( |
4748 | - quote(existing_registry_url)) |
4749 | + browser.getControl(name="field.existing_credentials").value = quote( |
4750 | + existing_registry_url |
4751 | + ) |
4752 | browser.getControl("Save").click() |
4753 | with person_logged_in(self.person): |
4754 | - rules = list(removeSecurityProxy( |
4755 | - getUtility(IOCIPushRuleSet).findByRecipe(self.recipe))) |
4756 | + rules = list( |
4757 | + removeSecurityProxy( |
4758 | + getUtility(IOCIPushRuleSet).findByRecipe(self.recipe) |
4759 | + ) |
4760 | + ) |
4761 | self.assertEqual(len(rules), 2) |
4762 | rule = rules[1] |
4763 | - self.assertThat(rule, MatchesStructure( |
4764 | - image_name=Equals("imagename2"), |
4765 | - registry_url=Equals(existing_registry_url), |
4766 | - registry_credentials=MatchesStructure( |
4767 | - url=Equals(existing_registry_url), |
4768 | - username=Is(None)))) |
4769 | + self.assertThat( |
4770 | + rule, |
4771 | + MatchesStructure( |
4772 | + image_name=Equals("imagename2"), |
4773 | + registry_url=Equals(existing_registry_url), |
4774 | + registry_credentials=MatchesStructure( |
4775 | + url=Equals(existing_registry_url), username=Is(None) |
4776 | + ), |
4777 | + ), |
4778 | + ) |
4779 | with person_logged_in(self.person): |
4780 | self.assertEqual({}, rule.registry_credentials.getCredentials()) |
4781 | |
4782 | @@ -2220,40 +2741,44 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4783 | url = self.factory.getUniqueURL() |
4784 | browser = self.getViewBrowser(self.team_owned_recipe, user=self.member) |
4785 | browser.getLink("Edit push rules").click() |
4786 | - browser.getControl( |
4787 | - name="field.add_image_name").value = "imagename1" |
4788 | - browser.getControl( |
4789 | - name="field.add_url").value = url |
4790 | + browser.getControl(name="field.add_image_name").value = "imagename1" |
4791 | + browser.getControl(name="field.add_url").value = url |
4792 | browser.getControl(name="field.add_credentials").value = "new" |
4793 | browser.getControl("Save").click() |
4794 | |
4795 | with person_logged_in(self.member): |
4796 | - rules = list(removeSecurityProxy( |
4797 | - getUtility(IOCIPushRuleSet).findByRecipe( |
4798 | - self.team_owned_recipe))) |
4799 | + rules = list( |
4800 | + removeSecurityProxy( |
4801 | + getUtility(IOCIPushRuleSet).findByRecipe( |
4802 | + self.team_owned_recipe |
4803 | + ) |
4804 | + ) |
4805 | + ) |
4806 | self.assertEqual(len(rules), 1) |
4807 | rule = rules[0] |
4808 | - self.assertThat(rule, MatchesStructure( |
4809 | - image_name=Equals('imagename1'), |
4810 | - registry_url=Equals(url), |
4811 | - registry_credentials=MatchesStructure( |
4812 | - url=Equals(url), |
4813 | - username=Is(None)))) |
4814 | + self.assertThat( |
4815 | + rule, |
4816 | + MatchesStructure( |
4817 | + image_name=Equals("imagename1"), |
4818 | + registry_url=Equals(url), |
4819 | + registry_credentials=MatchesStructure( |
4820 | + url=Equals(url), username=Is(None) |
4821 | + ), |
4822 | + ), |
4823 | + ) |
4824 | |
4825 | with person_logged_in(self.member): |
4826 | self.assertThat( |
4827 | rule.registry_credentials.getCredentials(), |
4828 | - MatchesDict( |
4829 | - {"password": Equals(None)})) |
4830 | + MatchesDict({"password": Equals(None)}), |
4831 | + ) |
4832 | |
4833 | def test_edit_oci_push_rules_team_owned(self): |
4834 | url = self.factory.getUniqueURL() |
4835 | browser = self.getViewBrowser(self.team_owned_recipe, user=self.member) |
4836 | browser.getLink("Edit push rules").click() |
4837 | - browser.getControl( |
4838 | - name="field.add_image_name").value = "imagename1" |
4839 | - browser.getControl( |
4840 | - name="field.add_url").value = url |
4841 | + browser.getControl(name="field.add_image_name").value = "imagename1" |
4842 | + browser.getControl(name="field.add_url").value = url |
4843 | browser.getControl(name="field.add_credentials").value = "new" |
4844 | browser.getControl("Save").click() |
4845 | |
4846 | @@ -2262,43 +2787,55 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4847 | browser = self.getViewBrowser(self.team_owned_recipe, user=self.person) |
4848 | browser.getLink("Edit push rules").click() |
4849 | with person_logged_in(self.person): |
4850 | - rules = list(removeSecurityProxy( |
4851 | - getUtility(IOCIPushRuleSet).findByRecipe( |
4852 | - self.team_owned_recipe))) |
4853 | + rules = list( |
4854 | + removeSecurityProxy( |
4855 | + getUtility(IOCIPushRuleSet).findByRecipe( |
4856 | + self.team_owned_recipe |
4857 | + ) |
4858 | + ) |
4859 | + ) |
4860 | self.assertEqual(len(rules), 1) |
4861 | rule = rules[0] |
4862 | - self.assertEqual("imagename1", browser.getControl( |
4863 | - name="field.image_name.%d" % rule.id).value) |
4864 | + self.assertEqual( |
4865 | + "imagename1", |
4866 | + browser.getControl(name="field.image_name.%d" % rule.id).value, |
4867 | + ) |
4868 | |
4869 | # set image name to valid string |
4870 | with person_logged_in(self.person): |
4871 | browser.getControl( |
4872 | - name="field.image_name.%d" % rule.id).value = "image1" |
4873 | + name="field.image_name.%d" % rule.id |
4874 | + ).value = "image1" |
4875 | browser.getControl("Save").click() |
4876 | |
4877 | # and assert model changed |
4878 | with person_logged_in(self.member): |
4879 | - self.assertEqual( |
4880 | - rule.image_name, "image1") |
4881 | + self.assertEqual(rule.image_name, "image1") |
4882 | |
4883 | # self.member will see the new image name |
4884 | browser = self.getViewBrowser(self.team_owned_recipe, user=self.member) |
4885 | browser.getLink("Edit push rules").click() |
4886 | with person_logged_in(self.member): |
4887 | - self.assertEqual("image1", browser.getControl( |
4888 | - name="field.image_name.%d" % rule.id).value) |
4889 | + self.assertEqual( |
4890 | + "image1", |
4891 | + browser.getControl(name="field.image_name.%d" % rule.id).value, |
4892 | + ) |
4893 | |
4894 | def test_edit_oci_registry_creds(self): |
4895 | url = self.factory.getUniqueURL() |
4896 | - credentials = {'username': 'foo', 'password': 'bar'} |
4897 | + credentials = {"username": "foo", "password": "bar"} |
4898 | image_name = self.factory.getUniqueUnicode() |
4899 | registry_credentials = getUtility(IOCIRegistryCredentialsSet).new( |
4900 | - registrant=self.person, owner=self.person, url=url, |
4901 | - credentials=credentials) |
4902 | + registrant=self.person, |
4903 | + owner=self.person, |
4904 | + url=url, |
4905 | + credentials=credentials, |
4906 | + ) |
4907 | getUtility(IOCIPushRuleSet).new( |
4908 | recipe=self.recipe, |
4909 | registry_credentials=registry_credentials, |
4910 | - image_name=image_name) |
4911 | + image_name=image_name, |
4912 | + ) |
4913 | browser = self.getViewBrowser(self.recipe, user=self.person) |
4914 | browser.getLink("Edit push rules").click() |
4915 | browser.getLink("Edit OCI registry credentials").click() |
4916 | @@ -2307,22 +2844,27 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin, |
4917 | browser.getControl(name="field.add_region").value = "new_region1" |
4918 | browser.getControl(name="field.add_username").value = "new_username" |
4919 | browser.getControl(name="field.add_password").value = "password" |
4920 | - browser.getControl(name="field.add_confirm_password" |
4921 | - ).value = "password" |
4922 | + browser.getControl( |
4923 | + name="field.add_confirm_password" |
4924 | + ).value = "password" |
4925 | |
4926 | browser.getControl("Save").click() |
4927 | with person_logged_in(self.person): |
4928 | - creds = list(getUtility( |
4929 | - IOCIRegistryCredentialsSet).findByOwner( |
4930 | - self.person)) |
4931 | + creds = list( |
4932 | + getUtility(IOCIRegistryCredentialsSet).findByOwner(self.person) |
4933 | + ) |
4934 | |
4935 | self.assertEqual(url, creds[1].url) |
4936 | self.assertThat( |
4937 | (creds[1]).getCredentials(), |
4938 | - MatchesDict({ |
4939 | - "username": Equals("new_username"), |
4940 | - "password": Equals("password"), |
4941 | - "region": Equals("new_region1")})) |
4942 | + MatchesDict( |
4943 | + { |
4944 | + "username": Equals("new_username"), |
4945 | + "password": Equals("password"), |
4946 | + "region": Equals("new_region1"), |
4947 | + } |
4948 | + ), |
4949 | + ) |
4950 | |
4951 | |
4952 | class TestOCIRecipeListingView(BaseTestOCIRecipeView): |
4953 | @@ -2330,32 +2872,42 @@ class TestOCIRecipeListingView(BaseTestOCIRecipeView): |
4954 | super().setUp() |
4955 | self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
4956 | self.distroseries = self.factory.makeDistroSeries( |
4957 | - distribution=self.ubuntu, name="shiny", displayname="Shiny") |
4958 | + distribution=self.ubuntu, name="shiny", displayname="Shiny" |
4959 | + ) |
4960 | self.architectures = [] |
4961 | for processor, architecture in ("386", "i386"), ("amd64", "amd64"): |
4962 | das = self.factory.makeDistroArchSeries( |
4963 | - distroseries=self.distroseries, architecturetag=architecture, |
4964 | - processor=getUtility(IProcessorSet).getByName(processor)) |
4965 | + distroseries=self.distroseries, |
4966 | + architecturetag=architecture, |
4967 | + processor=getUtility(IProcessorSet).getByName(processor), |
4968 | + ) |
4969 | das.addOrUpdateChroot(self.factory.makeLibraryFileAlias()) |
4970 | self.architectures.append(das) |
4971 | - self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'})) |
4972 | + self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"})) |
4973 | self.oci_project = self.factory.makeOCIProject( |
4974 | pillar=self.distroseries.distribution, |
4975 | - ociprojectname="oci-project-name") |
4976 | + ociprojectname="oci-project-name", |
4977 | + ) |
4978 | |
4979 | def makeRecipes(self, count=1, **kwargs): |
4980 | with person_logged_in(self.person): |
4981 | owner = self.factory.makePerson() |
4982 | - return [self.factory.makeOCIRecipe( |
4983 | - registrant=owner, owner=owner, oci_project=self.oci_project, |
4984 | - **kwargs) |
4985 | - for _ in range(count)] |
4986 | + return [ |
4987 | + self.factory.makeOCIRecipe( |
4988 | + registrant=owner, |
4989 | + owner=owner, |
4990 | + oci_project=self.oci_project, |
4991 | + **kwargs, |
4992 | + ) |
4993 | + for _ in range(count) |
4994 | + ] |
4995 | |
4996 | def test_oci_recipe_list_for_person(self): |
4997 | owner = self.factory.makePerson(name="recipe-owner") |
4998 | for i in range(2): |
4999 | self.factory.makeOCIRecipe( |
5000 | - name="my-oci-recipe-%s" % i, owner=owner, registrant=owner) |
The diff has been truncated for viewing.
Self-approving based on previous discussions. I've run the `lp.oci` tests.