Merge ~cjwatson/launchpad:black-oci into launchpad: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)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+427157@code.launchpad.net

Commit message

lp.oci: Apply black

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

Self-approving based on previous discussions. I've run the `lp.oci` tests.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
2index 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
11diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
12index 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
39diff --git a/lib/lp/oci/browser/hasocirecipes.py b/lib/lp/oci/browser/hasocirecipes.py
40index 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")
71diff --git a/lib/lp/oci/browser/ocirecipe.py b/lib/lp/oci/browser/ocirecipe.py
72index 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):
1353diff --git a/lib/lp/oci/browser/ocirecipebuild.py b/lib/lp/oci/browser/ocirecipebuild.py
1354index 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")
1504diff --git a/lib/lp/oci/browser/ocirecipesubscription.py b/lib/lp/oci/browser/ocirecipesubscription.py
1505index 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+ )
1675diff --git a/lib/lp/oci/browser/tests/test_ocirecipe.py b/lib/lp/oci/browser/tests/test_ocirecipe.py
1676index 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- "&#x27;non-existing git-ref&#x27;."
3156- ) % git_ref.repository.display_name
3157+ "The repository at %s does not contain a branch named "
3158+ "&#x27;non-existing git-ref&#x27;."
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.

Subscribers

People subscribed via source and target branches

to status/vote changes: