← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:black-oci into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:black-oci into launchpad:master.

Commit message:
lp.oci: Apply black

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/427157
-- 
The attached diff has been truncated due to its size.
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:black-oci into launchpad:master.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index f667459..1166ab6 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -76,3 +76,5 @@ a6bed71f3d2fdbceae20c2d435c993e8bededdce
 ed7d7b97b8fb4ebe92799f922b0fa9c4bd1714e8
 # apply black to lp.coop
 1e6ead9387a1d073eea9271fe8dc646bb22fa3d2
+# apply black to lp.oci
+06b1048510a0b65bb0a6fc46d4e063510b52b5f6
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a7d8233..43f0476 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -52,6 +52,7 @@ repos:
             |code
             |codehosting
             |coop
+            |oci
           )/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
@@ -80,6 +81,7 @@ repos:
             |code
             |codehosting
             |coop
+            |oci
           )/
     -   id: isort
         alias: isort-black
@@ -98,6 +100,7 @@ repos:
             |code
             |codehosting
             |coop
+            |oci
           )/
 -   repo: https://github.com/PyCQA/flake8
     rev: 3.9.2
diff --git a/lib/lp/oci/browser/hasocirecipes.py b/lib/lp/oci/browser/hasocirecipes.py
index a32ff79..efb92aa 100644
--- a/lib/lp/oci/browser/hasocirecipes.py
+++ b/lib/lp/oci/browser/hasocirecipes.py
@@ -4,8 +4,8 @@
 """Mixins for browser classes for objects related to OCI recipe."""
 
 __all__ = [
-    'HasOCIRecipesMenuMixin',
-    ]
+    "HasOCIRecipesMenuMixin",
+]
 
 from zope.component import getUtility
 
@@ -17,8 +17,11 @@ class HasOCIRecipesMenuMixin:
     """A mixin for context menus for objects that has OCI recipes."""
 
     def view_oci_recipes(self):
-        target = '+oci-recipes'
-        text = 'View OCI recipes'
-        enabled = not getUtility(IOCIRecipeSet).findByContext(
-            self.context, visible_by_user=self.user).is_empty()
-        return Link(target, text, enabled=enabled, icon='info')
+        target = "+oci-recipes"
+        text = "View OCI recipes"
+        enabled = (
+            not getUtility(IOCIRecipeSet)
+            .findByContext(self.context, visible_by_user=self.user)
+            .is_empty()
+        )
+        return Link(target, text, enabled=enabled, icon="info")
diff --git a/lib/lp/oci/browser/ocirecipe.py b/lib/lp/oci/browser/ocirecipe.py
index 7329652..0b9f4d2 100644
--- a/lib/lp/oci/browser/ocirecipe.py
+++ b/lib/lp/oci/browser/ocirecipe.py
@@ -4,22 +4,19 @@
 """OCI recipe views."""
 
 __all__ = [
-    'OCIRecipeAddView',
-    'OCIRecipeAdminView',
-    'OCIRecipeContextMenu',
-    'OCIRecipeDeleteView',
-    'OCIRecipeEditPushRulesView',
-    'OCIRecipeEditView',
-    'OCIRecipeNavigation',
-    'OCIRecipeNavigationMenu',
-    'OCIRecipeRequestBuildsView',
-    'OCIRecipeView',
-    ]
-
-from lazr.restful.interface import (
-    copy_field,
-    use_template,
-    )
+    "OCIRecipeAddView",
+    "OCIRecipeAdminView",
+    "OCIRecipeContextMenu",
+    "OCIRecipeDeleteView",
+    "OCIRecipeEditPushRulesView",
+    "OCIRecipeEditView",
+    "OCIRecipeNavigation",
+    "OCIRecipeNavigationMenu",
+    "OCIRecipeRequestBuildsView",
+    "OCIRecipeView",
+]
+
+from lazr.restful.interface import copy_field, use_template
 from zope.component import getUtility
 from zope.formlib.form import FormFields
 from zope.formlib.textwidgets import TextAreaWidget
@@ -27,7 +24,7 @@ from zope.formlib.widget import (
     CustomWidgetFactory,
     DisplayWidget,
     renderElement,
-    )
+)
 from zope.interface import Interface
 from zope.schema import (
     Bool,
@@ -37,66 +34,63 @@ from zope.schema import (
     Text,
     TextLine,
     ValidationError,
-    )
+)
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.browser.lazrjs import InlinePersonEditPickerWidget
-from lp.app.browser.tales import (
-    format_link,
-    GitRepositoryFormatterAPI,
-    )
+from lp.app.browser.tales import GitRepositoryFormatterAPI, format_link
 from lp.app.errors import UnexpectedFormData
 from lp.app.validators.validation import validate_oci_branch_name
 from lp.app.vocabularies import InformationTypeVocabulary
 from lp.app.widgets.itemswidgets import (
     LabeledMultiCheckBoxWidget,
     LaunchpadRadioWidgetWithDescription,
-    )
+)
 from lp.buildmaster.interfaces.processor import IProcessorSet
 from lp.code.browser.widgets.gitref import GitRefWidget
 from lp.code.interfaces.gitrepository import IGitRepositorySet
 from lp.oci.interfaces.ocipushrule import (
     IOCIPushRuleSet,
     OCIPushRuleAlreadyExists,
-    )
+)
 from lp.oci.interfaces.ocirecipe import (
+    OCI_RECIPE_ALLOW_CREATE,
+    OCI_RECIPE_WEBHOOKS_FEATURE_FLAG,
     IOCIRecipe,
     IOCIRecipeSet,
     NoSuchOCIRecipe,
-    OCI_RECIPE_ALLOW_CREATE,
-    OCI_RECIPE_WEBHOOKS_FEATURE_FLAG,
     OCIRecipeFeatureDisabled,
-    )
+)
 from lp.oci.interfaces.ocirecipebuild import (
     IOCIRecipeBuildSet,
     OCIRecipeBuildRegistryUploadStatus,
-    )
+)
 from lp.oci.interfaces.ocirecipejob import IOCIRecipeRequestBuildsJobSource
 from lp.oci.interfaces.ociregistrycredentials import (
     IOCIRegistryCredentialsSet,
     OCIRegistryCredentialsAlreadyExist,
     user_can_edit_credentials_for_owner,
-    )
+)
 from lp.registry.interfaces.person import IPersonSet
 from lp.services.features import getFeatureFlag
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
     NavigationMenu,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
     structured,
-    )
+)
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.breadcrumb import NameBreadcrumb
@@ -110,7 +104,7 @@ class OCIRecipeNavigation(WebhookTargetNavigationMixin, Navigation):
 
     usedfor = IOCIRecipe
 
-    @stepthrough('+build-request')
+    @stepthrough("+build-request")
     def traverse_build_request(self, name):
         try:
             job_id = int(name)
@@ -118,14 +112,14 @@ class OCIRecipeNavigation(WebhookTargetNavigationMixin, Navigation):
             return None
         return self.context.getBuildRequest(job_id)
 
-    @stepthrough('+build')
+    @stepthrough("+build")
     def traverse_build(self, name):
         build = get_build_by_id_str(IOCIRecipeBuildSet, name)
         if build is None or build.recipe != self.context:
             return None
         return build
 
-    @stepthrough('+push-rule')
+    @stepthrough("+push-rule")
     def traverse_pushrule(self, id):
         id = int(id)
         return getUtility(IOCIPushRuleSet).getByID(id)
@@ -139,7 +133,6 @@ class OCIRecipeNavigation(WebhookTargetNavigationMixin, Navigation):
 
 
 class OCIRecipeBreadcrumb(NameBreadcrumb):
-
     @property
     def inside(self):
         return self.context.oci_project
@@ -162,11 +155,14 @@ class OCIRecipeNavigationMenu(NavigationMenu):
     def edit(self):
         return Link("+edit", "Edit OCI recipe", icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def webhooks(self):
         return Link(
-            '+webhooks', 'Manage webhooks', icon='edit',
-            enabled=bool(getFeatureFlag(OCI_RECIPE_WEBHOOKS_FEATURE_FLAG)))
+            "+webhooks",
+            "Manage webhooks",
+            icon="edit",
+            enabled=bool(getFeatureFlag(OCI_RECIPE_WEBHOOKS_FEATURE_FLAG)),
+        )
 
     @enabled_with_permission("launchpad.Edit")
     def delete(self):
@@ -178,19 +174,22 @@ class OCIRecipeContextMenu(ContextMenu):
 
     usedfor = IOCIRecipe
 
-    facet = 'overview'
+    facet = "overview"
 
-    links = ('request_builds', 'edit_push_rules',
-             'add_subscriber', 'subscription')
+    links = (
+        "request_builds",
+        "edit_push_rules",
+        "add_subscriber",
+        "subscription",
+    )
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def request_builds(self):
-        return Link('+request-builds', 'Request builds', icon='add')
+        return Link("+request-builds", "Request builds", icon="add")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit_push_rules(self):
-        return Link(
-            '+edit-push-rules', 'Edit push rules', icon='edit')
+        return Link("+edit-push-rules", "Edit push rules", icon="edit")
 
     @enabled_with_permission("launchpad.AnyPerson")
     def subscription(self):
@@ -214,11 +213,12 @@ class OCIRecipeListingView(LaunchpadView):
     """Default view for the list of OCI recipes of a context (OCI project
     or Person).
     """
-    page_title = 'Recipes'
+
+    page_title = "Recipes"
 
     @property
     def label(self):
-        return 'OCI recipes for %s' % self.context.name
+        return "OCI recipes for %s" % self.context.name
 
     @property
     def title(self):
@@ -227,8 +227,9 @@ class OCIRecipeListingView(LaunchpadView):
     @property
     def recipes(self):
         recipes = getUtility(IOCIRecipeSet).findByContext(
-            self.context, visible_by_user=self.user)
-        return recipes.order_by('name')
+            self.context, visible_by_user=self.user
+        )
+        return recipes.order_by("name")
 
     @property
     def recipes_navigator(self):
@@ -266,8 +267,7 @@ class OCIRecipeView(LaunchpadView):
         # We're still not sure how this plays into
         # plans for internal registry announcements and such, so we
         # land redaction first and think about this later.
-        return list(
-            getUtility(IOCIPushRuleSet).findByRecipe(self.context))
+        return list(getUtility(IOCIPushRuleSet).findByRecipe(self.context))
 
     @property
     def has_push_rules(self):
@@ -284,10 +284,15 @@ class OCIRecipeView(LaunchpadView):
     def person_picker(self):
         field = copy_field(
             IOCIRecipe["owner"],
-            vocabularyName="AllUserTeamsParticipationPlusSelfSimpleDisplay")
+            vocabularyName="AllUserTeamsParticipationPlusSelfSimpleDisplay",
+        )
         return InlinePersonEditPickerWidget(
-            self.context, field, format_link(self.context.owner),
-            header="Change owner", step_title="Select a new owner")
+            self.context,
+            field,
+            format_link(self.context.owner),
+            header="Change owner",
+            step_title="Select a new owner",
+        )
 
     @property
     def build_frequency(self):
@@ -300,11 +305,12 @@ class OCIRecipeView(LaunchpadView):
     def build_args(self):
         return "\n".join(
             "%s=%s" % (k, v)
-            for k, v in sorted(self.context.build_args.items()))
+            for k, v in sorted(self.context.build_args.items())
+        )
 
     @property
     def distribution_has_credentials(self):
-        if hasattr(self.context, 'oci_project'):
+        if hasattr(self.context, "oci_project"):
             oci_project = self.context.oci_project
         else:
             oci_project = self.context
@@ -313,13 +319,12 @@ class OCIRecipeView(LaunchpadView):
 
     def getImageForStatus(self, status):
         image_map = {
-            BuildSetStatus.NEEDSBUILD: '/@@/build-needed',
-            BuildSetStatus.FULLYBUILT_PENDING: '/@@/build-success-publishing',
-            BuildSetStatus.FAILEDTOBUILD: '/@@/no',
-            BuildSetStatus.BUILDING: '/@@/processing',
-            }
-        return image_map.get(
-            status, '/@@/yes')
+            BuildSetStatus.NEEDSBUILD: "/@@/build-needed",
+            BuildSetStatus.FULLYBUILT_PENDING: "/@@/build-success-publishing",
+            BuildSetStatus.FAILEDTOBUILD: "/@@/no",
+            BuildSetStatus.BUILDING: "/@@/processing",
+        }
+        return image_map.get(status, "/@@/yes")
 
     def _convertBuildJobToStatus(self, build_job):
         recipe_set = getUtility(IOCIRecipeSet)
@@ -327,7 +332,8 @@ class OCIRecipeView(LaunchpadView):
         upload_status = build_job.registry_upload_status
         # This is just a dict, but zope wraps it as RecipeSet is secured
         status = removeSecurityProxy(
-                    recipe_set.getStatusSummaryForBuilds([build_job]))
+            recipe_set.getStatusSummaryForBuilds([build_job])
+        )
         # Add the registry job status
         status["upload_requested"] = upload_status != unscheduled_upload
         status["upload"] = upload_status
@@ -338,7 +344,7 @@ class OCIRecipeView(LaunchpadView):
             "job_id": "build{}".format(build_job.id),
             "date_created": build_job.date_created,
             "date_finished": build_job.date_finished,
-            "build_status": status
+            "build_status": status,
         }
 
     def build_requests(self):
@@ -352,7 +358,7 @@ class OCIRecipeView(LaunchpadView):
         if len(build_requests) < 10:
             recipe = self.context
             no_request_builds = recipe.completed_builds_without_build_request
-            for build in no_request_builds[:10 - len(build_requests)]:
+            for build in no_request_builds[: 10 - len(build_requests)]:
                 build_requests.append(self._convertBuildJobToStatus(build))
         return build_requests[:10]
 
@@ -372,7 +378,7 @@ def builds_for_recipe(recipe):
     """
     builds = list(recipe.pending_builds)
     if len(builds) < 10:
-        builds.extend(recipe.completed_builds[:10 - len(builds)])
+        builds.extend(recipe.completed_builds[: 10 - len(builds)])
     return builds
 
 
@@ -408,8 +414,7 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
 
     @cachedproperty
     def push_rules(self):
-        return list(
-            getUtility(IOCIPushRuleSet).findByRecipe(self.context))
+        return list(getUtility(IOCIPushRuleSet).findByRecipe(self.context))
 
     @property
     def has_push_rules(self):
@@ -418,7 +423,8 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
     @cachedproperty
     def can_edit_credentials(self):
         return user_can_edit_credentials_for_owner(
-            self.context.owner, self.user)
+            self.context.owner, self.user
+        )
 
     def _getFieldName(self, name, rule_id):
         """Get the combined field name for an `OCIPushRule` ID.
@@ -437,13 +443,15 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
         field_bits = field_name.split(".")
         if len(field_bits) != 2:
             raise UnexpectedFormData(
-                "Cannot parse field name: %s" % field_name)
+                "Cannot parse field name: %s" % field_name
+            )
         field_type = field_bits[0]
         try:
             rule_id = int(field_bits[1])
         except ValueError:
             raise UnexpectedFormData(
-                "Cannot parse field name: %s" % field_name)
+                "Cannot parse field name: %s" % field_name
+            )
         return field_type, rule_id
 
     def setUpFields(self):
@@ -460,26 +468,38 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
         for elem in list(self.context.push_rules):
             image_name_fields.append(
                 TextLine(
-                    __name__=self._getFieldName('image_name', elem.id),
+                    __name__=self._getFieldName("image_name", elem.id),
                     default=elem.image_name,
-                    required=True, readonly=False))
-            if check_permission('launchpad.View', elem.registry_credentials):
+                    required=True,
+                    readonly=False,
+                )
+            )
+            if check_permission("launchpad.View", elem.registry_credentials):
                 url_fields.append(
                     TextLine(
-                        __name__=self._getFieldName('url', elem.id),
+                        __name__=self._getFieldName("url", elem.id),
                         default=elem.registry_credentials.url,
-                        required=True, readonly=True))
+                        required=True,
+                        readonly=True,
+                    )
+                )
                 region = elem.registry_credentials.region
                 region_fields.append(
                     TextLine(
-                        __name__=self._getFieldName('region', elem.id),
+                        __name__=self._getFieldName("region", elem.id),
                         default=region,
-                        required=False, readonly=True))
+                        required=False,
+                        readonly=True,
+                    )
+                )
                 username_fields.append(
                     TextLine(
-                        __name__=self._getFieldName('username', elem.id),
+                        __name__=self._getFieldName("username", elem.id),
                         default=elem.registry_credentials.username,
-                        required=True, readonly=True))
+                        required=True,
+                        readonly=True,
+                    )
+                )
             else:
                 # XXX cjwatson 2020-08-27: Ideally we'd be able to just show
                 # the URL, and maybe the username too, but the
@@ -492,71 +512,89 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
                 # viewer's recipes.
                 private_url_fields.append(
                     TextLine(
-                        __name__=self._getFieldName('url', elem.id),
-                        default='', required=True, readonly=True))
+                        __name__=self._getFieldName("url", elem.id),
+                        default="",
+                        required=True,
+                        readonly=True,
+                    )
+                )
                 private_region_fields.append(
                     TextLine(
-                        __name__=self._getFieldName('region', elem.id),
-                        default='', required=False, readonly=True))
+                        __name__=self._getFieldName("region", elem.id),
+                        default="",
+                        required=False,
+                        readonly=True,
+                    )
+                )
                 private_username_fields.append(
                     TextLine(
-                        __name__=self._getFieldName('username', elem.id),
-                        default='', required=True, readonly=True))
+                        __name__=self._getFieldName("username", elem.id),
+                        default="",
+                        required=True,
+                        readonly=True,
+                    )
+                )
             delete_fields.append(
                 Bool(
-                    __name__=self._getFieldName('delete', elem.id),
+                    __name__=self._getFieldName("delete", elem.id),
                     default=False,
-                    required=True, readonly=False))
+                    required=True,
+                    readonly=False,
+                )
+            )
         image_name_fields.append(
-            TextLine(
-                __name__='add_image_name',
-                required=False, readonly=False))
+            TextLine(__name__="add_image_name", required=False, readonly=False)
+        )
         add_credentials = Choice(
-            __name__='add_credentials',
-            default='existing', values=('existing', 'new'),
-            required=False, readonly=False)
+            __name__="add_credentials",
+            default="existing",
+            values=("existing", "new"),
+            required=False,
+            readonly=False,
+        )
         existing_credentials = Choice(
-            vocabulary='OCIRegistryCredentials',
+            vocabulary="OCIRegistryCredentials",
             required=False,
-            __name__='existing_credentials')
+            __name__="existing_credentials",
+        )
         url_fields.append(
-            TextLine(
-                __name__='add_url',
-                required=False, readonly=False))
+            TextLine(__name__="add_url", required=False, readonly=False)
+        )
         region_fields.append(
-            TextLine(
-                __name__='add_region',
-                required=False, readonly=False))
+            TextLine(__name__="add_region", required=False, readonly=False)
+        )
         username_fields.append(
-            TextLine(
-                __name__='add_username',
-                required=False, readonly=False))
+            TextLine(__name__="add_username", required=False, readonly=False)
+        )
         password_fields.append(
-            Password(
-                __name__='add_password',
-                required=False, readonly=False))
+            Password(__name__="add_password", required=False, readonly=False)
+        )
         password_fields.append(
             Password(
-                __name__='add_confirm_password',
-                required=False, readonly=False))
+                __name__="add_confirm_password", required=False, readonly=False
+            )
+        )
 
         self.form_fields = (
-            FormFields(*image_name_fields) +
-            FormFields(*url_fields) +
-            FormFields(
-                *private_url_fields,
-                custom_widget=InvisibleCredentialsWidget) +
-            FormFields(*region_fields) +
-            FormFields(
+            FormFields(*image_name_fields)
+            + FormFields(*url_fields)
+            + FormFields(
+                *private_url_fields, custom_widget=InvisibleCredentialsWidget
+            )
+            + FormFields(*region_fields)
+            + FormFields(
                 *private_region_fields,
-                custom_widget=InvisibleCredentialsWidget) +
-            FormFields(*username_fields) +
-            FormFields(
+                custom_widget=InvisibleCredentialsWidget,
+            )
+            + FormFields(*username_fields)
+            + FormFields(
                 *private_username_fields,
-                custom_widget=InvisibleCredentialsWidget) +
-            FormFields(*password_fields) +
-            FormFields(*delete_fields) +
-            FormFields(add_credentials, existing_credentials))
+                custom_widget=InvisibleCredentialsWidget,
+            )
+            + FormFields(*password_fields)
+            + FormFields(*delete_fields)
+            + FormFields(add_credentials, existing_credentials)
+        )
 
     def setUpWidgets(self, context=None):
         """See `LaunchpadFormView`."""
@@ -567,9 +605,9 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
 
     @property
     def label(self):
-        return 'Edit OCI push rules for %s' % self.context.name
+        return "Edit OCI push rules for %s" % self.context.name
 
-    page_title = 'Edit OCI push rules'
+    page_title = "Edit OCI push rules"
 
     @property
     def cancel_url(self):
@@ -577,16 +615,13 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
 
     def getRuleWidgets(self, rule):
         widgets_by_name = {widget.name: widget for widget in self.widgets}
-        url_field_name = (
-                "field." + self._getFieldName("url", rule.id))
-        region_field_name = (
-                "field." + self._getFieldName("region", rule.id))
-        image_field_name = (
-                "field." + self._getFieldName("image_name", rule.id))
-        username_field_name = (
-                "field." + self._getFieldName("username", rule.id))
-        delete_field_name = (
-                "field." + self._getFieldName("delete", rule.id))
+        url_field_name = "field." + self._getFieldName("url", rule.id)
+        region_field_name = "field." + self._getFieldName("region", rule.id)
+        image_field_name = "field." + self._getFieldName("image_name", rule.id)
+        username_field_name = "field." + self._getFieldName(
+            "username", rule.id
+        )
+        delete_field_name = "field." + self._getFieldName("delete", rule.id)
         return {
             "url": widgets_by_name[url_field_name],
             "region": widgets_by_name[region_field_name],
@@ -599,14 +634,15 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
         widgets_by_name = {widget.name: widget for widget in self.widgets}
         return {
             "image_name": widgets_by_name["field.add_image_name"],
-            "existing_credentials":
-                widgets_by_name["field.existing_credentials"],
+            "existing_credentials": widgets_by_name[
+                "field.existing_credentials"
+            ],
             "url": widgets_by_name["field.add_url"],
             "region": widgets_by_name["field.add_region"],
             "username": widgets_by_name["field.add_username"],
             "password": widgets_by_name["field.add_password"],
             "confirm_password": widgets_by_name["field.add_confirm_password"],
-            }
+        }
 
     def parseData(self, data):
         """Rearrange form data to make it easier to process."""
@@ -620,23 +656,33 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
         add_existing_credentials = data.get("existing_credentials")
 
         # parse data from the Add new rule section of the form
-        if (add_url or add_region or add_username or add_password or
-                add_confirm_password or add_image_name or
-                add_existing_credentials):
-            parsed_data.setdefault(None, {
-                "image_name": add_image_name,
-                "url": add_url,
-                "region": add_region,
-                "username": add_username,
-                "password": add_password,
-                "confirm_password": add_confirm_password,
-                "existing_credentials": data["existing_credentials"],
-                "add_credentials": data["add_credentials"],
-                "action": "add",
-            })
+        if (
+            add_url
+            or add_region
+            or add_username
+            or add_password
+            or add_confirm_password
+            or add_image_name
+            or add_existing_credentials
+        ):
+            parsed_data.setdefault(
+                None,
+                {
+                    "image_name": add_image_name,
+                    "url": add_url,
+                    "region": add_region,
+                    "username": add_username,
+                    "password": add_password,
+                    "confirm_password": add_confirm_password,
+                    "existing_credentials": data["existing_credentials"],
+                    "add_credentials": data["add_credentials"],
+                    "action": "add",
+                },
+            )
         # parse data from the Edit existing rule section of the form
         for field_name in sorted(
-                name for name in data if name.split(".")[0] == "image_name"):
+            name for name in data if name.split(".")[0] == "image_name"
+        ):
             _, rule_id = self._parseFieldName(field_name)
             image_field_name = self._getFieldName("image_name", rule_id)
             delete_field_name = self._getFieldName("delete", rule_id)
@@ -644,10 +690,13 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
                 action = "delete"
             else:
                 action = "change"
-            parsed_data.setdefault(rule_id, {
-                "image_name": data.get(image_field_name),
-                "action": action,
-            })
+            parsed_data.setdefault(
+                rule_id,
+                {
+                    "image_name": data.get(image_field_name),
+                    "action": action,
+                },
+            )
 
         return parsed_data
 
@@ -678,22 +727,27 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
                 return
             if password != confirm_password:
                 self.setFieldError(
-                    "add_confirm_password", "Passwords do not match.")
+                    "add_confirm_password", "Passwords do not match."
+                )
                 return
 
             credentials_set = getUtility(IOCIRegistryCredentialsSet)
             try:
-                credential_data = {'username': username, 'password': password}
+                credential_data = {"username": username, "password": password}
                 if region is not None:
-                    credential_data['region'] = region
+                    credential_data["region"] = region
                 credentials = credentials_set.getOrCreate(
-                    registrant=self.user, owner=self.context.owner, url=url,
-                    credentials=credential_data)
+                    registrant=self.user,
+                    owner=self.context.owner,
+                    url=url,
+                    credentials=credential_data,
+                )
             except OCIRegistryCredentialsAlreadyExist:
                 self.setFieldError(
                     "add_url",
                     "Credentials already exist with the same URL, username, "
-                    "and region.")
+                    "and region.",
+                )
                 return
             except ValidationError:
                 self.setFieldError("add_url", "Not a valid URL.")
@@ -701,17 +755,17 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
 
         try:
             getUtility(IOCIPushRuleSet).new(
-                self.context, credentials, image_name)
+                self.context, credentials, image_name
+            )
         except OCIPushRuleAlreadyExists:
             self.setFieldError(
                 "add_image_name",
                 "A push rule already exists with the same URL, image name, "
-                "and credentials.")
+                "and credentials.",
+            )
 
     def updatePushRulesFromData(self, parsed_data):
-        rules_map = {
-            rule.id: rule
-            for rule in self.context.push_rules}
+        rules_map = {rule.id: rule for rule in self.context.push_rules}
         for rule_id, parsed_rules in parsed_data.items():
             rule = rules_map.get(rule_id)
             action = parsed_rules["action"]
@@ -720,9 +774,9 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
                 image_name = parsed_rules["image_name"]
                 if not image_name:
                     self.setFieldError(
-                        self._getFieldName(
-                            "image_name", rule_id),
-                        "Image name must be set.")
+                        self._getFieldName("image_name", rule_id),
+                        "Image name must be set.",
+                    )
                 else:
                     if rule.image_name != image_name:
                         rule.setNewImageName(image_name)
@@ -748,16 +802,18 @@ class OCIRecipeRequestBuildsView(LaunchpadFormView):
 
     @property
     def label(self):
-        return 'Request builds for %s' % self.context.name
+        return "Request builds for %s" % self.context.name
 
-    page_title = 'Request builds'
+    page_title = "Request builds"
 
     class schema(Interface):
         """Schema for requesting a build."""
 
         distro_arch_series = List(
-            Choice(vocabulary='OCIRecipeDistroArchSeries'),
-            title='Architectures', required=True)
+            Choice(vocabulary="OCIRecipeDistroArchSeries"),
+            title="Architectures",
+            required=True,
+        )
 
     custom_widget_distro_arch_series = LabeledMultiCheckBoxWidget
 
@@ -768,61 +824,71 @@ class OCIRecipeRequestBuildsView(LaunchpadFormView):
     @property
     def initial_values(self):
         """See `LaunchpadFormView`."""
-        return {'distro_arch_series': self.context.getAllowedArchitectures()}
+        return {"distro_arch_series": self.context.getAllowedArchitectures()}
 
     def validate(self, data):
         """See `LaunchpadFormView`."""
-        arches = data.get('distro_arch_series', [])
+        arches = data.get("distro_arch_series", [])
         if not arches:
             self.setFieldError(
-                'distro_arch_series',
-                'You need to select at least one architecture.')
+                "distro_arch_series",
+                "You need to select at least one architecture.",
+            )
 
-    @action('Request builds', name='request')
+    @action("Request builds", name="request")
     def request_action(self, action, data):
-        if data.get('distro_arch_series'):
+        if data.get("distro_arch_series"):
             architectures = [
-                arch.architecturetag for arch in data['distro_arch_series']]
+                arch.architecturetag for arch in data["distro_arch_series"]
+            ]
         else:
             architectures = None
         self.context.requestBuilds(self.user, architectures)
         self.request.response.addNotification(
             "Your builds were scheduled and should start soon. "
-            "Refresh this page for details.")
+            "Refresh this page for details."
+        )
         self.next_url = self.cancel_url
 
 
 class IOCIRecipeEditSchema(Interface):
     """Schema for adding or editing an OCI recipe."""
 
-    use_template(IOCIRecipe, include=[
-        "name",
-        "owner",
-        "information_type",
-        "description",
-        "git_ref",
-        "build_file",
-        "build_args",
-        "build_path",
-        "build_daily",
-        "require_virtualized",
-        "allow_internet",
-        ])
+    use_template(
+        IOCIRecipe,
+        include=[
+            "name",
+            "owner",
+            "information_type",
+            "description",
+            "git_ref",
+            "build_file",
+            "build_args",
+            "build_path",
+            "build_daily",
+            "require_virtualized",
+            "allow_internet",
+        ],
+    )
 
 
 class OCIRecipeFormMixin:
     """Mixin with common processing for both edit and add views."""
+
     custom_widget_build_args = CustomWidgetFactory(
-        TextAreaWidget, height=5, width=100)
+        TextAreaWidget, height=5, width=100
+    )
 
     custom_widget_information_type = CustomWidgetFactory(
         LaunchpadRadioWidgetWithDescription,
-        vocabulary=InformationTypeVocabulary(types=[]))
+        vocabulary=InformationTypeVocabulary(types=[]),
+    )
 
     def setUpInformationTypeWidget(self):
-        info_type_widget = self.widgets['information_type']
+        info_type_widget = self.widgets["information_type"]
         info_type_widget.vocabulary = InformationTypeVocabulary(
-            types=self.getInformationTypesToShow())
+            types=self.getInformationTypesToShow()
+        )
 
     def getInformationTypesToShow(self):
         """Get the information types to display on the edit form.
@@ -840,36 +906,45 @@ class OCIRecipeFormMixin:
         if IOCIRecipe.providedBy(self.context):
             default = "\n".join(
                 "%s=%s" % (k, v)
-                for k, v in sorted(self.context.build_args.items()))
+                for k, v in sorted(self.context.build_args.items())
+            )
         else:
             default = ""
-        return FormFields(Text(
-            __name__='build_args',
-            title='Build-time ARG variables',
-            description=("One per line. Each ARG should be in the format "
-                         "of ARG_KEY=arg_value."),
-            default=default,
-            required=False, readonly=False))
+        return FormFields(
+            Text(
+                __name__="build_args",
+                title="Build-time ARG variables",
+                description=(
+                    "One per line. Each ARG should be in the format "
+                    "of ARG_KEY=arg_value."
+                ),
+                default=default,
+                required=False,
+                readonly=False,
+            )
+        )
 
     def validateBuildArgs(self, data):
-        field_value = data.get('build_args')
+        field_value = data.get("build_args")
         if not field_value:
             return
         build_args = {}
         for i, line in enumerate(field_value.split("\n")):
-            if '=' not in line:
-                msg = ("'%s' at line %s is not a valid KEY=value pair." %
-                       (line, i + 1))
+            if "=" not in line:
+                msg = "'%s' at line %s is not a valid KEY=value pair." % (
+                    line,
+                    i + 1,
+                )
                 self.setFieldError("build_args", str(msg))
                 return
-            k, v = line.split('=', 1)
+            k, v = line.split("=", 1)
             build_args[k] = v
-        data['build_args'] = build_args
+        data["build_args"] = build_args
 
     def userIsRecipeAdmin(self):
         if check_permission("launchpad.Admin", self.context):
             return True
-        person = getattr(self.request.principal, 'person', None)
+        person = getattr(self.request.principal, "person", None)
         if not person:
             return False
         # Edit context = OCIRecipe, New context = OCIProject
@@ -890,7 +965,7 @@ class OCIRecipeFormMixin:
 
     @property
     def distribution_has_credentials(self):
-        if hasattr(self.context, 'oci_project'):
+        if hasattr(self.context, "oci_project"):
             oci_project = self.context.oci_project
         else:
             oci_project = self.context
@@ -898,8 +973,9 @@ class OCIRecipeFormMixin:
         return bool(distro and distro.oci_registry_credentials)
 
 
-class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin,
-                       OCIRecipeFormMixin):
+class OCIRecipeAddView(
+    LaunchpadFormView, EnableProcessorsMixin, OCIRecipeFormMixin
+):
     """View for creating OCI recipes."""
 
     page_title = label = "Create a new OCI recipe"
@@ -914,7 +990,7 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin,
         "build_file",
         "build_path",
         "build_daily",
-        )
+    )
     custom_widget_git_ref = GitRefWidget
 
     def initialize(self):
@@ -930,25 +1006,36 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin,
             getUtility(IProcessorSet).getAll(),
             "The architectures that this OCI recipe builds for. Some "
             "architectures are restricted and may only be enabled or "
-            "disabled by administrators.")
-        self.form_fields += FormFields(Bool(
-            __name__="official_recipe",
-            title="Official recipe",
-            description=(
-                "Mark this recipe as official for this OCI Project. "
-                "Allows use of distribution registry credentials "
-                "and the default git repository routing. "
-                "May only be enabled by the owner of the OCI Project."),
-            default=False,
-            required=False, readonly=False))
-        if self.distribution_has_credentials:
-            self.form_fields += FormFields(TextLine(
-                __name__='image_name',
-                title="Image name",
+            "disabled by administrators.",
+        )
+        self.form_fields += FormFields(
+            Bool(
+                __name__="official_recipe",
+                title="Official recipe",
                 description=(
-                    "Name to use for registry upload. "
-                    "Defaults to the name of the recipe."),
-                required=False, readonly=False))
+                    "Mark this recipe as official for this OCI Project. "
+                    "Allows use of distribution registry credentials "
+                    "and the default git repository routing. "
+                    "May only be enabled by the owner of the OCI Project."
+                ),
+                default=False,
+                required=False,
+                readonly=False,
+            )
+        )
+        if self.distribution_has_credentials:
+            self.form_fields += FormFields(
+                TextLine(
+                    __name__="image_name",
+                    title="Image name",
+                    description=(
+                        "Name to use for registry upload. "
+                        "Defaults to the name of the recipe."
+                    ),
+                    required=False,
+                    readonly=False,
+                )
+            )
 
     def setUpGitRefWidget(self):
         """Setup GitRef widget indicating the user to use the default
@@ -963,13 +1050,15 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin,
             # Do not override more important git_ref errors.
             return
         default_repo = getUtility(IGitRepositorySet).getDefaultRepository(
-            self.context)
+            self.context
+        )
         if default_repo is None:
             msg = (
-                'The default git repository for this OCI project was not '
-                'created yet.<br/>'
+                "The default git repository for this OCI project was not "
+                "created yet.<br/>"
                 'Check the <a href="%s">OCI project page</a> for instructions '
-                'on how to create one.')
+                "on how to create one."
+            )
             msg = structured(msg, canonical_url(self.context))
             self.widget_errors["git_ref"] = msg.escapedtext
 
@@ -981,7 +1070,7 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin,
         self.setUpGitRefWidget()
         # disable the official recipe button if the user doesn't have
         # permissions to change it
-        widget = self.widgets['official_recipe']
+        widget = self.widgets["official_recipe"]
         if not self.userIsRecipeAdmin():
             widget.extra = "disabled='disabled'"
 
@@ -998,9 +1087,11 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin,
             "build_file": "Dockerfile",
             "build_path": ".",
             "processors": [
-                p for p in getUtility(IProcessorSet).getAll()
-                if p.build_by_default],
-            }
+                p
+                for p in getUtility(IProcessorSet).getAll()
+                if p.build_by_default
+            ],
+        }
 
     def validate(self, data):
         """See `LaunchpadFormView`."""
@@ -1012,27 +1103,36 @@ class OCIRecipeAddView(LaunchpadFormView, EnableProcessorsMixin,
                 self.setFieldError(
                     "name",
                     "There is already an OCI recipe owned by %s in %s with "
-                    "this name." % (
-                        owner.display_name, self.context.display_name))
+                    "this name."
+                    % (owner.display_name, self.context.display_name),
+                )
         self.validateBuildArgs(data)
         official = data.get("official_recipe", None)
         if official and not self.userIsRecipeAdmin():
             self.setFieldError(
                 "official_recipe",
                 "You do not have permission to set the official status "
-                "of this recipe.")
+                "of this recipe.",
+            )
 
     @action("Create OCI recipe", name="create")
     def create_action(self, action, data):
         recipe = getUtility(IOCIRecipeSet).new(
-            name=data["name"], registrant=self.user, owner=data["owner"],
-            oci_project=self.context, git_ref=data["git_ref"],
-            build_file=data["build_file"], description=data["description"],
-            build_daily=data["build_daily"], build_args=data["build_args"],
-            build_path=data["build_path"], processors=data["processors"],
-            official=data.get('official_recipe', False),
+            name=data["name"],
+            registrant=self.user,
+            owner=data["owner"],
+            oci_project=self.context,
+            git_ref=data["git_ref"],
+            build_file=data["build_file"],
+            description=data["description"],
+            build_daily=data["build_daily"],
+            build_args=data["build_args"],
+            build_path=data["build_path"],
+            processors=data["processors"],
+            official=data.get("official_recipe", False),
             # image_name is only available if using distribution credentials.
-            image_name=data.get("image_name"))
+            image_name=data.get("image_name"),
+        )
         self.next_url = canonical_url(recipe)
 
 
@@ -1055,12 +1155,14 @@ class BaseOCIRecipeEditView(LaunchpadEditFormView):
         if new_processors is not None:
             if set(self.context.processors) != set(new_processors):
                 self.context.setProcessors(
-                    new_processors, check_permissions=True, user=self.user)
+                    new_processors, check_permissions=True, user=self.user
+                )
             del data["processors"]
-        official = data.pop('official_recipe', None)
+        official = data.pop("official_recipe", None)
         if official is not None and self.userIsRecipeAdmin():
             self.context.oci_project.setOfficialRecipeStatus(
-                self.context, official)
+                self.context, official
+            )
 
         self.updateContextFromData(data)
         self.next_url = canonical_url(self.context)
@@ -1083,8 +1185,9 @@ class OCIRecipeAdminView(BaseOCIRecipeEditView):
     field_names = ("require_virtualized", "allow_internet")
 
 
-class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin,
-                        OCIRecipeFormMixin):
+class OCIRecipeEditView(
+    BaseOCIRecipeEditView, EnableProcessorsMixin, OCIRecipeFormMixin
+):
     """View for editing OCI recipes."""
 
     @property
@@ -1102,7 +1205,7 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin,
         "build_file",
         "build_path",
         "build_daily",
-        )
+    )
     custom_widget_git_ref = GitRefWidget
 
     def setUpGitRefWidget(self):
@@ -1119,17 +1222,21 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin,
             return
         msg = None
         if self.context.git_ref.namespace.target != self.context.oci_project:
-            msg = ("This recipe's git repository is not in the "
-                   "correct namespace.<br/>")
+            msg = (
+                "This recipe's git repository is not in the "
+                "correct namespace.<br/>"
+            )
             default_repo = getUtility(IGitRepositorySet).getDefaultRepository(
-                oci_proj)
+                oci_proj
+            )
             if default_repo:
-                link = GitRepositoryFormatterAPI(default_repo).link('')
+                link = GitRepositoryFormatterAPI(default_repo).link("")
                 msg += "Consider using %s instead." % link
             else:
                 msg += (
                     'Check the <a href="%(oci_proj_url)s">OCI project page</a>'
-                    ' for instructions on how to create it correctly.')
+                    " for instructions on how to create it correctly."
+                )
         if msg:
             msg = structured(msg, oci_proj_url=oci_proj_url)
             self.widget_errors["git_ref"] = msg.escapedtext
@@ -1141,7 +1248,7 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin,
         self.setUpGitRefWidget()
         # disable the official recipe button if the user doesn't have
         # permissions to change it
-        widget = self.widgets['official_recipe']
+        widget = self.widgets["official_recipe"]
         if not self.userIsRecipeAdmin():
             widget.extra = "disabled='disabled'"
 
@@ -1153,26 +1260,37 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin,
             self.context.available_processors,
             "The architectures that this OCI recipe builds for. Some "
             "architectures are restricted and may only be enabled or "
-            "disabled by administrators.")
-        self.form_fields += FormFields(Bool(
-            __name__="official_recipe",
-            title="Official recipe",
-            description=(
-                "Mark this recipe as official for this OCI Project. "
-                "Allows use of distribution registry credentials "
-                "and the default git repository routing. "
-                "May only be enabled by the owner of the OCI Project."),
-            default=self.context.official,
-            required=False, readonly=False))
-        if self.distribution_has_credentials:
-            self.form_fields += FormFields(TextLine(
-                __name__='image_name',
-                title="Image name",
+            "disabled by administrators.",
+        )
+        self.form_fields += FormFields(
+            Bool(
+                __name__="official_recipe",
+                title="Official recipe",
                 description=(
-                    "Name to use for registry upload. "
-                    "Defaults to the name of the recipe."),
-                default=self.context.image_name,
-                required=False, readonly=False))
+                    "Mark this recipe as official for this OCI Project. "
+                    "Allows use of distribution registry credentials "
+                    "and the default git repository routing. "
+                    "May only be enabled by the owner of the OCI Project."
+                ),
+                default=self.context.official,
+                required=False,
+                readonly=False,
+            )
+        )
+        if self.distribution_has_credentials:
+            self.form_fields += FormFields(
+                TextLine(
+                    __name__="image_name",
+                    title="Image name",
+                    description=(
+                        "Name to use for registry upload. "
+                        "Defaults to the name of the recipe."
+                    ),
+                    default=self.context.image_name,
+                    required=False,
+                    readonly=False,
+                )
+            )
 
     def validate(self, data):
         """See `LaunchpadFormView`."""
@@ -1184,14 +1302,18 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin,
         if owner and name:
             try:
                 recipe = getUtility(IOCIRecipeSet).getByName(
-                    owner, self.context.oci_project, name)
+                    owner, self.context.oci_project, name
+                )
                 if recipe != self.context:
                     self.setFieldError(
                         "name",
                         "There is already an OCI recipe owned by %s in %s "
-                        "with this name." % (
+                        "with this name."
+                        % (
                             owner.display_name,
-                            self.context.oci_project.display_name))
+                            self.context.oci_project.display_name,
+                        ),
+                    )
             except NoSuchOCIRecipe:
                 pass
         if "processors" in data:
@@ -1208,14 +1330,15 @@ class OCIRecipeEditView(BaseOCIRecipeEditView, EnableProcessorsMixin,
                         # enabled. Leave it untouched.
                         data["processors"].append(processor)
         self.validateBuildArgs(data)
-        official = data.get('official_recipe')
+        official = data.get("official_recipe")
         official_change = self.context.official != official
         is_admin = self.userIsRecipeAdmin()
         if official is not None and official_change and not is_admin:
             self.setFieldError(
                 "official_recipe",
                 "You do not have permission to change the official status "
-                "of this recipe.")
+                "of this recipe.",
+            )
 
 
 class OCIRecipeDeleteView(BaseOCIRecipeEditView):
diff --git a/lib/lp/oci/browser/ocirecipebuild.py b/lib/lp/oci/browser/ocirecipebuild.py
index 9f1d55e..7aaad43 100644
--- a/lib/lp/oci/browser/ocirecipebuild.py
+++ b/lib/lp/oci/browser/ocirecipebuild.py
@@ -4,35 +4,32 @@
 """OCI recipe build views."""
 
 __all__ = [
-    'OCIRecipeBuildCancelView',
-    'OCIRecipeBuildContextMenu',
-    'OCIRecipeBuildNavigation',
-    'OCIRecipeBuildRescoreView',
-    'OCIRecipeBuildView',
-    ]
+    "OCIRecipeBuildCancelView",
+    "OCIRecipeBuildContextMenu",
+    "OCIRecipeBuildNavigation",
+    "OCIRecipeBuildRescoreView",
+    "OCIRecipeBuildView",
+]
 
 from zope.interface import Interface
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.oci.interfaces.ocirecipebuild import (
     CannotScheduleRegistryUpload,
     IOCIRecipeBuild,
-    )
+)
 from lp.services.librarian.browser import (
     FileNavigationMixin,
     ProxiedLibraryFileAlias,
-    )
+)
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     Link,
     Navigation,
-    )
+    canonical_url,
+    enabled_with_permission,
+)
 from lp.soyuz.interfaces.binarypackagebuild import IBuildRescoreForm
 
 
@@ -53,20 +50,29 @@ class OCIRecipeBuildContextMenu(ContextMenu):
     @enabled_with_permission("launchpad.Edit")
     def retry(self):
         return Link(
-            "+retry", "Retry this build", icon="retry",
-            enabled=self.context.can_be_retried)
+            "+retry",
+            "Retry this build",
+            icon="retry",
+            enabled=self.context.can_be_retried,
+        )
 
     @enabled_with_permission("launchpad.Edit")
     def cancel(self):
         return Link(
-            "+cancel", "Cancel build", icon="remove",
-            enabled=self.context.can_be_cancelled)
+            "+cancel",
+            "Cancel build",
+            icon="remove",
+            enabled=self.context.can_be_cancelled,
+        )
 
     @enabled_with_permission("launchpad.Admin")
     def rescore(self):
         return Link(
-            "+rescore", "Rescore build", icon="edit",
-            enabled=self.context.can_be_rescored)
+            "+rescore",
+            "Rescore build",
+            icon="edit",
+            enabled=self.context.can_be_rescored,
+        )
 
 
 class OCIRecipeBuildView(LaunchpadFormView):
@@ -89,7 +95,9 @@ class OCIRecipeBuildView(LaunchpadFormView):
 
         return [
             ProxiedLibraryFileAlias(alias, self.context)
-            for _, alias, _ in self.context.getFiles() if not alias.deleted]
+            for _, alias, _ in self.context.getFiles()
+            if not alias.deleted
+        ]
 
     @cachedproperty
     def has_files(self):
@@ -109,7 +117,8 @@ class OCIRecipeBuildView(LaunchpadFormView):
         else:
             self.request.response.addInfoNotification(
                 "An upload has been scheduled and will run as soon as "
-                "possible.")
+                "possible."
+            )
 
 
 class OCIRecipeBuildRetryView(LaunchpadFormView):
@@ -123,6 +132,7 @@ class OCIRecipeBuildRetryView(LaunchpadFormView):
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
     @action("Retry build", name="retry")
@@ -130,7 +140,8 @@ class OCIRecipeBuildRetryView(LaunchpadFormView):
         """Retry the build."""
         if not self.context.can_be_retried:
             self.request.response.addErrorNotification(
-                "Build cannot be retried")
+                "Build cannot be retried"
+            )
         else:
             self.context.retry()
             self.request.response.addInfoNotification("Build has been queued")
@@ -149,6 +160,7 @@ class OCIRecipeBuildCancelView(LaunchpadFormView):
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
     @action("Cancel build", name="cancel")
@@ -168,12 +180,14 @@ class OCIRecipeBuildRescoreView(LaunchpadFormView):
         if self.context.can_be_rescored:
             return super().__call__()
         self.request.response.addWarningNotification(
-            "Cannot rescore this build because it is not queued.")
+            "Cannot rescore this build because it is not queued."
+        )
         self.request.response.redirect(canonical_url(self.context))
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
     @action("Rescore build", name="rescore")
diff --git a/lib/lp/oci/browser/ocirecipesubscription.py b/lib/lp/oci/browser/ocirecipesubscription.py
index c4f016f..65643ba 100644
--- a/lib/lp/oci/browser/ocirecipesubscription.py
+++ b/lib/lp/oci/browser/ocirecipesubscription.py
@@ -3,9 +3,7 @@
 
 """OCI recipe subscription views."""
 
-__all__ = [
-    'OCIRecipePortletSubscribersContent'
-]
+__all__ = ["OCIRecipePortletSubscribersContent"]
 
 from zope.component import getUtility
 from zope.formlib.form import action
@@ -14,17 +12,14 @@ from zope.security.interfaces import ForbiddenAttribute
 from lp.app.browser.launchpadform import (
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+)
 from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription
 from lp.registry.interfaces.person import IPersonSet
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    )
+from lp.services.webapp import LaunchpadView, canonical_url
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 
 
 class OCIRecipePortletSubscribersContent(LaunchpadView):
@@ -38,20 +33,28 @@ class OCIRecipePortletSubscribersContent(LaunchpadView):
         # need the expense of running several complex SQL queries.
         subscriptions = list(self.context.subscriptions)
         person_ids = [sub.person.id for sub in subscriptions]
-        list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
-            person_ids, need_validity=True))
+        list(
+            getUtility(IPersonSet).getPrecachedPersonsFromIDs(
+                person_ids, need_validity=True
+            )
+        )
         if self.user is not None:
             subscribers = [
-                subscription.person for subscription in subscriptions]
+                subscription.person for subscription in subscriptions
+            ]
             precache_permission_for_objects(
-                self.request, "launchpad.LimitedView", subscribers)
+                self.request, "launchpad.LimitedView", subscribers
+            )
 
         visible_subscriptions = [
-            subscription for subscription in subscriptions
-            if check_permission("launchpad.LimitedView", subscription.person)]
+            subscription
+            for subscription in subscriptions
+            if check_permission("launchpad.LimitedView", subscription.person)
+        ]
         return sorted(
             visible_subscriptions,
-            key=lambda subscription: subscription.person.displayname)
+            key=lambda subscription: subscription.person.displayname,
+        )
 
 
 class RedirectToOCIRecipeMixin:
@@ -73,23 +76,25 @@ class RedirectToOCIRecipeMixin:
     cancel_url = next_url
 
 
-class OCIRecipeSubscriptionEditView(RedirectToOCIRecipeMixin,
-                                    LaunchpadEditFormView):
+class OCIRecipeSubscriptionEditView(
+    RedirectToOCIRecipeMixin, LaunchpadEditFormView
+):
     """The view for editing OCI recipe subscriptions."""
+
     schema = IOCIRecipeSubscription
     field_names = []
 
     @property
     def page_title(self):
         return (
-            "Edit subscription to OCI recipe %s" %
-            self.ocirecipe.displayname)
+            "Edit subscription to OCI recipe %s" % self.ocirecipe.displayname
+        )
 
     @property
     def label(self):
         return (
-            "Edit subscription to OCI recipe for %s" %
-            self.person.displayname)
+            "Edit subscription to OCI recipe for %s" % self.person.displayname
+        )
 
     def initialize(self):
         self.ocirecipe = self.context.recipe
@@ -102,11 +107,13 @@ class OCIRecipeSubscriptionEditView(RedirectToOCIRecipeMixin,
         self.ocirecipe.unsubscribe(self.person, self.user)
         self.request.response.addNotification(
             "%s has been unsubscribed from this OCI recipe."
-            % self.person.displayname)
+            % self.person.displayname
+        )
 
 
-class _OCIRecipeSubscriptionCreationView(RedirectToOCIRecipeMixin,
-                                         LaunchpadFormView):
+class _OCIRecipeSubscriptionCreationView(
+    RedirectToOCIRecipeMixin, LaunchpadFormView
+):
     """Contains the common functionality of the Add and Edit views."""
 
     schema = IOCIRecipeSubscription
@@ -127,11 +134,13 @@ class OCIRecipeSubscriptionAddView(_OCIRecipeSubscriptionCreationView):
         # subscribed before continuing.
         if self.context.getSubscription(self.user) is not None:
             self.request.response.addNotification(
-                "You are already subscribed to this OCI recipe.")
+                "You are already subscribed to this OCI recipe."
+            )
         else:
             self.context.subscribe(self.user, self.user)
             self.request.response.addNotification(
-                "You have subscribed to this OCI recipe.")
+                "You have subscribed to this OCI recipe."
+            )
 
 
 class OCIRecipeSubscriptionAddOtherView(_OCIRecipeSubscriptionCreationView):
@@ -150,12 +159,14 @@ class OCIRecipeSubscriptionAddOtherView(_OCIRecipeSubscriptionCreationView):
         if "person" in data:
             person = data["person"]
             subscription = self.context.getSubscription(person)
-            if (subscription is None
-                    and not self.context.userCanBeSubscribed(person)):
+            if subscription is None and not self.context.userCanBeSubscribed(
+                person
+            ):
                 self.setFieldError(
                     "person",
                     "Open and delegated teams cannot be subscribed to "
-                    "private OCI recipes.")
+                    "private OCI recipes.",
+                )
 
     @action("Subscribe", name="subscribe_action")
     def subscribe_action(self, action, data):
@@ -165,9 +176,11 @@ class OCIRecipeSubscriptionAddOtherView(_OCIRecipeSubscriptionCreationView):
         if subscription is None:
             self.context.subscribe(person, self.user)
             self.request.response.addNotification(
-                "%s has been subscribed to this OCI recipe." %
-                person.displayname)
+                "%s has been subscribed to this OCI recipe."
+                % person.displayname
+            )
         else:
             self.request.response.addNotification(
-                "%s was already subscribed to this OCI recipe." %
-                person.displayname)
+                "%s was already subscribed to this OCI recipe."
+                % person.displayname
+            )
diff --git a/lib/lp/oci/browser/tests/test_ocirecipe.py b/lib/lp/oci/browser/tests/test_ocirecipe.py
index b135ed8..3050f61 100644
--- a/lib/lp/oci/browser/tests/test_ocirecipe.py
+++ b/lib/lp/oci/browser/tests/test_ocirecipe.py
@@ -3,16 +3,13 @@
 
 """Test OCI recipe views."""
 
-from datetime import (
-    datetime,
-    timedelta,
-    )
+from datetime import datetime, timedelta
 from operator import attrgetter
 from urllib.parse import quote
 
-from fixtures import FakeLogger
 import pytz
 import soupmatchers
+from fixtures import FakeLogger
 from storm.locals import Store
 from testtools.matchers import (
     Equals,
@@ -21,7 +18,7 @@ from testtools.matchers import (
     MatchesSetwise,
     MatchesStructure,
     Not,
-    )
+)
 from zope.component import getUtility
 from zope.publisher.interfaces import NotFound
 from zope.security.interfaces import Unauthorized
@@ -38,20 +35,18 @@ from lp.oci.browser.ocirecipe import (
     OCIRecipeAdminView,
     OCIRecipeEditView,
     OCIRecipeView,
-    )
+)
 from lp.oci.interfaces.ocipushrule import (
     IOCIPushRuleSet,
     OCIPushRuleAlreadyExists,
-    )
+)
 from lp.oci.interfaces.ocirecipe import (
+    OCI_RECIPE_ALLOW_CREATE,
     CannotModifyOCIRecipeProcessor,
     IOCIRecipeSet,
-    OCI_RECIPE_ALLOW_CREATE,
-    )
+)
 from lp.oci.interfaces.ocirecipejob import IOCIRecipeRequestBuildsJobSource
-from lp.oci.interfaces.ociregistrycredentials import (
-    IOCIRegistryCredentialsSet,
-    )
+from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentialsSet
 from lp.oci.tests.helpers import OCIConfigHelperMixin
 from lp.registry.enums import BranchSharingPolicy
 from lp.registry.interfaces.person import IPersonSet
@@ -64,36 +59,27 @@ from lp.services.propertycache import get_property_cache
 from lp.services.webapp import canonical_url
 from lp.services.webapp.servers import LaunchpadTestRequest
 from lp.testing import (
+    BrowserTestCase,
+    TestCaseWithFactory,
     admin_logged_in,
     anonymous_logged_in,
-    BrowserTestCase,
     login,
     login_person,
     person_logged_in,
     record_two_runs,
-    TestCaseWithFactory,
     time_counter,
-    )
+)
 from lp.testing.dbuser import dbuser
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadFunctionalLayer,
-    )
-from lp.testing.matchers import (
-    MatchesPickerText,
-    MatchesTagText,
-    )
+from lp.testing.layers import DatabaseFunctionalLayer, LaunchpadFunctionalLayer
+from lp.testing.matchers import MatchesPickerText, MatchesTagText
 from lp.testing.pages import (
     extract_text,
     find_main_content,
     find_tag_by_id,
     find_tags_by_class,
-    )
+)
 from lp.testing.publication import test_traverse
-from lp.testing.views import (
-    create_initialized_view,
-    create_view,
-    )
+from lp.testing.views import create_initialized_view, create_view
 
 
 class TestOCIRecipeNavigation(TestCaseWithFactory):
@@ -102,19 +88,25 @@ class TestOCIRecipeNavigation(TestCaseWithFactory):
 
     def setUp(self):
         super().setUp()
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_canonical_url(self):
         owner = self.factory.makePerson(name="person")
         distribution = self.factory.makeDistribution(name="distro")
         oci_project = self.factory.makeOCIProject(
-            pillar=distribution, ociprojectname="oci-project")
+            pillar=distribution, ociprojectname="oci-project"
+        )
         recipe = self.factory.makeOCIRecipe(
-            name="recipe", registrant=owner, owner=owner,
-            oci_project=oci_project)
+            name="recipe",
+            registrant=owner,
+            owner=owner,
+            oci_project=oci_project,
+        )
         self.assertEqual(
             "http://launchpad.test/~person/distro/+oci/oci-project/";
-            "+recipe/recipe", canonical_url(recipe))
+            "+recipe/recipe",
+            canonical_url(recipe),
+        )
 
     def test_recipe_traverse_distribution(self):
         # Make sure we can reach recipe of distro-based OCI projects.
@@ -122,9 +114,14 @@ class TestOCIRecipeNavigation(TestCaseWithFactory):
         oci_project = self.factory.makeOCIProject(pillar=distro)
         recipe = self.factory.makeOCIRecipe(oci_project=oci_project)
         obj, _, _ = test_traverse(
-            "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s"; % (
-                recipe.owner.name, recipe.oci_project.pillar.name,
-                recipe.oci_project.name, recipe.name))
+            "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s";
+            % (
+                recipe.owner.name,
+                recipe.oci_project.pillar.name,
+                recipe.oci_project.name,
+                recipe.name,
+            )
+        )
         self.assertEqual(recipe, obj)
 
     def test_recipe_traverse_project(self):
@@ -133,9 +130,14 @@ class TestOCIRecipeNavigation(TestCaseWithFactory):
         oci_project = self.factory.makeOCIProject(pillar=project)
         recipe = self.factory.makeOCIRecipe(oci_project=oci_project)
         obj, _, _ = test_traverse(
-            "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s"; % (
-                recipe.owner.name, recipe.oci_project.pillar.name,
-                recipe.oci_project.name, recipe.name))
+            "http://launchpad.test/~%s/%s/+oci/%s/+recipe/%s";
+            % (
+                recipe.owner.name,
+                recipe.oci_project.pillar.name,
+                recipe.oci_project.name,
+                recipe.name,
+            )
+        )
         self.assertEqual(recipe, obj)
 
 
@@ -147,20 +149,24 @@ class BaseTestOCIRecipeView(BrowserTestCase):
         super().setUp()
         self.useFixture(FakeLogger())
         self.person = self.factory.makePerson(
-            name="test-person", displayname="Test Person")
+            name="test-person", displayname="Test Person"
+        )
 
 
 class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
-
     def setUp(self):
         super().setUp()
         self.distroseries = self.factory.makeDistroSeries()
         self.distribution = self.distroseries.distribution
-        self.useFixture(FeatureFixture({
-            OCI_RECIPE_ALLOW_CREATE: "on",
-            "oci.build_series.%s" % self.distribution.name:
-                self.distroseries.name,
-            }))
+        self.useFixture(
+            FeatureFixture(
+                {
+                    OCI_RECIPE_ALLOW_CREATE: "on",
+                    "oci.build_series.%s"
+                    % self.distribution.name: self.distroseries.name,
+                }
+            )
+        )
         self.setConfig()
 
     def setUpDistroSeries(self):
@@ -169,138 +175,166 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         for name in processor_names:
             processor = getUtility(IProcessorSet).getByName(name)
             self.factory.makeDistroArchSeries(
-                distroseries=self.distroseries, architecturetag=name,
-                processor=processor)
+                distroseries=self.distroseries,
+                architecturetag=name,
+                processor=processor,
+            )
 
     def assertProcessorControls(self, processors_control, enabled, disabled):
         matchers = [
             MatchesStructure.byEquality(optionValue=name, disabled=False)
-            for name in enabled]
-        matchers.extend([
-            MatchesStructure.byEquality(optionValue=name, disabled=True)
-            for name in disabled])
+            for name in enabled
+        ]
+        matchers.extend(
+            [
+                MatchesStructure.byEquality(optionValue=name, disabled=True)
+                for name in disabled
+            ]
+        )
         self.assertThat(processors_control.controls, MatchesSetwise(*matchers))
 
     def test_create_new_recipe_not_logged_in(self):
         oci_project = self.factory.makeOCIProject()
         self.assertRaises(
-            Unauthorized, self.getViewBrowser, oci_project,
-            view_name="+new-recipe", no_login=True)
+            Unauthorized,
+            self.getViewBrowser,
+            oci_project,
+            view_name="+new-recipe",
+            no_login=True,
+        )
 
     def test_create_new_recipe(self):
         oci_project = self.factory.makeOCIProject()
         oci_project_display = oci_project.display_name
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
         source_display = git_ref.display_name
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
         browser.getControl("Create OCI recipe").click()
 
         content = find_main_content(browser.contents)
         self.assertEqual("recipe-name", extract_text(content.h1))
         self.assertThat(
-            "Recipe description",
-            MatchesTagText(content, "recipe-description"))
+            "Recipe description", MatchesTagText(content, "recipe-description")
+        )
         self.assertThat(
-            "Test Person", MatchesPickerText(content, "edit-owner"))
+            "Test Person", MatchesPickerText(content, "edit-owner")
+        )
         self.assertThat(
             "OCI project:\n%s" % oci_project_display,
-            MatchesTagText(content, "oci-project"))
+            MatchesTagText(content, "oci-project"),
+        )
         self.assertThat(
             "Source:\n%s\nEdit OCI recipe" % source_display,
-            MatchesTagText(content, "source"))
+            MatchesTagText(content, "source"),
+        )
         self.assertThat(
             "Build file path:\nDockerfile\n"
             "Edit OCI recipe\n"
             "Build context directory:\n.\n"
             "Edit OCI recipe",
-            MatchesTagText(content, "build-file"))
+            MatchesTagText(content, "build-file"),
+        )
         self.assertThat(
             "Build schedule:\nBuilt on request\nEdit OCI recipe\n",
-            MatchesTagText(content, "build-schedule"))
+            MatchesTagText(content, "build-schedule"),
+        )
         self.assertThat(
-            "Official recipe:\nNo",
-            MatchesTagText(content, "official-recipe"))
+            "Official recipe:\nNo", MatchesTagText(content, "official-recipe")
+        )
 
     def test_create_new_available_information_types(self):
         public_pillar = self.factory.makeProduct(owner=self.person)
         private_pillar = self.factory.makeProduct(
             owner=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         public_oci_project = self.factory.makeOCIProject(
-            registrant=self.person, pillar=public_pillar)
+            registrant=self.person, pillar=public_pillar
+        )
         private_oci_project = self.factory.makeOCIProject(
-            registrant=self.person, pillar=private_pillar)
+            registrant=self.person, pillar=private_pillar
+        )
 
         # Public pillar.
         browser = self.getViewBrowser(
-            public_oci_project, view_name="+new-recipe", user=self.person)
+            public_oci_project, view_name="+new-recipe", user=self.person
+        )
         self.assertContentEqual(
-            ['PUBLIC', 'PUBLICSECURITY', 'PRIVATESECURITY', 'USERDATA'],
-            browser.getControl(name="field.information_type").options)
+            ["PUBLIC", "PUBLICSECURITY", "PRIVATESECURITY", "USERDATA"],
+            browser.getControl(name="field.information_type").options,
+        )
 
         # Proprietary pillar.
         browser = self.getViewBrowser(
-            private_oci_project, view_name="+new-recipe", user=self.person)
+            private_oci_project, view_name="+new-recipe", user=self.person
+        )
         self.assertContentEqual(
-            ['PROPRIETARY'],
-            browser.getControl(name="field.information_type").options)
+            ["PROPRIETARY"],
+            browser.getControl(name="field.information_type").options,
+        )
 
     def test_create_new_recipe_invalid_format(self):
         oci_project = self.factory.makeOCIProject()
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/invalid'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/invalid"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
         browser.getControl("Create OCI recipe").click()
         self.assertIn("Branch does not match format", browser.contents)
 
     def test_create_new_recipe_with_build_args(self):
         oci_project = self.factory.makeOCIProject()
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
-        browser.getControl("Build-time ARG variables").value = (
-            "VAR1=10\nVAR2=20")
+        browser.getControl(
+            "Build-time ARG variables"
+        ).value = "VAR1=10\nVAR2=20"
         browser.getControl("Create OCI recipe").click()
 
         content = find_main_content(browser.contents)
         self.assertEqual("recipe-name", extract_text(content.h1))
         self.assertThat(
             "Build-time\nARG variables:\nVAR1=10\nVAR2=20",
-            MatchesTagText(content, "build-args"))
+            MatchesTagText(content, "build-args"),
+        )
 
     def test_create_new_recipe_with_image_name(self):
         oci_project = self.factory.makeOCIProject()
         credentials = self.factory.makeOCIRegistryCredentials()
         with person_logged_in(oci_project.distribution.owner):
             oci_project.distribution.oci_registry_credentials = credentials
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
 
         image_name = self.factory.getUniqueUnicode()
@@ -309,29 +343,35 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         content = find_main_content(browser.contents)
         self.assertThat(
             "Registry image name:\n{}".format(image_name),
-            MatchesTagText(content, "image-name"))
+            MatchesTagText(content, "image-name"),
+        )
 
     def test_create_new_recipe_users_teams_as_owner_options(self):
         # Teams that the user is in are options for the OCI recipe owner.
         self.factory.makeTeam(
-            name="test-team", displayname="Test Team", members=[self.person])
+            name="test-team", displayname="Test Team", members=[self.person]
+        )
         oci_project = self.factory.makeOCIProject()
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         options = browser.getControl("Owner").displayOptions
         self.assertEqual(
             ["Test Person (test-person)", "Test Team (test-team)"],
-            sorted(str(option) for option in options))
+            sorted(str(option) for option in options),
+        )
 
     def test_create_new_recipe_display_processors(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(
             ["Intel 386 (386)", "AMD 64bit (amd64)", "HPPA Processor (hppa)"],
-            [extract_text(option) for option in processors.displayOptions])
+            [extract_text(option) for option in processors.displayOptions],
+        )
         self.assertContentEqual(["386", "amd64", "hppa"], processors.options)
         self.assertContentEqual(["386", "amd64", "hppa"], processors.value)
 
@@ -341,35 +381,43 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         proc_armhf = self.factory.makeProcessor(
-            name="armhf", restricted=True, build_by_default=False)
+            name="armhf", restricted=True, build_by_default=False
+        )
         self.factory.makeDistroArchSeries(
-            distroseries=self.distroseries, architecturetag="armhf",
-            processor=proc_armhf)
+            distroseries=self.distroseries,
+            architecturetag="armhf",
+            processor=proc_armhf,
+        )
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         processors = browser.getControl(name="field.processors")
         self.assertProcessorControls(
-            processors, ["386", "amd64", "hppa"], ["armhf"])
+            processors, ["386", "amd64", "hppa"], ["armhf"]
+        )
 
     def test_create_new_recipe_processors(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         processors = browser.getControl(name="field.processors")
         processors.value = ["386", "amd64"]
         browser.getControl(name="field.name").value = "recipe-name"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
         browser.getControl("Create OCI recipe").click()
         login_person(self.person)
         recipe = getUtility(IOCIRecipeSet).getByName(
-            self.person, oci_project, "recipe-name")
+            self.person, oci_project, "recipe-name"
+        )
         self.assertContentEqual(
-            ["386", "amd64"], [proc.name for proc in recipe.processors])
+            ["386", "amd64"], [proc.name for proc in recipe.processors]
+        )
 
     def test_create_new_recipe_no_default_repo_warning(self):
         self.setUpDistroSeries()
@@ -377,12 +425,14 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         with admin_logged_in():
             oci_project_url = canonical_url(oci_project)
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         error_message = (
-            'The default git repository for this OCI project was not created '
-            'yet.<br/>'
+            "The default git repository for this OCI project was not created "
+            "yet.<br/>"
             'Check the <a href="{url}">OCI project page</a> for instructions '
-            'on how to create one.').format(url=oci_project_url)
+            "on how to create one."
+        ).format(url=oci_project_url)
         self.assertIn(error_message, browser.contents)
 
     def test_create_new_recipe_with_default_repo_already_created(self):
@@ -390,52 +440,73 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         repository = self.factory.makeGitRepository(
             name=oci_project.name,
-            target=oci_project, owner=self.person, registrant=self.person)
+            target=oci_project,
+            owner=self.person,
+            registrant=self.person,
+        )
         with person_logged_in(self.distribution.owner):
             getUtility(IGitRepositorySet).setDefaultRepository(
-                oci_project, repository)
+                oci_project, repository
+            )
             default_repo_path = "%s/+oci/%s" % (
-                self.distribution.name, oci_project.name)
+                self.distribution.name,
+                oci_project.name,
+            )
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         error_message = (
-            'The default git repository for this OCI project was not created '
-            'yet.')
+            "The default git repository for this OCI project was not created "
+            "yet."
+        )
         self.assertNotIn(error_message, browser.contents)
-        self.assertThat(browser.contents, soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                'Repository pre-filled', 'input', attrs={
-                    "id": "field.git_ref.repository",
-                    "value": default_repo_path})))
+        self.assertThat(
+            browser.contents,
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "Repository pre-filled",
+                    "input",
+                    attrs={
+                        "id": "field.git_ref.repository",
+                        "value": default_repo_path,
+                    },
+                )
+            ),
+        )
 
     def test_official_is_disabled(self):
         oci_project = self.factory.makeOCIProject()
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         official_control = browser.getControl("Official recipe")
         self.assertTrue(official_control.disabled)
 
     def test_official_is_enabled(self):
         distribution = self.factory.makeDistribution(
-            oci_project_admin=self.person)
+            oci_project_admin=self.person
+        )
         oci_project = self.factory.makeOCIProject(pillar=distribution)
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         official_control = browser.getControl("Official recipe")
         self.assertFalse(official_control.disabled)
 
     def test_set_official(self):
         distribution = self.factory.makeDistribution(
-            oci_project_admin=self.person)
+            oci_project_admin=self.person
+        )
         oci_project = self.factory.makeOCIProject(pillar=distribution)
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
         official_control = browser.getControl("Official recipe")
         official_control.selected = True
@@ -443,28 +514,29 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
         content = find_main_content(browser.contents)
         self.assertThat(
-            "Official recipe:\nYes",
-            MatchesTagText(content, "official-recipe"))
+            "Official recipe:\nYes", MatchesTagText(content, "official-recipe")
+        )
 
     def test_set_official_multiple(self):
         distribution = self.factory.makeDistribution(
-            oci_project_admin=self.person)
+            oci_project_admin=self.person
+        )
 
         # do it once
         oci_project = self.factory.makeOCIProject(pillar=distribution)
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
 
         # and then do it again
         oci_project2 = self.factory.makeOCIProject(pillar=distribution)
-        [git_ref2] = self.factory.makeGitRefs(
-            paths=['refs/heads/v3.0-20.04'])
+        [git_ref2] = self.factory.makeGitRefs(paths=["refs/heads/v3.0-20.04"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
         official_control = browser.getControl("Official recipe")
         official_control.selected = True
@@ -472,15 +544,17 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
         content = find_main_content(browser.contents)
         self.assertThat(
-            "Official recipe:\nYes",
-            MatchesTagText(content, "official-recipe"))
+            "Official recipe:\nYes", MatchesTagText(content, "official-recipe")
+        )
 
         browser2 = self.getViewBrowser(
-            oci_project2, view_name="+new-recipe", user=self.person)
+            oci_project2, view_name="+new-recipe", user=self.person
+        )
         browser2.getControl(name="field.name").value = "recipe-name"
         browser2.getControl("Description").value = "Recipe description"
-        browser2.getControl(name="field.git_ref.repository").value = (
-            git_ref2.repository.identity)
+        browser2.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref2.repository.identity
         browser2.getControl(name="field.git_ref.path").value = git_ref2.path
         official_control = browser2.getControl("Official recipe")
         official_control.selected = True
@@ -488,28 +562,30 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
         content = find_main_content(browser2.contents)
         self.assertThat(
-            "Official recipe:\nYes",
-            MatchesTagText(content, "official-recipe"))
+            "Official recipe:\nYes", MatchesTagText(content, "official-recipe")
+        )
 
         browser.reload()
         content = find_main_content(browser.contents)
         self.assertThat(
-            "Official recipe:\nYes",
-            MatchesTagText(content, "official-recipe"))
+            "Official recipe:\nYes", MatchesTagText(content, "official-recipe")
+        )
 
     def test_set_official_no_permissions(self):
         distro_owner = self.factory.makePerson()
         distribution = self.factory.makeDistribution(
-            oci_project_admin=distro_owner)
+            oci_project_admin=distro_owner
+        )
         oci_project = self.factory.makeOCIProject(pillar=distribution)
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = git_ref.path
         official_control = browser.getControl("Official recipe")
         official_control.selected = True
@@ -517,19 +593,21 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
         error_message = (
             "You do not have permission to set the official status "
-            "of this recipe.")
+            "of this recipe."
+        )
         self.assertIn(error_message, browser.contents)
 
     def test_create_recipe_doesnt_override_gitref_errors(self):
         oci_project = self.factory.makeOCIProject()
-        [git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v2.0-20.04"])
         browser = self.getViewBrowser(
-            oci_project, view_name="+new-recipe", user=self.person)
+            oci_project, view_name="+new-recipe", user=self.person
+        )
         browser.getControl(name="field.name").value = "recipe-name"
         browser.getControl("Description").value = "Recipe description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = "non-exist"
         browser.getControl("Create OCI recipe").click()
 
@@ -538,10 +616,9 @@ class TestOCIRecipeAddView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
 
 class TestOCIRecipeAdminView(BaseTestOCIRecipeView):
-
     def setUp(self):
         super().setUp()
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_unauthorized(self):
         # A non-admin user cannot administer an OCI recipe.
@@ -550,16 +627,21 @@ class TestOCIRecipeAdminView(BaseTestOCIRecipeView):
         recipe_url = canonical_url(recipe)
         browser = self.getViewBrowser(recipe, user=self.person)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Administer OCI recipe")
+            LinkNotFoundError, browser.getLink, "Administer OCI recipe"
+        )
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, recipe_url + "/+admin",
-            user=self.person)
+            Unauthorized,
+            self.getUserBrowser,
+            recipe_url + "/+admin",
+            user=self.person,
+        )
 
     def test_admin_recipe(self):
         # Admins can change require_virtualized.
         login("admin@xxxxxxxxxxxxx")
         commercial_admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).commercial_admin])
+            member_of=[getUtility(ILaunchpadCelebrities).commercial_admin]
+        )
         login_person(self.person)
         recipe = self.factory.makeOCIRecipe(registrant=self.person)
         self.assertTrue(recipe.require_virtualized)
@@ -579,30 +661,36 @@ class TestOCIRecipeAdminView(BaseTestOCIRecipeView):
         # Administering an OCI recipe sets the date_last_modified property.
         login("admin@xxxxxxxxxxxxx")
         ppa_admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).ppa_admin])
+            member_of=[getUtility(ILaunchpadCelebrities).ppa_admin]
+        )
         login_person(self.person)
         date_created = datetime(2000, 1, 1, tzinfo=pytz.UTC)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, date_created=date_created)
+            registrant=self.person, date_created=date_created
+        )
         login_person(ppa_admin)
         view = OCIRecipeAdminView(recipe, LaunchpadTestRequest())
         view.initialize()
         view.request_action.success({"require_virtualized": False})
         self.assertSqlAttributeEqualsDate(
-            recipe, "date_last_modified", UTC_NOW)
+            recipe, "date_last_modified", UTC_NOW
+        )
 
 
 class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
-
     def setUp(self):
         super().setUp()
         self.distroseries = self.factory.makeDistroSeries()
         self.distribution = self.distroseries.distribution
-        self.useFixture(FeatureFixture({
-            OCI_RECIPE_ALLOW_CREATE: "on",
-            "oci.build_series.%s" % self.distribution.name:
-                self.distroseries.name,
-            }))
+        self.useFixture(
+            FeatureFixture(
+                {
+                    OCI_RECIPE_ALLOW_CREATE: "on",
+                    "oci.build_series.%s"
+                    % self.distribution.name: self.distroseries.name,
+                }
+            )
+        )
         self.setConfig()
 
     def setUpDistroSeries(self):
@@ -611,34 +699,45 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         for name in processor_names:
             processor = getUtility(IProcessorSet).getByName(name)
             self.factory.makeDistroArchSeries(
-                distroseries=self.distroseries, architecturetag=name,
-                processor=processor)
+                distroseries=self.distroseries,
+                architecturetag=name,
+                processor=processor,
+            )
 
     def assertRecipeProcessors(self, recipe, names):
         self.assertContentEqual(
-            names, [processor.name for processor in recipe.processors])
+            names, [processor.name for processor in recipe.processors]
+        )
 
     def assertProcessorControls(self, processors_control, enabled, disabled):
         matchers = [
             MatchesStructure.byEquality(optionValue=name, disabled=False)
-            for name in enabled]
-        matchers.extend([
-            MatchesStructure.byEquality(optionValue=name, disabled=True)
-            for name in disabled])
+            for name in enabled
+        ]
+        matchers.extend(
+            [
+                MatchesStructure.byEquality(optionValue=name, disabled=True)
+                for name in disabled
+            ]
+        )
         self.assertThat(processors_control.controls, MatchesSetwise(*matchers))
 
     def assertShowsPrivateBanner(self, browser):
         banners = find_tags_by_class(
-            browser.contents, "private_banner_container")
+            browser.contents, "private_banner_container"
+        )
         self.assertEqual(1, len(banners))
         self.assertEqual(
-            'The information on this page is private.',
-            extract_text(banners[0]))
+            "The information on this page is private.",
+            extract_text(banners[0]),
+        )
 
     def test_edit_private_recipe_shows_banner(self):
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            information_type=InformationType.USERDATA)
+            registrant=self.person,
+            owner=self.person,
+            information_type=InformationType.USERDATA,
+        )
         browser = self.getViewBrowser(recipe, user=self.person)
         browser.getLink("Edit OCI recipe").click()
         self.assertShowsPrivateBanner(browser)
@@ -647,14 +746,20 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         oci_project = self.factory.makeOCIProject()
         oci_project_display = oci_project.display_name
         [old_git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v1.0-20.04'])
+            paths=["refs/heads/v1.0-20.04"]
+        )
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project, git_ref=old_git_ref)
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            git_ref=old_git_ref,
+        )
         self.factory.makeTeam(
-            name="new-team", displayname="New Team", members=[self.person])
+            name="new-team", displayname="New Team", members=[self.person]
+        )
         [new_git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v2.0-20.04'])
+            paths=["refs/heads/v2.0-20.04"]
+        )
         self.factory.makeOCIPushRule(recipe=recipe)
 
         browser = self.getViewBrowser(recipe, user=self.person)
@@ -662,8 +767,9 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         browser.getControl("Owner").value = ["new-team"]
         browser.getControl(name="field.name").value = "new-name"
         browser.getControl("Description").value = "New description"
-        browser.getControl(name="field.git_ref.repository").value = (
-            new_git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = new_git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = new_git_ref.path
         browser.getControl("Build file path").value = "Dockerfile-2"
         browser.getControl("Build directory context").value = "apath"
@@ -675,32 +781,42 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         self.assertThat("New Team", MatchesPickerText(content, "edit-owner"))
         self.assertThat(
             "OCI project:\n%s" % oci_project_display,
-            MatchesTagText(content, "oci-project"))
+            MatchesTagText(content, "oci-project"),
+        )
         self.assertThat(
             "Source:\n%s\nEdit OCI recipe" % new_git_ref.display_name,
-            MatchesTagText(content, "source"))
+            MatchesTagText(content, "source"),
+        )
         self.assertThat(
             "Build file path:\nDockerfile-2\n"
             "Edit OCI recipe\n"
             "Build context directory:\napath\n"
             "Edit OCI recipe",
-            MatchesTagText(content, "build-file"))
+            MatchesTagText(content, "build-file"),
+        )
         self.assertThat(
             "Build schedule:\nBuilt daily\nEdit OCI recipe\n",
-            MatchesTagText(content, "build-schedule"))
+            MatchesTagText(content, "build-schedule"),
+        )
 
     def test_edit_recipe_invalid_branch(self):
         oci_project = self.factory.makeOCIProject()
         repository = self.factory.makeGitRepository()
         [old_git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v1.0-20.04'], repository=repository)
+            paths=["refs/heads/v1.0-20.04"], repository=repository
+        )
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project, git_ref=old_git_ref)
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            git_ref=old_git_ref,
+        )
         self.factory.makeTeam(
-            name="new-team", displayname="New Team", members=[self.person])
+            name="new-team", displayname="New Team", members=[self.person]
+        )
         [new_git_ref] = self.factory.makeGitRefs(
-            repository=repository, paths=['refs/heads/invalid'])
+            repository=repository, paths=["refs/heads/invalid"]
+        )
         self.factory.makeOCIPushRule(recipe=recipe)
 
         browser = self.getViewBrowser(recipe, user=self.person)
@@ -713,25 +829,36 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         pillar = self.factory.makeProduct(
             owner=self.person,
             information_type=InformationType.PUBLIC,
-            branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY,
+        )
         oci_project = self.factory.makeOCIProject(
-            registrant=self.person, pillar=pillar)
+            registrant=self.person, pillar=pillar
+        )
         [git_ref] = self.factory.makeGitRefs(
-            owner=self.person,
-            paths=['refs/heads/v2.0-20.04'])
+            owner=self.person, paths=["refs/heads/v2.0-20.04"]
+        )
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project, git_ref=git_ref,
-            information_type=InformationType.PUBLIC)
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            git_ref=git_ref,
+            information_type=InformationType.PUBLIC,
+        )
 
-        browser = self.getViewBrowser(recipe, '+edit', user=self.person)
+        browser = self.getViewBrowser(recipe, "+edit", user=self.person)
 
         # Make sure we are showing all available information types:
         info_type_field = browser.getControl(name="field.information_type")
-        self.assertContentEqual([
-            'PUBLIC', 'PUBLICSECURITY', 'PRIVATESECURITY', 'USERDATA',
-            'PROPRIETARY'],
-            info_type_field.options)
+        self.assertContentEqual(
+            [
+                "PUBLIC",
+                "PUBLICSECURITY",
+                "PRIVATESECURITY",
+                "USERDATA",
+                "PROPRIETARY",
+            ],
+            info_type_field.options,
+        )
 
         info_type_field.value = InformationType.PROPRIETARY.name
         browser.getControl("Update OCI recipe").click()
@@ -739,39 +866,51 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
     def test_edit_recipe_on_public_pillar_information_types(self):
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person)
-        browser = self.getViewBrowser(recipe, '+edit', user=self.person)
+            registrant=self.person, owner=self.person
+        )
+        browser = self.getViewBrowser(recipe, "+edit", user=self.person)
 
         info_type_field = browser.getControl(name="field.information_type")
         self.assertContentEqual(
-            ['PUBLIC', 'PUBLICSECURITY', 'PRIVATESECURITY', 'USERDATA'],
-            info_type_field.options)
+            ["PUBLIC", "PUBLICSECURITY", "PRIVATESECURITY", "USERDATA"],
+            info_type_field.options,
+        )
 
     def test_edit_recipe_sets_date_last_modified(self):
         # Editing an OCI recipe sets the date_last_modified property.
         date_created = datetime(2000, 1, 1, tzinfo=pytz.UTC)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, date_created=date_created)
+            registrant=self.person, date_created=date_created
+        )
         with person_logged_in(self.person):
             view = OCIRecipeEditView(recipe, LaunchpadTestRequest())
             view.initialize()
-            view.request_action.success({
-                "owner": recipe.owner,
-                "name": "changed",
-                "description": "changed",
-                })
+            view.request_action.success(
+                {
+                    "owner": recipe.owner,
+                    "name": "changed",
+                    "description": "changed",
+                }
+            )
         self.assertSqlAttributeEqualsDate(
-            recipe, "date_last_modified", UTC_NOW)
+            recipe, "date_last_modified", UTC_NOW
+        )
 
     def test_edit_recipe_already_exists(self):
         oci_project = self.factory.makeOCIProject()
         oci_project_display = oci_project.display_name
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project, name="one")
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            name="one",
+        )
         self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project, name="two")
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            name="two",
+        )
         browser = self.getViewBrowser(recipe, user=self.person)
         browser.getLink("Edit OCI recipe").click()
         browser.getControl(name="field.name").value = "two"
@@ -779,29 +918,35 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         self.assertEqual(
             "There is already an OCI recipe owned by Test Person in %s with "
             "this name." % oci_project_display,
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_display_processors(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
         browser = self.getViewBrowser(
-            recipe, view_name="+edit", user=recipe.owner)
+            recipe, view_name="+edit", user=recipe.owner
+        )
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(
             ["Intel 386 (386)", "AMD 64bit (amd64)", "HPPA Processor (hppa)"],
-            [extract_text(option) for option in processors.displayOptions])
+            [extract_text(option) for option in processors.displayOptions],
+        )
         self.assertContentEqual(["386", "amd64", "hppa"], processors.options)
 
     def test_edit_processors(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
         self.assertRecipeProcessors(recipe, ["386", "amd64", "hppa"])
         browser = self.getViewBrowser(
-            recipe, view_name="+edit", user=recipe.owner)
+            recipe, view_name="+edit", user=recipe.owner
+        )
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(["386", "amd64", "hppa"], processors.value)
         processors.value = ["386", "amd64"]
@@ -813,10 +958,14 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project, build_args={"VAR1": "xxx", "VAR2": "uu"})
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            build_args={"VAR1": "xxx", "VAR2": "uu"},
+        )
         browser = self.getViewBrowser(
-            recipe, view_name="+edit", user=recipe.owner)
+            recipe, view_name="+edit", user=recipe.owner
+        )
         args = browser.getControl(name="field.build_args")
         self.assertContentEqual("VAR1=xxx\r\nVAR2=uu", args.value)
         args.value = "VAR=aa\nANOTHER_VAR=bbb"
@@ -824,16 +973,21 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         login_person(self.person)
         IStore(recipe).reload(recipe)
         self.assertEqual(
-            {"VAR": "aa", "ANOTHER_VAR": "bbb"}, recipe.build_args)
+            {"VAR": "aa", "ANOTHER_VAR": "bbb"}, recipe.build_args
+        )
 
     def test_edit_build_args_invalid_content(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project, build_args={"VAR1": "xxx", "VAR2": "uu"})
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            build_args={"VAR1": "xxx", "VAR2": "uu"},
+        )
         browser = self.getViewBrowser(
-            recipe, view_name="+edit", user=recipe.owner)
+            recipe, view_name="+edit", user=recipe.owner
+        )
         args = browser.getControl(name="field.build_args")
         self.assertContentEqual("VAR1=xxx\r\nVAR2=uu", args.value)
         args.value = "VAR=aa\nmessed up text"
@@ -843,7 +997,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         content = find_main_content(browser.contents)
         self.assertIn(
             "'messed up text' at line 2 is not a valid KEY=value pair.",
-            extract_text(content))
+            extract_text(content),
+        )
 
         # Assert that recipe still have the original build_args.
         login_person(self.person)
@@ -859,11 +1014,14 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
             oci_project = self.factory.makeOCIProject(pillar=self.distribution)
             recipe = self.factory.makeOCIRecipe(
                 name=original_name,
-                registrant=self.person, owner=self.person,
-                oci_project=oci_project)
+                registrant=self.person,
+                owner=self.person,
+                oci_project=oci_project,
+            )
             oci_project.setOfficialRecipeStatus(recipe, True)
         browser = self.getViewBrowser(
-            recipe, view_name="+edit", user=recipe.owner)
+            recipe, view_name="+edit", user=recipe.owner
+        )
         image_name = self.factory.getUniqueUnicode()
         field = browser.getControl(name="field.image_name")
         # Default is the recipe name
@@ -873,7 +1031,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         content = find_main_content(browser.contents)
         self.assertThat(
             "Registry image name:\n{}".format(image_name),
-            MatchesTagText(content, "image-name"))
+            MatchesTagText(content, "image-name"),
+        )
 
     def test_edit_with_invisible_processor(self):
         # It's possible for existing recipes to have an enabled processor
@@ -885,14 +1044,17 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         proc_386 = getUtility(IProcessorSet).getByName("386")
         proc_amd64 = getUtility(IProcessorSet).getByName("amd64")
         proc_armel = self.factory.makeProcessor(
-            name="armel", restricted=True, build_by_default=False)
+            name="armel", restricted=True, build_by_default=False
+        )
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
         recipe.setProcessors([proc_386, proc_amd64, proc_armel])
         browser = self.getViewBrowser(
-            recipe, view_name="+edit", user=recipe.owner)
+            recipe, view_name="+edit", user=recipe.owner
+        )
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(["386", "amd64"], processors.value)
         processors.value = ["amd64"]
@@ -905,20 +1067,26 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         # checkbox in the UI, and the processor cannot be enabled.
         self.setUpDistroSeries()
         proc_armhf = self.factory.makeProcessor(
-            name="armhf", restricted=True, build_by_default=False)
+            name="armhf", restricted=True, build_by_default=False
+        )
         self.factory.makeDistroArchSeries(
-            distroseries=self.distroseries, architecturetag="armhf",
-            processor=proc_armhf)
+            distroseries=self.distroseries,
+            architecturetag="armhf",
+            processor=proc_armhf,
+        )
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
         self.assertRecipeProcessors(recipe, ["386", "amd64", "hppa"])
         browser = self.getViewBrowser(
-            recipe, view_name="+edit", user=recipe.owner)
+            recipe, view_name="+edit", user=recipe.owner
+        )
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(["386", "amd64", "hppa"], processors.value)
         self.assertProcessorControls(
-            processors, ["386", "amd64", "hppa"], ["armhf"])
+            processors, ["386", "amd64", "hppa"], ["armhf"]
+        )
         # Even if the user works around the disabled checkbox and forcibly
         # enables it, they can't enable the restricted processor.
         for control in processors.controls:
@@ -927,7 +1095,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         processors.value = ["386", "amd64", "armhf"]
         self.assertRaises(
             CannotModifyOCIRecipeProcessor,
-            browser.getControl("Update OCI recipe").click)
+            browser.getControl("Update OCI recipe").click,
+        )
 
     def test_edit_processors_restricted_already_enabled(self):
         # A restricted processor that is already enabled is shown with a
@@ -938,23 +1107,29 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
         proc_386 = getUtility(IProcessorSet).getByName("386")
         proc_amd64 = getUtility(IProcessorSet).getByName("amd64")
         proc_armhf = self.factory.makeProcessor(
-            name="armhf", restricted=True, build_by_default=False)
+            name="armhf", restricted=True, build_by_default=False
+        )
         self.setUpDistroSeries()
         self.factory.makeDistroArchSeries(
-            distroseries=self.distroseries, architecturetag="armhf",
-            processor=proc_armhf)
+            distroseries=self.distroseries,
+            architecturetag="armhf",
+            processor=proc_armhf,
+        )
         oci_project = self.factory.makeOCIProject(pillar=self.distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
         recipe.setProcessors([proc_386, proc_amd64, proc_armhf])
         self.assertRecipeProcessors(recipe, ["386", "amd64", "armhf"])
         browser = self.getUserBrowser(
-            canonical_url(recipe) + "/+edit", user=recipe.owner)
+            canonical_url(recipe) + "/+edit", user=recipe.owner
+        )
         processors = browser.getControl(name="field.processors")
         # armhf is checked but disabled.
         self.assertContentEqual(["386", "amd64", "armhf"], processors.value)
         self.assertProcessorControls(
-            processors, ["386", "amd64", "hppa"], ["armhf"])
+            processors, ["386", "amd64", "hppa"], ["armhf"]
+        )
         processors.value = ["386"]
         browser.getControl("Update OCI recipe").click()
         login_person(self.person)
@@ -963,93 +1138,126 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
     def test_edit_without_default_repo_for_ociproject(self):
         self.setUpDistroSeries()
         repo = self.factory.makeGitRepository(
-            owner=self.person, registrant=self.person)
+            owner=self.person, registrant=self.person
+        )
         [git_ref] = self.factory.makeGitRefs(
-            repository=repo, paths=['refs/heads/v1.0-20.04'])
+            repository=repo, paths=["refs/heads/v1.0-20.04"]
+        )
         oci_project = self.factory.makeOCIProject(
-            registrant=self.person, pillar=self.distribution)
+            registrant=self.person, pillar=self.distribution
+        )
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, oci_project=oci_project, git_ref=git_ref)
+            registrant=self.person, oci_project=oci_project, git_ref=git_ref
+        )
         with person_logged_in(self.person):
             oci_project_url = canonical_url(oci_project)
             browser = self.getViewBrowser(
-                recipe, view_name="+edit", user=self.person)
+                recipe, view_name="+edit", user=self.person
+            )
         error_message = (
             "This recipe's git repository is not in the correct "
             'namespace.<br/>Check the <a href="{url}">OCI project page</a> '
-            "for instructions on how to create it correctly.")
+            "for instructions on how to create it correctly."
+        )
         self.assertIn(
-            error_message.format(url=oci_project_url), browser.contents)
+            error_message.format(url=oci_project_url), browser.contents
+        )
 
     def test_edit_repository_is_not_default_for_ociproject(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(
-            registrant=self.person, pillar=self.distribution)
+            registrant=self.person, pillar=self.distribution
+        )
         [random_git_ref] = self.factory.makeGitRefs(
-            paths=['refs/heads/v1.0-20.04'])
+            paths=["refs/heads/v1.0-20.04"]
+        )
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project,
-            git_ref=random_git_ref)
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            git_ref=random_git_ref,
+        )
 
         # Make the default git repository that should have been used by the
         # recipe.
         default_repo = self.factory.makeGitRepository(
             name=oci_project.name,
-            target=oci_project, owner=self.person, registrant=self.person)
+            target=oci_project,
+            owner=self.person,
+            registrant=self.person,
+        )
         with person_logged_in(self.distribution.owner):
             getUtility(IGitRepositorySet).setDefaultRepository(
-                oci_project, default_repo)
+                oci_project, default_repo
+            )
 
         with person_logged_in(self.person):
-            repo_link = GitRepositoryFormatterAPI(default_repo).link('')
+            repo_link = GitRepositoryFormatterAPI(default_repo).link("")
             browser = self.getViewBrowser(
-                recipe, view_name="+edit", user=self.person)
+                recipe, view_name="+edit", user=self.person
+            )
         error_message = (
             "This recipe's git repository is not in the correct "
-            "namespace.<br/>Consider using {repo} instead.")
-        self.assertIn(
-            error_message.format(repo=repo_link), browser.contents)
+            "namespace.<br/>Consider using {repo} instead."
+        )
+        self.assertIn(error_message.format(repo=repo_link), browser.contents)
 
     def test_edit_repository_in_the_correct_namespace(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(
-            registrant=self.person, pillar=self.distribution)
+            registrant=self.person, pillar=self.distribution
+        )
         default_repo = self.factory.makeGitRepository(
             name=oci_project.name,
-            target=oci_project, owner=self.person, registrant=self.person)
+            target=oci_project,
+            owner=self.person,
+            registrant=self.person,
+        )
 
         [git_ref] = self.factory.makeGitRefs(
-            repository=default_repo, paths=['refs/heads/v1.0-20.04'])
+            repository=default_repo, paths=["refs/heads/v1.0-20.04"]
+        )
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project,
-            git_ref=git_ref)
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            git_ref=git_ref,
+        )
 
         with person_logged_in(self.person):
             browser = self.getViewBrowser(
-                recipe, view_name="+edit", user=self.person)
+                recipe, view_name="+edit", user=self.person
+            )
         self.assertNotIn(
             "This recipe's git repository is not in the correct namespace",
-            browser.contents)
+            browser.contents,
+        )
 
     def test_edit_repository_dont_override_important_msgs(self):
         self.setUpDistroSeries()
         oci_project = self.factory.makeOCIProject(
-            registrant=self.person, pillar=self.distribution)
+            registrant=self.person, pillar=self.distribution
+        )
 
-        [git_ref] = self.factory.makeGitRefs(paths=['refs/heads/v1.0-20.04'])
+        [git_ref] = self.factory.makeGitRefs(paths=["refs/heads/v1.0-20.04"])
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, oci_project=oci_project,
-            git_ref=git_ref)
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+            git_ref=git_ref,
+        )
 
         wrong_namespace_msg = (
-            "This recipe's git repository is not in the correct namespace")
+            "This recipe's git repository is not in the correct namespace"
+        )
         wrong_ref_path_msg = (
-                "The repository at %s does not contain a branch named "
-                "&#x27;non-existing git-ref&#x27;."
-            ) % git_ref.repository.display_name
+            "The repository at %s does not contain a branch named "
+            "&#x27;non-existing git-ref&#x27;."
+        ) % git_ref.repository.display_name
         with person_logged_in(self.person):
             browser = self.getViewBrowser(
-                recipe, view_name="+edit", user=self.person)
+                recipe, view_name="+edit", user=self.person
+            )
             self.assertIn(wrong_namespace_msg, browser.contents)
             args = browser.getControl(name="field.git_ref.path")
             args.value = "non-existing git-ref"
@@ -1062,8 +1270,8 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
     def test_official_is_disabled(self):
         oci_project = self.factory.makeOCIProject()
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
 
         browser = self.getViewBrowser(recipe, user=self.person)
         browser.getLink("Edit OCI recipe").click()
@@ -1072,12 +1280,13 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
     def test_official_is_set_while_disabled(self):
         distribution = self.factory.makeDistribution(
-            oci_project_admin=self.person)
+            oci_project_admin=self.person
+        )
         non_admin = self.factory.makePerson()
         oci_project = self.factory.makeOCIProject(pillar=distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=non_admin, owner=non_admin,
-            oci_project=oci_project)
+            registrant=non_admin, owner=non_admin, oci_project=oci_project
+        )
         with person_logged_in(self.person):
             oci_project.setOfficialRecipeStatus(recipe, True)
         browser = self.getViewBrowser(recipe, user=non_admin)
@@ -1088,11 +1297,12 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
     def test_official_is_enabled(self):
         distribution = self.factory.makeDistribution(
-            oci_project_admin=self.person)
+            oci_project_admin=self.person
+        )
         oci_project = self.factory.makeOCIProject(pillar=distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
 
         browser = self.getViewBrowser(recipe, user=self.person)
         browser.getLink("Edit OCI recipe").click()
@@ -1101,11 +1311,12 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
     def test_set_official(self):
         distribution = self.factory.makeDistribution(
-            oci_project_admin=self.person)
+            oci_project_admin=self.person
+        )
         oci_project = self.factory.makeOCIProject(pillar=distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
 
         browser = self.getViewBrowser(recipe, user=self.person)
         browser.getLink("Edit OCI recipe").click()
@@ -1115,17 +1326,18 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
         content = find_main_content(browser.contents)
         self.assertThat(
-            "Official recipe:\nYes",
-            MatchesTagText(content, "official-recipe"))
+            "Official recipe:\nYes", MatchesTagText(content, "official-recipe")
+        )
 
     def test_set_official_no_permissions(self):
         distro_owner = self.factory.makePerson()
         distribution = self.factory.makeDistribution(
-            oci_project_admin=distro_owner)
+            oci_project_admin=distro_owner
+        )
         oci_project = self.factory.makeOCIProject(pillar=distribution)
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            oci_project=oci_project)
+            registrant=self.person, owner=self.person, oci_project=oci_project
+        )
 
         browser = self.getViewBrowser(recipe, user=self.person)
         browser.getLink("Edit OCI recipe").click()
@@ -1135,33 +1347,39 @@ class TestOCIRecipeEditView(OCIConfigHelperMixin, BaseTestOCIRecipeView):
 
         error_message = (
             "You do not have permission to change the official status "
-            "of this recipe.")
+            "of this recipe."
+        )
         self.assertIn(error_message, browser.contents)
 
 
 class TestOCIRecipeDeleteView(BaseTestOCIRecipeView):
-
     def setUp(self):
         super().setUp()
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_unauthorized(self):
         # A user without edit access cannot delete an OCI recipe.
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person)
+            registrant=self.person, owner=self.person
+        )
         recipe_url = canonical_url(recipe)
         other_person = self.factory.makePerson()
         browser = self.getViewBrowser(recipe, user=other_person)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Delete OCI recipe")
+            LinkNotFoundError, browser.getLink, "Delete OCI recipe"
+        )
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, recipe_url + "/+delete",
-            user=other_person)
+            Unauthorized,
+            self.getUserBrowser,
+            recipe_url + "/+delete",
+            user=other_person,
+        )
 
     def test_delete_recipe_without_builds(self):
         # An OCI recipe without builds can be deleted.
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person)
+            registrant=self.person, owner=self.person
+        )
         recipe_url = canonical_url(recipe)
         oci_project_url = canonical_url(recipe.oci_project)
         browser = self.getViewBrowser(recipe, user=self.person)
@@ -1173,7 +1391,8 @@ class TestOCIRecipeDeleteView(BaseTestOCIRecipeView):
     def test_delete_recipe_with_builds(self):
         # An OCI recipe with builds can be deleted.
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person)
+            registrant=self.person, owner=self.person
+        )
         ocibuild = self.factory.makeOCIRecipeBuild(recipe=recipe)
         job = self.factory.makeOCIRecipeBuildJob(build=ocibuild)
         ocifile = self.factory.makeOCIFile(build=ocibuild)
@@ -1191,41 +1410,48 @@ class TestOCIRecipeDeleteView(BaseTestOCIRecipeView):
         self.assertRaises(NotFound, browser.open, recipe_url)
 
         # Checks that only the related artifacts were deleted too.
-        def obj_exists(obj, search_key='id'):
+        def obj_exists(obj, search_key="id"):
             obj = removeSecurityProxy(obj)
             store = IStore(obj)
             cls = obj.__class__
             cls_attribute = getattr(cls, search_key)
             identifier = getattr(obj, search_key)
             return not store.find(cls, cls_attribute == identifier).is_empty()
+
         self.assertFalse(obj_exists(ocibuild))
         self.assertFalse(obj_exists(ocifile))
-        self.assertFalse(obj_exists(job, 'job_id'))
+        self.assertFalse(obj_exists(job, "job_id"))
 
         self.assertTrue(obj_exists(unrelated_build))
         self.assertTrue(obj_exists(unrelated_file))
-        self.assertTrue(obj_exists(unrelated_job, 'job_id'))
+        self.assertTrue(obj_exists(unrelated_job, "job_id"))
 
 
 class TestOCIRecipeView(BaseTestOCIRecipeView):
-
     def setUp(self):
         super().setUp()
         self.distroseries = self.factory.makeDistroSeries()
         processor = getUtility(IProcessorSet).getByName("386")
         self.distroarchseries = self.factory.makeDistroArchSeries(
-            distroseries=self.distroseries, architecturetag="i386",
-            processor=processor)
+            distroseries=self.distroseries,
+            architecturetag="i386",
+            processor=processor,
+        )
         self.factory.makeBuilder(virtualized=True)
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
 
     def makeOCIRecipe(self, oci_project=None, **kwargs):
         if oci_project is None:
             oci_project = self.factory.makeOCIProject(
-                pillar=self.distroseries.distribution)
+                pillar=self.distroseries.distribution
+            )
         return self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, name="recipe-name",
-            oci_project=oci_project, **kwargs)
+            registrant=self.person,
+            owner=self.person,
+            name="recipe-name",
+            oci_project=oci_project,
+            **kwargs,
+        )
 
     def makeBuild(self, recipe=None, date_created=None, **kwargs):
         if recipe is None:
@@ -1233,13 +1459,17 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
         if date_created is None:
             date_created = datetime.now(pytz.UTC) - timedelta(hours=1)
         return self.factory.makeOCIRecipeBuild(
-            requester=self.person, recipe=recipe,
+            requester=self.person,
+            recipe=recipe,
             distro_arch_series=self.distroarchseries,
-            date_created=date_created, **kwargs)
+            date_created=date_created,
+            **kwargs,
+        )
 
     def test_breadcrumb_and_top_header(self):
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution)
+            pillar=self.distroseries.distribution
+        )
         oci_project_name = oci_project.name
         oci_project_url = canonical_url(oci_project)
         pillar_name = oci_project.pillar.name
@@ -1251,7 +1481,8 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
         view.initialize()
         content = view()
         breadcrumbs = soupmatchers.Tag(
-            "breadcrumbs", "ol", attrs={"class": "breadcrumbs"})
+            "breadcrumbs", "ol", attrs={"class": "breadcrumbs"}
+        )
 
         # Should not have a breadcrumbs (OCI project link should be at the
         # top of the page, close to project/distribution name).
@@ -1259,27 +1490,50 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
 
         # OCI project should appear at the top header, right after pillar link.
         header = soupmatchers.Tag(
-            "subtitle", "h2", attrs={"id": "watermark-heading"})
-        self.assertThat(content, soupmatchers.HTMLContains(soupmatchers.Within(
-            header, soupmatchers.Tag(
-                "pillar link", "a",
-                text=pillar_name.title(), attrs={"href": pillar_url}))))
-        self.assertThat(content, soupmatchers.HTMLContains(soupmatchers.Within(
-            header, soupmatchers.Tag(
-                    "OCI project link", "a",
-                    text="%s OCI project" % oci_project_name,
-                    attrs={"href": oci_project_url}))))
+            "subtitle", "h2", attrs={"id": "watermark-heading"}
+        )
+        self.assertThat(
+            content,
+            soupmatchers.HTMLContains(
+                soupmatchers.Within(
+                    header,
+                    soupmatchers.Tag(
+                        "pillar link",
+                        "a",
+                        text=pillar_name.title(),
+                        attrs={"href": pillar_url},
+                    ),
+                )
+            ),
+        )
+        self.assertThat(
+            content,
+            soupmatchers.HTMLContains(
+                soupmatchers.Within(
+                    header,
+                    soupmatchers.Tag(
+                        "OCI project link",
+                        "a",
+                        text="%s OCI project" % oci_project_name,
+                        attrs={"href": oci_project_url},
+                    ),
+                )
+            ),
+        )
 
     def makeRecipe(self, processor_names, **kwargs):
         recipe = self.factory.makeOCIRecipe(**kwargs)
         processors_list = []
         distroseries = self.factory.makeDistroSeries(
-            distribution=recipe.oci_project.distribution)
+            distribution=recipe.oci_project.distribution
+        )
         for proc_name in processor_names:
             proc = getUtility(IProcessorSet).getByName(proc_name)
             distro = self.factory.makeDistroArchSeries(
-                distroseries=distroseries, architecturetag=proc_name,
-                processor=proc)
+                distroseries=distroseries,
+                architecturetag=proc_name,
+                processor=proc,
+            )
             distro.addOrUpdateChroot(self.factory.makeLibraryFileAlias())
             processors_list.append(proc)
         recipe.setProcessors(processors_list)
@@ -1287,15 +1541,23 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
 
     def test_index(self):
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution)
+            pillar=self.distroseries.distribution
+        )
         oci_project_display = oci_project.display_name
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="recipe-repository",
-            paths=["refs/heads/v1.0-20.04"])
+            owner=self.person,
+            target=self.person,
+            name="recipe-repository",
+            paths=["refs/heads/v1.0-20.04"],
+        )
         recipe = self.makeRecipe(
             processor_names=["amd64", "386"],
-            build_file="Dockerfile", git_ref=ref,
-            oci_project=oci_project, registrant=self.person, owner=self.person)
+            build_file="Dockerfile",
+            git_ref=ref,
+            oci_project=oci_project,
+            registrant=self.person,
+            owner=self.person,
+        )
         build_request = recipe.requestBuilds(self.person)
         builds = recipe.requestBuildsFromJob(self.person, build_request)
         job = removeSecurityProxy(build_request).job
@@ -1303,20 +1565,27 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
 
         for build in builds:
             removeSecurityProxy(build).updateStatus(
-                    BuildStatus.BUILDING, builder=None,
-                    date_started=build.date_created)
+                BuildStatus.BUILDING,
+                builder=None,
+                date_started=build.date_created,
+            )
             removeSecurityProxy(build).updateStatus(
-                BuildStatus.FULLYBUILT, builder=None,
-                date_finished=build.date_started + timedelta(minutes=30))
+                BuildStatus.FULLYBUILT,
+                builder=None,
+                date_finished=build.date_started + timedelta(minutes=30),
+            )
 
         # We also need to account for builds that don't have a build_request
         build = self.makeBuild(
-            recipe=recipe, status=BuildStatus.FULLYBUILT,
-            duration=timedelta(minutes=30))
+            recipe=recipe,
+            status=BuildStatus.FULLYBUILT,
+            duration=timedelta(minutes=30),
+        )
 
         browser = self.getViewBrowser(build_request.recipe)
         login_person(self.person)
-        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """\
             .*
             OCI recipe information
             Owner: Test Person
@@ -1354,26 +1623,37 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
             30 minutes ago
             Recipe push rules
             This OCI recipe has no push rules defined yet.
-            """ % (oci_project_display, recipe.build_path),
-            extract_text(find_main_content(browser.contents)))
+            """
+            % (oci_project_display, recipe.build_path),
+            extract_text(find_main_content(browser.contents)),
+        )
 
         # Check portlet on side menu.
         privacy_tag = find_tag_by_id(browser.contents, "privacy")
         self.assertTextMatchesExpressionIgnoreWhitespace(
             "This OCI recipe contains Public information",
-            extract_text(privacy_tag))
+            extract_text(privacy_tag),
+        )
 
     def test_index_cancelled_build(self):
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution)
+            pillar=self.distroseries.distribution
+        )
         oci_project_display = oci_project.display_name
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="recipe-repository",
-            paths=["refs/heads/v1.0-20.04"])
+            owner=self.person,
+            target=self.person,
+            name="recipe-repository",
+            paths=["refs/heads/v1.0-20.04"],
+        )
         recipe = self.makeRecipe(
             processor_names=["amd64", "386"],
-            build_file="Dockerfile", git_ref=ref,
-            oci_project=oci_project, registrant=self.person, owner=self.person)
+            build_file="Dockerfile",
+            git_ref=ref,
+            oci_project=oci_project,
+            registrant=self.person,
+            owner=self.person,
+        )
         build_request = recipe.requestBuilds(self.person)
         builds = recipe.requestBuildsFromJob(self.person, build_request)
         job = removeSecurityProxy(build_request).job
@@ -1381,20 +1661,27 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
 
         for build in builds:
             removeSecurityProxy(build).updateStatus(
-                    BuildStatus.BUILDING, builder=None,
-                    date_started=build.date_created)
+                BuildStatus.BUILDING,
+                builder=None,
+                date_started=build.date_created,
+            )
             removeSecurityProxy(build).updateStatus(
-                BuildStatus.CANCELLED, builder=None,
-                date_finished=build.date_started + timedelta(minutes=30))
+                BuildStatus.CANCELLED,
+                builder=None,
+                date_finished=build.date_started + timedelta(minutes=30),
+            )
 
         # We also need to account for builds that don't have a build_request
         build = self.makeBuild(
-            recipe=recipe, status=BuildStatus.FULLYBUILT,
-            duration=timedelta(minutes=30))
+            recipe=recipe,
+            status=BuildStatus.FULLYBUILT,
+            duration=timedelta(minutes=30),
+        )
 
         browser = self.getViewBrowser(build_request.recipe)
         login_person(self.person)
-        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """\
             .*
             OCI recipe information
             Owner: Test Person
@@ -1432,25 +1719,36 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
             30 minutes ago
             Recipe push rules
             This OCI recipe has no push rules defined yet.
-            """ % (oci_project_display, recipe.build_path),
-            extract_text(find_main_content(browser.contents)))
+            """
+            % (oci_project_display, recipe.build_path),
+            extract_text(find_main_content(browser.contents)),
+        )
 
         # Check portlet on side menu.
         privacy_tag = find_tag_by_id(browser.contents, "privacy")
         self.assertTextMatchesExpressionIgnoreWhitespace(
             "This OCI recipe contains Public information",
-            extract_text(privacy_tag))
+            extract_text(privacy_tag),
+        )
 
     def test_index_cancelling_build(self):
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution)
+            pillar=self.distroseries.distribution
+        )
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="recipe-repository",
-            paths=["refs/heads/v1.0-20.04"])
+            owner=self.person,
+            target=self.person,
+            name="recipe-repository",
+            paths=["refs/heads/v1.0-20.04"],
+        )
         recipe = self.makeRecipe(
             processor_names=["amd64", "386"],
-            build_file="Dockerfile", git_ref=ref,
-            oci_project=oci_project, registrant=self.person, owner=self.person)
+            build_file="Dockerfile",
+            git_ref=ref,
+            oci_project=oci_project,
+            registrant=self.person,
+            owner=self.person,
+        )
         build_request = recipe.requestBuilds(self.person)
         builds = recipe.requestBuildsFromJob(self.person, build_request)
         job = removeSecurityProxy(build_request).job
@@ -1458,15 +1756,20 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
 
         for build in builds:
             removeSecurityProxy(build).updateStatus(
-                    BuildStatus.BUILDING, builder=None,
-                    date_started=build.date_created)
+                BuildStatus.BUILDING,
+                builder=None,
+                date_started=build.date_created,
+            )
             removeSecurityProxy(build).updateStatus(
-                BuildStatus.CANCELLING, builder=None,
-                date_finished=build.date_started + timedelta(minutes=30))
+                BuildStatus.CANCELLING,
+                builder=None,
+                date_finished=build.date_started + timedelta(minutes=30),
+            )
 
         browser = self.getViewBrowser(build_request.recipe)
         login_person(self.person)
-        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """\
             .*
             There were build failures.
             No registry upload requested.
@@ -1483,49 +1786,66 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
             \\(estimated\\)
             .*
             """,
-            extract_text(find_main_content(browser.contents)))
+            extract_text(find_main_content(browser.contents)),
+        )
 
         # Check portlet on side menu.
         privacy_tag = find_tag_by_id(browser.contents, "privacy")
         self.assertTextMatchesExpressionIgnoreWhitespace(
             "This OCI recipe contains Public information",
-            extract_text(privacy_tag))
+            extract_text(privacy_tag),
+        )
 
     def test_index_for_private_recipe_shows_banner(self):
         recipe = self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person,
-            information_type=InformationType.USERDATA)
+            registrant=self.person,
+            owner=self.person,
+            information_type=InformationType.USERDATA,
+        )
         browser = self.getViewBrowser(recipe, user=self.person)
 
         # Check top banner.
         banners = find_tags_by_class(
-            browser.contents, "private_banner_container")
+            browser.contents, "private_banner_container"
+        )
         self.assertEqual(1, len(banners))
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            'The information on this page is private.',
-            extract_text(banners[0]))
+            "The information on this page is private.",
+            extract_text(banners[0]),
+        )
 
         # Check portlet on side menu.
         privacy_tag = find_tag_by_id(browser.contents, "privacy")
         self.assertTextMatchesExpressionIgnoreWhitespace(
             "This OCI recipe contains Private information",
-            extract_text(privacy_tag))
+            extract_text(privacy_tag),
+        )
 
     def test_index_with_build_args(self):
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution)
+            pillar=self.distroseries.distribution
+        )
         oci_project_display = oci_project.display_name
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="recipe-repository",
-            paths=["refs/heads/v1.0-20.04"])
+            owner=self.person,
+            target=self.person,
+            name="recipe-repository",
+            paths=["refs/heads/v1.0-20.04"],
+        )
         recipe = self.makeOCIRecipe(
-            oci_project=oci_project, git_ref=ref, build_file="Dockerfile",
-            build_args={"VAR1": "123", "VAR2": "XXX"})
+            oci_project=oci_project,
+            git_ref=ref,
+            build_file="Dockerfile",
+            build_args={"VAR1": "123", "VAR2": "XXX"},
+        )
         build_path = recipe.build_path
         build = self.makeBuild(
-            recipe=recipe, status=BuildStatus.FULLYBUILT,
-            duration=timedelta(minutes=30))
-        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+            recipe=recipe,
+            status=BuildStatus.FULLYBUILT,
+            duration=timedelta(minutes=30),
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """\
             recipe-name
             .*
             OCI recipe information
@@ -1551,25 +1871,36 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
             Successfully built
             386
             30 minutes ago
-            """ % (oci_project_display, build_path),
-            self.getMainText(build.recipe))
+            """
+            % (oci_project_display, build_path),
+            self.getMainText(build.recipe),
+        )
 
     def test_index_for_subscriber_without_git_repo_access(self):
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution)
+            pillar=self.distroseries.distribution
+        )
         oci_project_display = oci_project.display_name
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="recipe-repository",
+            owner=self.person,
+            target=self.person,
+            name="recipe-repository",
             paths=["refs/heads/v1.0-20.04"],
-            information_type=InformationType.PRIVATESECURITY)
+            information_type=InformationType.PRIVATESECURITY,
+        )
         recipe = self.makeOCIRecipe(
-            oci_project=oci_project, git_ref=ref, build_file="Dockerfile",
-            information_type=InformationType.PRIVATESECURITY)
+            oci_project=oci_project,
+            git_ref=ref,
+            build_file="Dockerfile",
+            information_type=InformationType.PRIVATESECURITY,
+        )
         with admin_logged_in():
             build_path = recipe.build_path
             self.makeBuild(
-                recipe=recipe, status=BuildStatus.FULLYBUILT,
-                duration=timedelta(minutes=30))
+                recipe=recipe,
+                status=BuildStatus.FULLYBUILT,
+                duration=timedelta(minutes=30),
+            )
 
         # Subscribe a user.
         subscriber = self.factory.makePerson()
@@ -1578,7 +1909,8 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
 
         with person_logged_in(subscriber):
             main_text = self.getMainText(recipe, user=subscriber)
-        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """\
             recipe-name
             .*
             OCI recipe information
@@ -1603,15 +1935,19 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
             Successfully built
             386
             30 minutes ago
-            """ % (oci_project_display, build_path),
-            main_text)
+            """
+            % (oci_project_display, build_path),
+            main_text,
+        )
 
     def test_index_success_with_buildlog(self):
         # The build log is shown if it is there.
         build = self.makeBuild(
-            status=BuildStatus.FULLYBUILT, duration=timedelta(minutes=30))
+            status=BuildStatus.FULLYBUILT, duration=timedelta(minutes=30)
+        )
         build.setLog(self.factory.makeLibraryFileAlias())
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Latest builds
             Build status
             Upload status
@@ -1627,31 +1963,42 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
             Successfully built
             386
             30 minutes ago
-            """, self.getMainText(build.recipe))
+            """,
+            self.getMainText(build.recipe),
+        )
 
     def test_index_no_builds(self):
         # A message is shown when there are no builds.
         recipe = self.factory.makeOCIRecipe()
         self.assertIn(
-            "This OCI recipe has not been built yet.",
-            self.getMainText(recipe))
+            "This OCI recipe has not been built yet.", self.getMainText(recipe)
+        )
 
     def test_index_pending_build(self):
         # A pending build is listed as such.
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution)
+            pillar=self.distroseries.distribution
+        )
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="recipe-repository",
-            paths=["refs/heads/v1.0-20.04"])
+            owner=self.person,
+            target=self.person,
+            name="recipe-repository",
+            paths=["refs/heads/v1.0-20.04"],
+        )
         recipe = self.makeRecipe(
             processor_names=["amd64", "386"],
-            build_file="Dockerfile", git_ref=ref,
-            oci_project=oci_project, registrant=self.person, owner=self.person)
+            build_file="Dockerfile",
+            git_ref=ref,
+            oci_project=oci_project,
+            registrant=self.person,
+            owner=self.person,
+        )
         build_request = recipe.requestBuilds(self.person)
         builds = recipe.requestBuildsFromJob(self.person, build_request)
         job = removeSecurityProxy(build_request).job
         removeSecurityProxy(job).builds = builds
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Latest builds
             Build status
             Upload status
@@ -1661,16 +2008,20 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
             Waiting for builds to start.
             a moment ago
             in .* \(estimated\)
-            """, self.getMainText(recipe))
+            """,
+            self.getMainText(recipe),
+        )
 
     def test_index_request_builds_link(self):
         # Recipe owners get a link to allow requesting builds.
         owner = self.factory.makePerson()
         distroseries = self.factory.makeDistroSeries()
         oci_project = self.factory.makeOCIProject(
-            pillar=distroseries.distribution)
+            pillar=distroseries.distribution
+        )
         recipe = self.factory.makeOCIRecipe(
-            registrant=owner, owner=owner, oci_project=oci_project)
+            registrant=owner, owner=owner, oci_project=oci_project
+        )
         recipe_name = recipe.name
         browser = self.getViewBrowser(recipe, user=owner)
         browser.getLink("Request builds").click()
@@ -1681,30 +2032,38 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
         # requesting builds.
         distroseries = self.factory.makeDistroSeries()
         oci_project = self.factory.makeOCIProject(
-            pillar=distroseries.distribution)
+            pillar=distroseries.distribution
+        )
         recipe = self.factory.makeOCIRecipe(oci_project=oci_project)
         recipe_url = canonical_url(recipe)
         browser = self.getViewBrowser(recipe, user=self.person)
         self.assertRaises(LinkNotFoundError, browser.getLink, "Request builds")
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, recipe_url + "/+request-builds",
-            user=self.person)
+            Unauthorized,
+            self.getUserBrowser,
+            recipe_url + "/+request-builds",
+            user=self.person,
+        )
 
     def setStatus(self, build, status):
         build.updateStatus(
-            BuildStatus.BUILDING, date_started=build.date_created)
+            BuildStatus.BUILDING, date_started=build.date_created
+        )
         build.updateStatus(
-            status, date_finished=build.date_started + timedelta(minutes=30))
+            status, date_finished=build.date_started + timedelta(minutes=30)
+        )
 
     def test_builds(self):
         # OCIRecipeView.builds produces reasonable results.
         recipe = self.makeOCIRecipe()
         # Create oldest builds first so that they sort properly by id.
         date_gen = time_counter(
-            datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1))
+            datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1)
+        )
         builds = [
             self.makeBuild(recipe=recipe, date_created=next(date_gen))
-            for i in range(11)]
+            for i in range(11)
+        ]
         view = OCIRecipeView(recipe, None)
         self.assertEqual(list(reversed(builds)), view.builds)
         self.setStatus(builds[10], BuildStatus.FULLYBUILT)
@@ -1713,7 +2072,8 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
         # When there are >= 9 pending builds, only the most recent of any
         # completed builds is returned.
         self.assertEqual(
-            list(reversed(builds[:9])) + [builds[10]], view.builds)
+            list(reversed(builds[:9])) + [builds[10]], view.builds
+        )
         for build in builds[:9]:
             self.setStatus(build, BuildStatus.FULLYBUILT)
         del get_property_cache(view).builds
@@ -1721,34 +2081,46 @@ class TestOCIRecipeView(BaseTestOCIRecipeView):
 
 
 class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView):
-
     def setUp(self):
         super().setUp()
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         self.distroseries = self.factory.makeDistroSeries(
-            distribution=self.ubuntu, name="shiny", displayname="Shiny")
+            distribution=self.ubuntu, name="shiny", displayname="Shiny"
+        )
+        distribution = self.distroseries.distribution
         self.architectures = []
         for processor, architecture in ("386", "i386"), ("amd64", "amd64"):
             das = self.factory.makeDistroArchSeries(
-                distroseries=self.distroseries, architecturetag=architecture,
-                processor=getUtility(IProcessorSet).getByName(processor))
+                distroseries=self.distroseries,
+                architecturetag=architecture,
+                processor=getUtility(IProcessorSet).getByName(processor),
+            )
             das.addOrUpdateChroot(self.factory.makeLibraryFileAlias())
             self.architectures.append(das)
-        self.useFixture(FeatureFixture({
-            OCI_RECIPE_ALLOW_CREATE: "on",
-            "oci.build_series.%s" % self.distroseries.distribution.name:
-                self.distroseries.name,
-            }))
+        self.useFixture(
+            FeatureFixture(
+                {
+                    OCI_RECIPE_ALLOW_CREATE: "on",
+                    "oci.build_series.%s"
+                    % distribution.name: self.distroseries.name,
+                }
+            )
+        )
         oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution,
-            ociprojectname="oci-project-name")
+            pillar=distribution,
+            ociprojectname="oci-project-name",
+        )
         self.recipe = self.factory.makeOCIRecipe(
-            name="recipe-name", registrant=self.person, owner=self.person,
-            oci_project=oci_project)
+            name="recipe-name",
+            registrant=self.person,
+            owner=self.person,
+            oci_project=oci_project,
+        )
 
     def test_request_builds_page(self):
         # The +request-builds page is sane.
-        self.assertTextMatchesExpressionIgnoreWhitespace("""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """
             Request builds for recipe-name
             recipe-name
             Request builds
@@ -1758,12 +2130,14 @@ class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView):
             or
             Cancel
             """,
-            self.getMainText(self.recipe, "+request-builds", user=self.person))
+            self.getMainText(self.recipe, "+request-builds", user=self.person),
+        )
 
     def test_request_builds_not_owner(self):
         # A user without launchpad.Edit cannot request builds.
         self.assertRaises(
-            Unauthorized, self.getViewBrowser, self.recipe, "+request-builds")
+            Unauthorized, self.getViewBrowser, self.recipe, "+request-builds"
+        )
 
     def runRequestBuildJobs(self):
         with admin_logged_in():
@@ -1774,7 +2148,8 @@ class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView):
     def test_request_builds_action(self):
         # Requesting a build creates pending builds.
         browser = self.getViewBrowser(
-            self.recipe, "+request-builds", user=self.person)
+            self.recipe, "+request-builds", user=self.person
+        )
         self.assertTrue(browser.getControl("amd64").selected)
         self.assertTrue(browser.getControl("i386").selected)
         browser.getControl("Request builds").click()
@@ -1785,146 +2160,206 @@ class TestOCIRecipeRequestBuildsView(BaseTestOCIRecipeView):
         builds = self.recipe.pending_builds
         self.assertContentEqual(
             ["amd64", "i386"],
-            [build.distro_arch_series.architecturetag for build in builds])
+            [build.distro_arch_series.architecturetag for build in builds],
+        )
         self.assertContentEqual(
-            [2510], {build.buildqueue_record.lastscore for build in builds})
+            [2510], {build.buildqueue_record.lastscore for build in builds}
+        )
 
     def test_request_builds_no_architectures(self):
         # Selecting no architectures causes a validation failure.
         browser = self.getViewBrowser(
-            self.recipe, "+request-builds", user=self.person)
+            self.recipe, "+request-builds", user=self.person
+        )
         browser.getControl("amd64").selected = False
         browser.getControl("i386").selected = False
         browser.getControl("Request builds").click()
         self.assertIn(
             "You need to select at least one architecture.",
-            extract_text(find_main_content(browser.contents)))
+            extract_text(find_main_content(browser.contents)),
+        )
 
 
-class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
-                                     BaseTestOCIRecipeView):
+class TestOCIRecipeEditPushRulesView(
+    OCIConfigHelperMixin, BaseTestOCIRecipeView
+):
     def setUp(self):
         super().setUp()
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         self.distroseries = self.factory.makeDistroSeries(
-            distribution=self.ubuntu, name="shiny", displayname="Shiny")
-
-        self.useFixture(FeatureFixture({
-            OCI_RECIPE_ALLOW_CREATE: "on",
-            "oci.build_series.%s" % self.distroseries.distribution.name:
-                self.distroseries.name,
-        }))
+            distribution=self.ubuntu, name="shiny", displayname="Shiny"
+        )
+        distribution = self.distroseries.distribution
+
+        self.useFixture(
+            FeatureFixture(
+                {
+                    OCI_RECIPE_ALLOW_CREATE: "on",
+                    "oci.build_series.%s"
+                    % distribution.name: self.distroseries.name,
+                }
+            )
+        )
         self.oci_project = self.factory.makeOCIProject(
-            pillar=self.distroseries.distribution,
-            ociprojectname="oci-project-name")
+            pillar=distribution,
+            ociprojectname="oci-project-name",
+        )
 
         self.member = self.factory.makePerson()
         self.team = self.factory.makeTeam(members=[self.person, self.member])
 
         self.recipe = self.factory.makeOCIRecipe(
-            name="recipe-name", registrant=self.person, owner=self.person,
-            oci_project=self.oci_project)
+            name="recipe-name",
+            registrant=self.person,
+            owner=self.person,
+            oci_project=self.oci_project,
+        )
 
         self.team_owned_recipe = self.factory.makeOCIRecipe(
-            name="recipe-name", registrant=self.person, owner=self.team,
-            oci_project=self.oci_project)
+            name="recipe-name",
+            registrant=self.person,
+            owner=self.team,
+            oci_project=self.oci_project,
+        )
 
         self.setConfig()
 
     def test_view_oci_push_rules_owner(self):
         url = self.factory.getUniqueURL()
-        credentials = {'username': 'foo', 'password': 'bar'}
+        credentials = {"username": "foo", "password": "bar"}
         registry_credentials = getUtility(IOCIRegistryCredentialsSet).new(
-            registrant=self.person, owner=self.person, url=url,
-            credentials=credentials)
+            registrant=self.person,
+            owner=self.person,
+            url=url,
+            credentials=credentials,
+        )
         image_name = self.factory.getUniqueUnicode()
         push_rule = getUtility(IOCIPushRuleSet).new(
             recipe=self.recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
+            image_name=image_name,
+        )
         view = create_initialized_view(
-                self.recipe, "+index", principal=self.person)
+            self.recipe, "+index", principal=self.person
+        )
 
         # Display the Registry URL and the Username
         # for the credentials owner
         with person_logged_in(self.person):
             rendered_view = view.render()
-            row = soupmatchers.Tag("push rule row", "tr",
-                                   attrs={"id": "rule-%d" % push_rule.id})
-            self.assertThat(rendered_view, soupmatchers.HTMLContains(
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Registry URL", "td",
-                                     text=registry_credentials.url)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Username", "td",
-                                     text=registry_credentials.username)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag(
-                        "Image name", "td", text=image_name))))
+            row = soupmatchers.Tag(
+                "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id}
+            )
+            self.assertThat(
+                rendered_view,
+                soupmatchers.HTMLContains(
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Registry URL", "td", text=registry_credentials.url
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Username",
+                            "td",
+                            text=registry_credentials.username,
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag("Image name", "td", text=image_name),
+                    ),
+                ),
+            )
 
     def test_view_oci_push_rules_non_owner(self):
         url = self.factory.getUniqueURL()
-        credentials = {'username': 'foo', 'password': 'bar'}
+        credentials = {"username": "foo", "password": "bar"}
         registry_credentials = getUtility(IOCIRegistryCredentialsSet).new(
-            registrant=self.person, owner=self.person, url=url,
-            credentials=credentials)
+            registrant=self.person,
+            owner=self.person,
+            url=url,
+            credentials=credentials,
+        )
         image_name = self.factory.getUniqueUnicode()
         push_rule = getUtility(IOCIPushRuleSet).new(
             recipe=self.recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
+            image_name=image_name,
+        )
         non_owner = self.factory.makePerson()
         admin = self.factory.makePerson(
-            member_of=[getUtility(IPersonSet).getByName('admins')])
+            member_of=[getUtility(IPersonSet).getByName("admins")]
+        )
         view = create_initialized_view(
-                self.recipe, "+index", principal=non_owner)
+            self.recipe, "+index", principal=non_owner
+        )
 
         # Display only the image name for users
         # who are not the registry credentials owner
         with person_logged_in(non_owner):
             rendered_view = view.render()
-            row = soupmatchers.Tag("push rule row", "tr",
-                                   attrs={"id": "rule-%d" % push_rule.id})
-            self.assertThat(rendered_view, soupmatchers.HTMLContains(
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Registry URL", "td",
-                                     text=soupmatchers._not_passed)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Username", "td",
-                                     text=soupmatchers._not_passed)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag(
-                        "Image name", "td", text=image_name))))
+            row = soupmatchers.Tag(
+                "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id}
+            )
+            self.assertThat(
+                rendered_view,
+                soupmatchers.HTMLContains(
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Registry URL", "td", text=soupmatchers._not_passed
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Username", "td", text=soupmatchers._not_passed
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag("Image name", "td", text=image_name),
+                    ),
+                ),
+            )
 
         # Anonymous users can't see registry credentials
         # even though they can see the push rule
         with anonymous_logged_in():
             rendered_view = view.render()
-            row = soupmatchers.Tag("push rule row", "tr",
-                                   attrs={"id": "rule-%d" % push_rule.id})
-            self.assertThat(rendered_view, soupmatchers.HTMLContains(
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Registry URL", "td",
-                                     text=soupmatchers._not_passed)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Region", "td",
-                                     text=soupmatchers._not_passed)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Username", "td",
-                                     text=soupmatchers._not_passed)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag(
-                        "Image name", "td", text=image_name))))
+            row = soupmatchers.Tag(
+                "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id}
+            )
+            self.assertThat(
+                rendered_view,
+                soupmatchers.HTMLContains(
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Registry URL", "td", text=soupmatchers._not_passed
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Region", "td", text=soupmatchers._not_passed
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Username", "td", text=soupmatchers._not_passed
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag("Image name", "td", text=image_name),
+                    ),
+                ),
+            )
 
         # Although not the owner of the registry credentials
         # the admin user has launchpad.View permission on
@@ -1933,56 +2368,76 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         # see ViewOCIRegistryCredentials
         with person_logged_in(admin):
             rendered_view = view.render()
-            row = soupmatchers.Tag("push rule row", "tr",
-                                   attrs={"id": "rule-%d" % push_rule.id})
-            self.assertThat(rendered_view, soupmatchers.HTMLContains(
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Registry URL", "td",
-                                     text=registry_credentials.url)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag("Username", "td",
-                                     text=registry_credentials.username)),
-                soupmatchers.Within(
-                    row,
-                    soupmatchers.Tag(
-                        "Image name", "td", text=image_name))))
+            row = soupmatchers.Tag(
+                "push rule row", "tr", attrs={"id": "rule-%d" % push_rule.id}
+            )
+            self.assertThat(
+                rendered_view,
+                soupmatchers.HTMLContains(
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Registry URL", "td", text=registry_credentials.url
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag(
+                            "Username",
+                            "td",
+                            text=registry_credentials.username,
+                        ),
+                    ),
+                    soupmatchers.Within(
+                        row,
+                        soupmatchers.Tag("Image name", "td", text=image_name),
+                    ),
+                ),
+            )
 
     def test_edit_oci_push_rules(self):
         url = self.factory.getUniqueURL()
-        credentials = {'username': 'foo', 'password': 'bar'}
+        credentials = {"username": "foo", "password": "bar"}
         registry_credentials = getUtility(IOCIRegistryCredentialsSet).new(
-            registrant=self.person, owner=self.person, url=url,
-            credentials=credentials)
+            registrant=self.person,
+            owner=self.person,
+            url=url,
+            credentials=credentials,
+        )
         image_name = self.factory.getUniqueUnicode()
         push_rule = getUtility(IOCIPushRuleSet).new(
             recipe=self.recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
+            image_name=image_name,
+        )
         browser = self.getViewBrowser(self.recipe, user=self.person)
         browser.getLink("Edit push rules").click()
         # assert image name is displayed correctly
         with person_logged_in(self.person):
-            self.assertEqual(image_name, browser.getControl(
-                name="field.image_name.%d" % push_rule.id).value)
+            self.assertEqual(
+                image_name,
+                browser.getControl(
+                    name="field.image_name.%d" % push_rule.id
+                ).value,
+            )
 
         # assert image name is required
         with person_logged_in(self.person):
             browser.getControl(
-                name="field.image_name.%d" % push_rule.id).value = ""
+                name="field.image_name.%d" % push_rule.id
+            ).value = ""
         browser.getControl("Save").click()
         self.assertIn("Required input is missing", browser.contents)
 
         # set image name to valid string
         with person_logged_in(self.person):
             browser.getControl(
-                name="field.image_name.%d" % push_rule.id).value = "image1"
+                name="field.image_name.%d" % push_rule.id
+            ).value = "image1"
         browser.getControl("Save").click()
         # and assert model changed
         with person_logged_in(self.person):
-            self.assertEqual(
-                push_rule.image_name, "image1")
+            self.assertEqual(push_rule.image_name, "image1")
             # Create a second push rule and test we call setNewImageName only
             # in cases where image name is different than the one on the model
             # otherwise we get the exception on rows the user doesn't actually
@@ -1992,16 +2447,17 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
             second_rule = getUtility(IOCIPushRuleSet).new(
                 recipe=self.recipe,
                 registry_credentials=registry_credentials,
-                image_name="second image")
+                image_name="second image",
+            )
         browser = self.getViewBrowser(self.recipe, user=self.person)
         browser.getLink("Edit push rules").click()
         with person_logged_in(self.person):
             browser.getControl(
-                name="field.image_name.%d" % push_rule.id).value = "image2"
+                name="field.image_name.%d" % push_rule.id
+            ).value = "image2"
         browser.getControl("Save").click()
         with person_logged_in(self.person):
-            self.assertEqual(
-                push_rule.image_name, "image2")
+            self.assertEqual(push_rule.image_name, "image2")
 
         # Attempt to set the same name on the second rule
         # will result in expected exception
@@ -2009,48 +2465,67 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         browser.getLink("Edit push rules").click()
         with person_logged_in(self.person):
             browser.getControl(
-                name="field.image_name.%d" % second_rule.id).value = "image2"
-        self.assertRaises(OCIPushRuleAlreadyExists,
-                          browser.getControl("Save").click)
+                name="field.image_name.%d" % second_rule.id
+            ).value = "image2"
+        self.assertRaises(
+            OCIPushRuleAlreadyExists, browser.getControl("Save").click
+        )
 
     def test_edit_oci_push_rules_non_owner_of_credentials(self):
         url = self.factory.getUniqueURL()
-        credentials = {'username': 'foo', 'password': 'bar'}
+        credentials = {"username": "foo", "password": "bar"}
         registry_credentials = getUtility(IOCIRegistryCredentialsSet).new(
-            registrant=self.person, owner=self.person, url=url,
-            credentials=credentials)
+            registrant=self.person,
+            owner=self.person,
+            url=url,
+            credentials=credentials,
+        )
         image_names = [self.factory.getUniqueUnicode() for _ in range(2)]
         push_rules = [
             getUtility(IOCIPushRuleSet).new(
                 recipe=self.team_owned_recipe,
                 registry_credentials=registry_credentials,
-                image_name=image_name)
-            for image_name in image_names]
+                image_name=image_name,
+            )
+            for image_name in image_names
+        ]
         Store.of(push_rules[-1]).flush()
         push_rule_ids = [push_rule.id for push_rule in push_rules]
         browser = self.getViewBrowser(self.team_owned_recipe, user=self.member)
         browser.getLink("Edit push rules").click()
         row = soupmatchers.Tag(
-            "push rule row", "tr", attrs={"class": "push-rule"})
-        self.assertThat(browser.contents, soupmatchers.HTMLContains(
-            soupmatchers.Within(
-                row,
-                soupmatchers.Tag(
-                    "username widget", "span",
-                    attrs={
-                        "id": "field.username.%d" % push_rule_ids[0],
-                        "class": "sprite private",
-                        })),
-            soupmatchers.Within(
-                row,
-                soupmatchers.Tag(
-                    "url widget", "span",
-                    attrs={
-                        "id": "field.url.%d" % push_rule_ids[0],
-                        "class": "sprite private",
-                        }))))
+            "push rule row", "tr", attrs={"class": "push-rule"}
+        )
+        self.assertThat(
+            browser.contents,
+            soupmatchers.HTMLContains(
+                soupmatchers.Within(
+                    row,
+                    soupmatchers.Tag(
+                        "username widget",
+                        "span",
+                        attrs={
+                            "id": "field.username.%d" % push_rule_ids[0],
+                            "class": "sprite private",
+                        },
+                    ),
+                ),
+                soupmatchers.Within(
+                    row,
+                    soupmatchers.Tag(
+                        "url widget",
+                        "span",
+                        attrs={
+                            "id": "field.url.%d" % push_rule_ids[0],
+                            "class": "sprite private",
+                        },
+                    ),
+                ),
+            ),
+        )
         browser.getControl(
-            name="field.image_name.%d" % push_rule_ids[0]).value = "image1"
+            name="field.image_name.%d" % push_rule_ids[0]
+        ).value = "image1"
         browser.getControl("Save").click()
         with person_logged_in(self.member):
             self.assertEqual("image1", push_rules[0].image_name)
@@ -2058,25 +2533,31 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
 
     def test_delete_oci_push_rules(self):
         url = self.factory.getUniqueURL()
-        credentials = {'username': 'foo', 'password': 'bar'}
+        credentials = {"username": "foo", "password": "bar"}
         registry_credentials = getUtility(IOCIRegistryCredentialsSet).new(
-            registrant=self.person, owner=self.person, url=url,
-            credentials=credentials)
+            registrant=self.person,
+            owner=self.person,
+            url=url,
+            credentials=credentials,
+        )
         image_name = self.factory.getUniqueUnicode()
         push_rule = getUtility(IOCIPushRuleSet).new(
             recipe=self.recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
+            image_name=image_name,
+        )
         browser = self.getViewBrowser(self.recipe, user=self.person)
         browser.getLink("Edit push rules").click()
         with person_logged_in(self.person):
             browser.getControl(
-                name="field.delete.%d" % push_rule.id).value = True
+                name="field.delete.%d" % push_rule.id
+            ).value = True
         browser.getControl("Save").click()
 
         with person_logged_in(self.person):
             self.assertIsNone(
-                getUtility(IOCIPushRuleSet).getByID(push_rule.id))
+                getUtility(IOCIPushRuleSet).getByID(push_rule.id)
+            )
 
     def test_add_oci_push_rules_validations(self):
         # Add new rule works when there are no rules in the DB.
@@ -2113,20 +2594,28 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         browser.getControl(name="field.add_url").value = url
         browser.getControl("Save").click()
         with person_logged_in(self.person):
-            rules = list(removeSecurityProxy(
-                getUtility(IOCIPushRuleSet).findByRecipe(self.recipe)))
+            rules = list(
+                removeSecurityProxy(
+                    getUtility(IOCIPushRuleSet).findByRecipe(self.recipe)
+                )
+            )
         self.assertEqual(len(rules), 1)
         rule = rules[0]
-        self.assertThat(rule, MatchesStructure(
-            image_name=Equals("imagename1"),
-            registry_url=Equals(url),
-            registry_credentials=MatchesStructure(
-                url=Equals(url),
-                username=Is(None))))
+        self.assertThat(
+            rule,
+            MatchesStructure(
+                image_name=Equals("imagename1"),
+                registry_url=Equals(url),
+                registry_credentials=MatchesStructure(
+                    url=Equals(url), username=Is(None)
+                ),
+            ),
+        )
 
         with person_logged_in(self.person):
             self.assertEqual(
-                {"password": None}, rule.registry_credentials.getCredentials())
+                {"password": None}, rule.registry_credentials.getCredentials()
+            )
 
     def test_add_oci_push_rules_new_username_password(self):
         # Supplying an image name, registry URL, username, and password
@@ -2142,24 +2631,36 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         browser.getControl(name="field.add_username").value = "username"
         browser.getControl(name="field.add_password").value = "password"
         browser.getControl(
-            name="field.add_confirm_password").value = "password"
+            name="field.add_confirm_password"
+        ).value = "password"
         browser.getControl("Save").click()
         with person_logged_in(self.person):
-            rules = list(removeSecurityProxy(
-                getUtility(IOCIPushRuleSet).findByRecipe(self.recipe)))
+            rules = list(
+                removeSecurityProxy(
+                    getUtility(IOCIPushRuleSet).findByRecipe(self.recipe)
+                )
+            )
         self.assertEqual(len(rules), 1)
         rule = rules[0]
-        self.assertThat(rule, MatchesStructure(
-            image_name=Equals("imagename3"),
-            registry_url=Equals(url),
-            registry_credentials=MatchesStructure.byEquality(
-                url=url,
-                username="username")))
+        self.assertThat(
+            rule,
+            MatchesStructure(
+                image_name=Equals("imagename3"),
+                registry_url=Equals(url),
+                registry_credentials=MatchesStructure.byEquality(
+                    url=url, username="username"
+                ),
+            ),
+        )
         with person_logged_in(self.person):
-            self.assertEqual({
-                "username": "username", "password": "password",
-                "region": "somewhere-02"},
-                rule.registry_credentials.getCredentials())
+            self.assertEqual(
+                {
+                    "username": "username",
+                    "password": "password",
+                    "region": "somewhere-02",
+                },
+                rule.registry_credentials.getCredentials(),
+            )
 
     def test_add_oci_push_rules_existing_credentials_duplicate(self):
         # Adding a new push rule using existing credentials fails if a rule
@@ -2167,21 +2668,30 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         existing_rule = self.factory.makeOCIPushRule(
             recipe=self.recipe,
             registry_credentials=self.factory.makeOCIRegistryCredentials(
-                registrant=self.recipe.owner, owner=self.recipe.owner))
+                registrant=self.recipe.owner, owner=self.recipe.owner
+            ),
+        )
         existing_image_name = existing_rule.image_name
         existing_registry_url = existing_rule.registry_url
         existing_username = existing_rule.username
         browser = self.getViewBrowser(self.recipe, user=self.person)
         browser.getLink("Edit push rules").click()
         browser.getControl(name="field.add_credentials").value = "existing"
-        browser.getControl(name="field.add_image_name").value = (
-            existing_image_name)
-        browser.getControl(name="field.existing_credentials").value = (
-            "%s %s" % (quote(existing_registry_url), quote(existing_username)))
+        browser.getControl(
+            name="field.add_image_name"
+        ).value = existing_image_name
+        browser.getControl(
+            name="field.existing_credentials"
+        ).value = "%s %s" % (
+            quote(existing_registry_url),
+            quote(existing_username),
+        )
         browser.getControl("Save").click()
         self.assertIn(
             "A push rule already exists with the same URL, "
-            "image name, and credentials.", browser.contents)
+            "image name, and credentials.",
+            browser.contents,
+        )
 
     def test_add_oci_push_rules_existing_credentials(self):
         # Previously added registry credentials can be chosen from the radio
@@ -2192,27 +2702,38 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         existing_rule = self.factory.makeOCIPushRule(
             recipe=self.recipe,
             registry_credentials=self.factory.makeOCIRegistryCredentials(
-                registrant=self.recipe.owner, owner=self.recipe.owner,
-                credentials={}))
+                registrant=self.recipe.owner,
+                owner=self.recipe.owner,
+                credentials={},
+            ),
+        )
         existing_registry_url = existing_rule.registry_url
         browser = self.getViewBrowser(self.recipe, user=self.person)
         browser.getLink("Edit push rules").click()
         browser.getControl(name="field.add_credentials").value = "existing"
         browser.getControl(name="field.add_image_name").value = "imagename2"
-        browser.getControl(name="field.existing_credentials").value = (
-            quote(existing_registry_url))
+        browser.getControl(name="field.existing_credentials").value = quote(
+            existing_registry_url
+        )
         browser.getControl("Save").click()
         with person_logged_in(self.person):
-            rules = list(removeSecurityProxy(
-                getUtility(IOCIPushRuleSet).findByRecipe(self.recipe)))
+            rules = list(
+                removeSecurityProxy(
+                    getUtility(IOCIPushRuleSet).findByRecipe(self.recipe)
+                )
+            )
         self.assertEqual(len(rules), 2)
         rule = rules[1]
-        self.assertThat(rule, MatchesStructure(
-            image_name=Equals("imagename2"),
-            registry_url=Equals(existing_registry_url),
-            registry_credentials=MatchesStructure(
-                url=Equals(existing_registry_url),
-                username=Is(None))))
+        self.assertThat(
+            rule,
+            MatchesStructure(
+                image_name=Equals("imagename2"),
+                registry_url=Equals(existing_registry_url),
+                registry_credentials=MatchesStructure(
+                    url=Equals(existing_registry_url), username=Is(None)
+                ),
+            ),
+        )
         with person_logged_in(self.person):
             self.assertEqual({}, rule.registry_credentials.getCredentials())
 
@@ -2220,40 +2741,44 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         url = self.factory.getUniqueURL()
         browser = self.getViewBrowser(self.team_owned_recipe, user=self.member)
         browser.getLink("Edit push rules").click()
-        browser.getControl(
-            name="field.add_image_name").value = "imagename1"
-        browser.getControl(
-            name="field.add_url").value = url
+        browser.getControl(name="field.add_image_name").value = "imagename1"
+        browser.getControl(name="field.add_url").value = url
         browser.getControl(name="field.add_credentials").value = "new"
         browser.getControl("Save").click()
 
         with person_logged_in(self.member):
-            rules = list(removeSecurityProxy(
-                getUtility(IOCIPushRuleSet).findByRecipe(
-                    self.team_owned_recipe)))
+            rules = list(
+                removeSecurityProxy(
+                    getUtility(IOCIPushRuleSet).findByRecipe(
+                        self.team_owned_recipe
+                    )
+                )
+            )
         self.assertEqual(len(rules), 1)
         rule = rules[0]
-        self.assertThat(rule, MatchesStructure(
-            image_name=Equals('imagename1'),
-            registry_url=Equals(url),
-            registry_credentials=MatchesStructure(
-                url=Equals(url),
-                username=Is(None))))
+        self.assertThat(
+            rule,
+            MatchesStructure(
+                image_name=Equals("imagename1"),
+                registry_url=Equals(url),
+                registry_credentials=MatchesStructure(
+                    url=Equals(url), username=Is(None)
+                ),
+            ),
+        )
 
         with person_logged_in(self.member):
             self.assertThat(
                 rule.registry_credentials.getCredentials(),
-                MatchesDict(
-                    {"password": Equals(None)}))
+                MatchesDict({"password": Equals(None)}),
+            )
 
     def test_edit_oci_push_rules_team_owned(self):
         url = self.factory.getUniqueURL()
         browser = self.getViewBrowser(self.team_owned_recipe, user=self.member)
         browser.getLink("Edit push rules").click()
-        browser.getControl(
-            name="field.add_image_name").value = "imagename1"
-        browser.getControl(
-            name="field.add_url").value = url
+        browser.getControl(name="field.add_image_name").value = "imagename1"
+        browser.getControl(name="field.add_url").value = url
         browser.getControl(name="field.add_credentials").value = "new"
         browser.getControl("Save").click()
 
@@ -2262,43 +2787,55 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         browser = self.getViewBrowser(self.team_owned_recipe, user=self.person)
         browser.getLink("Edit push rules").click()
         with person_logged_in(self.person):
-            rules = list(removeSecurityProxy(
-                getUtility(IOCIPushRuleSet).findByRecipe(
-                    self.team_owned_recipe)))
+            rules = list(
+                removeSecurityProxy(
+                    getUtility(IOCIPushRuleSet).findByRecipe(
+                        self.team_owned_recipe
+                    )
+                )
+            )
         self.assertEqual(len(rules), 1)
         rule = rules[0]
-        self.assertEqual("imagename1", browser.getControl(
-            name="field.image_name.%d" % rule.id).value)
+        self.assertEqual(
+            "imagename1",
+            browser.getControl(name="field.image_name.%d" % rule.id).value,
+        )
 
         # set image name to valid string
         with person_logged_in(self.person):
             browser.getControl(
-                name="field.image_name.%d" % rule.id).value = "image1"
+                name="field.image_name.%d" % rule.id
+            ).value = "image1"
         browser.getControl("Save").click()
 
         # and assert model changed
         with person_logged_in(self.member):
-            self.assertEqual(
-                rule.image_name, "image1")
+            self.assertEqual(rule.image_name, "image1")
 
         # self.member will see the new image name
         browser = self.getViewBrowser(self.team_owned_recipe, user=self.member)
         browser.getLink("Edit push rules").click()
         with person_logged_in(self.member):
-            self.assertEqual("image1", browser.getControl(
-                name="field.image_name.%d" % rule.id).value)
+            self.assertEqual(
+                "image1",
+                browser.getControl(name="field.image_name.%d" % rule.id).value,
+            )
 
     def test_edit_oci_registry_creds(self):
         url = self.factory.getUniqueURL()
-        credentials = {'username': 'foo', 'password': 'bar'}
+        credentials = {"username": "foo", "password": "bar"}
         image_name = self.factory.getUniqueUnicode()
         registry_credentials = getUtility(IOCIRegistryCredentialsSet).new(
-            registrant=self.person, owner=self.person, url=url,
-            credentials=credentials)
+            registrant=self.person,
+            owner=self.person,
+            url=url,
+            credentials=credentials,
+        )
         getUtility(IOCIPushRuleSet).new(
             recipe=self.recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
+            image_name=image_name,
+        )
         browser = self.getViewBrowser(self.recipe, user=self.person)
         browser.getLink("Edit push rules").click()
         browser.getLink("Edit OCI registry credentials").click()
@@ -2307,22 +2844,27 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
         browser.getControl(name="field.add_region").value = "new_region1"
         browser.getControl(name="field.add_username").value = "new_username"
         browser.getControl(name="field.add_password").value = "password"
-        browser.getControl(name="field.add_confirm_password"
-                           ).value = "password"
+        browser.getControl(
+            name="field.add_confirm_password"
+        ).value = "password"
 
         browser.getControl("Save").click()
         with person_logged_in(self.person):
-            creds = list(getUtility(
-                IOCIRegistryCredentialsSet).findByOwner(
-                self.person))
+            creds = list(
+                getUtility(IOCIRegistryCredentialsSet).findByOwner(self.person)
+            )
 
             self.assertEqual(url, creds[1].url)
             self.assertThat(
                 (creds[1]).getCredentials(),
-                MatchesDict({
-                    "username": Equals("new_username"),
-                    "password": Equals("password"),
-                    "region": Equals("new_region1")}))
+                MatchesDict(
+                    {
+                        "username": Equals("new_username"),
+                        "password": Equals("password"),
+                        "region": Equals("new_region1"),
+                    }
+                ),
+            )
 
 
 class TestOCIRecipeListingView(BaseTestOCIRecipeView):
@@ -2330,32 +2872,42 @@ class TestOCIRecipeListingView(BaseTestOCIRecipeView):
         super().setUp()
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         self.distroseries = self.factory.makeDistroSeries(
-            distribution=self.ubuntu, name="shiny", displayname="Shiny")
+            distribution=self.ubuntu, name="shiny", displayname="Shiny"
+        )
         self.architectures = []
         for processor, architecture in ("386", "i386"), ("amd64", "amd64"):
             das = self.factory.makeDistroArchSeries(
-                distroseries=self.distroseries, architecturetag=architecture,
-                processor=getUtility(IProcessorSet).getByName(processor))
+                distroseries=self.distroseries,
+                architecturetag=architecture,
+                processor=getUtility(IProcessorSet).getByName(processor),
+            )
             das.addOrUpdateChroot(self.factory.makeLibraryFileAlias())
             self.architectures.append(das)
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
         self.oci_project = self.factory.makeOCIProject(
             pillar=self.distroseries.distribution,
-            ociprojectname="oci-project-name")
+            ociprojectname="oci-project-name",
+        )
 
     def makeRecipes(self, count=1, **kwargs):
         with person_logged_in(self.person):
             owner = self.factory.makePerson()
-            return [self.factory.makeOCIRecipe(
-                registrant=owner, owner=owner, oci_project=self.oci_project,
-                **kwargs)
-                for _ in range(count)]
+            return [
+                self.factory.makeOCIRecipe(
+                    registrant=owner,
+                    owner=owner,
+                    oci_project=self.oci_project,
+                    **kwargs,
+                )
+                for _ in range(count)
+            ]
 
     def test_oci_recipe_list_for_person(self):
         owner = self.factory.makePerson(name="recipe-owner")
         for i in range(2):
             self.factory.makeOCIRecipe(
-                name="my-oci-recipe-%s" % i, owner=owner, registrant=owner)
+                name="my-oci-recipe-%s" % i, owner=owner, registrant=owner
+            )
 
         # This recipe should not be present.
         someone_else = self.factory.makePerson()
@@ -2373,23 +2925,30 @@ class TestOCIRecipeListingView(BaseTestOCIRecipeView):
             my-oci-recipe-1  Recipe-owner   .*
             1 .* 2 of 2 results
             First .* Previous .* Next .* Last
-            """, main_text)
+            """,
+            main_text,
+        )
 
     def test_shows_no_recipe(self):
         """Should shows correct message when there are no visible recipes."""
         # Create a private OCI recipe that should not be shown.
         owner = self.factory.makePerson()
         self.factory.makeOCIRecipe(
-            owner=owner, registrant=owner, oci_project=self.oci_project,
-            information_type=InformationType.PRIVATESECURITY)
+            owner=owner,
+            registrant=owner,
+            oci_project=self.oci_project,
+            information_type=InformationType.PRIVATESECURITY,
+        )
         browser = self.getViewBrowser(
-            self.oci_project, "+recipes", user=self.person)
+            self.oci_project, "+recipes", user=self.person
+        )
         main_text = extract_text(find_main_content(browser.contents))
         with person_logged_in(self.person):
             self.assertIn(
                 "There are no recipes registered for %s"
                 % self.oci_project.name,
-                main_text)
+                main_text,
+            )
 
     def test_paginates_recipes(self):
         batch_size = 5
@@ -2397,26 +2956,29 @@ class TestOCIRecipeListingView(BaseTestOCIRecipeView):
         # We will create 1 private recipe with proper permission in the
         # list, and 9 others. This way, we should have 10 recipes in the list.
         [private_recipe] = self.makeRecipes(
-            1, information_type=InformationType.PRIVATESECURITY)
+            1, information_type=InformationType.PRIVATESECURITY
+        )
         with admin_logged_in():
             private_recipe.subscribe(self.person, private_recipe.owner)
         recipes = self.makeRecipes(9)
         recipes.append(private_recipe)
 
         browser = self.getViewBrowser(
-            self.oci_project, "+recipes", user=self.person)
+            self.oci_project, "+recipes", user=self.person
+        )
         main_text = extract_text(find_main_content(browser.contents))
-        no_wrap_main_text = main_text.replace('\n', ' ')
+        no_wrap_main_text = main_text.replace("\n", " ")
         with person_logged_in(self.person):
             self.assertIn(
                 "There are 10 recipes registered for %s"
                 % self.oci_project.name,
-                no_wrap_main_text)
+                no_wrap_main_text,
+            )
             self.assertIn("1 → 5 of 10 results", no_wrap_main_text)
             self.assertIn("First • Previous • Next • Last", no_wrap_main_text)
 
             # Make sure it's listing the first set of recipes
-            items = sorted(recipes, key=attrgetter('name'))
+            items = sorted(recipes, key=attrgetter("name"))
             for recipe in items[:batch_size]:
                 self.assertIn(recipe.name, main_text)
 
@@ -2426,15 +2988,17 @@ class TestOCIRecipeListingView(BaseTestOCIRecipeView):
 
         def getView():
             view = self.getViewBrowser(
-                self.oci_project, "+recipes", user=self.person)
+                self.oci_project, "+recipes", user=self.person
+            )
             return view
 
         def do_login():
-            self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+            self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
             login_person(self.person)
 
         recorder1, recorder2 = record_two_runs(
-            getView, self.makeRecipes, 1, 15, login_method=do_login)
+            getView, self.makeRecipes, 1, 15, login_method=do_login
+        )
 
         # The first run (with no extra pages) makes BatchNavigator issue one
         # extra count(*) on OCIRecipe. Shouldn't be a big deal.
diff --git a/lib/lp/oci/browser/tests/test_ocirecipebuild.py b/lib/lp/oci/browser/tests/test_ocirecipebuild.py
index 2466f85..5d622e4 100644
--- a/lib/lp/oci/browser/tests/test_ocirecipebuild.py
+++ b/lib/lp/oci/browser/tests/test_ocirecipebuild.py
@@ -5,11 +5,11 @@
 
 import re
 
-from fixtures import FakeLogger
 import soupmatchers
+import transaction
+from fixtures import FakeLogger
 from storm.locals import Store
 from testtools.matchers import StartsWith
-import transaction
 from zope.component import getUtility
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
@@ -25,19 +25,16 @@ from lp.services.webapp import canonical_url
 from lp.testing import (
     ANONYMOUS,
     BrowserTestCase,
+    TestCaseWithFactory,
     login,
     person_logged_in,
-    TestCaseWithFactory,
-    )
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadFunctionalLayer,
-    )
+)
+from lp.testing.layers import DatabaseFunctionalLayer, LaunchpadFunctionalLayer
 from lp.testing.pages import (
     extract_text,
     find_main_content,
     find_tags_by_class,
-    )
+)
 from lp.testing.views import create_initialized_view
 
 
@@ -47,22 +44,28 @@ class TestCanonicalUrlForOCIRecipeBuild(TestCaseWithFactory):
 
     def setUp(self):
         super().setUp()
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_canonical_url(self):
         owner = self.factory.makePerson(name="person")
         distribution = self.factory.makeDistribution(name="distro")
         oci_project = self.factory.makeOCIProject(
-            pillar=distribution, ociprojectname="oci-project")
+            pillar=distribution, ociprojectname="oci-project"
+        )
         recipe = self.factory.makeOCIRecipe(
-            name="recipe", registrant=owner, owner=owner,
-            oci_project=oci_project)
+            name="recipe",
+            registrant=owner,
+            owner=owner,
+            oci_project=oci_project,
+        )
         build = self.factory.makeOCIRecipeBuild(requester=owner, recipe=recipe)
         self.assertThat(
             canonical_url(build),
             StartsWith(
                 "http://launchpad.test/~person/distro/+oci/oci-project/";
-                "+recipe/recipe/+build/"))
+                "+recipe/recipe/+build/"
+            ),
+        )
 
 
 class TestOCIRecipeBuildView(BrowserTestCase):
@@ -71,13 +74,14 @@ class TestOCIRecipeBuildView(BrowserTestCase):
 
     def setUp(self):
         super().setUp()
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_index(self):
         build = self.factory.makeOCIRecipeBuild()
         recipe = build.recipe
         oci_project = recipe.oci_project
-        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """\
             386 build of .*
             created .*
             Build status
@@ -85,10 +89,15 @@ class TestOCIRecipeBuildView(BrowserTestCase):
             Build details
             Recipe: OCI recipe %s/%s/%s for %s
             Architecture: i386
-            """ % (
-                oci_project.pillar.name, oci_project.name, recipe.name,
-                recipe.owner.display_name),
-            self.getMainText(build))
+            """
+            % (
+                oci_project.pillar.name,
+                oci_project.name,
+                recipe.name,
+                recipe.owner.display_name,
+            ),
+            self.getMainText(build),
+        )
 
     def test_files(self):
         # OCIRecipeBuildView.files returns all the associated files.
@@ -97,7 +106,8 @@ class TestOCIRecipeBuildView(BrowserTestCase):
         build_view = create_initialized_view(build, "+index")
         self.assertEqual(
             [oci_file.library_file.filename],
-            [lfa.filename for lfa in build_view.files])
+            [lfa.filename for lfa in build_view.files],
+        )
         # Deleted files won't be included.
         self.assertFalse(oci_file.library_file.deleted)
         removeSecurityProxy(oci_file.library_file).content = None
@@ -109,11 +119,17 @@ class TestOCIRecipeBuildView(BrowserTestCase):
         build = self.factory.makeOCIRecipeBuild(status=BuildStatus.FULLYBUILT)
         getUtility(IOCIRegistryUploadJobSource).create(build)
         build_view = create_initialized_view(build, "+index")
-        self.assertThat(build_view(), soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "registry upload status", "li",
-                attrs={"id": "registry-upload-status"},
-                text=re.compile(r"^\s*Registry upload in progress\s*$"))))
+        self.assertThat(
+            build_view(),
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "registry upload status",
+                    "li",
+                    attrs={"id": "registry-upload-status"},
+                    text=re.compile(r"^\s*Registry upload in progress\s*$"),
+                )
+            ),
+        )
 
     def test_registry_upload_status_completed(self):
         build = self.factory.makeOCIRecipeBuild(status=BuildStatus.FULLYBUILT)
@@ -121,35 +137,50 @@ class TestOCIRecipeBuildView(BrowserTestCase):
         naked_job = removeSecurityProxy(job)
         naked_job.job._status = JobStatus.COMPLETED
         build_view = create_initialized_view(build, "+index")
-        self.assertThat(build_view(), soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "registry upload status", "li",
-                attrs={"id": "registry-upload-status"},
-                text=re.compile(r"^\s*Registry upload complete\s*$"))))
+        self.assertThat(
+            build_view(),
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "registry upload status",
+                    "li",
+                    attrs={"id": "registry-upload-status"},
+                    text=re.compile(r"^\s*Registry upload complete\s*$"),
+                )
+            ),
+        )
 
     def test_registry_upload_status_failed(self):
         build = self.factory.makeOCIRecipeBuild(status=BuildStatus.FULLYBUILT)
         job = getUtility(IOCIRegistryUploadJobSource).create(build)
         naked_job = removeSecurityProxy(job)
         naked_job.job._status = JobStatus.FAILED
-        naked_job.error_summary = (
-            "Upload of test-digest for test-image failed")
+        naked_job.error_summary = "Upload of test-digest for test-image failed"
         build_view = create_initialized_view(build, "+index")
-        self.assertThat(build_view(), soupmatchers.HTMLContains(
-            soupmatchers.Within(
-                soupmatchers.Tag(
-                    "registry upload status", "li",
-                    attrs={"id": "registry-upload-status"},
-                    text=re.compile(
-                        r"^\s*Registry upload failed:\s+"
-                        r"Upload of test-digest for test-image failed\s*$")),
-                soupmatchers.Tag(
-                    "retry button", "input",
-                    attrs={
-                        "type": "submit",
-                        "name": "field.actions.upload",
-                        "value": "Retry",
-                        }))))
+        self.assertThat(
+            build_view(),
+            soupmatchers.HTMLContains(
+                soupmatchers.Within(
+                    soupmatchers.Tag(
+                        "registry upload status",
+                        "li",
+                        attrs={"id": "registry-upload-status"},
+                        text=re.compile(
+                            r"^\s*Registry upload failed:\s+"
+                            r"Upload of test-digest for test-image failed\s*$"
+                        ),
+                    ),
+                    soupmatchers.Tag(
+                        "retry button",
+                        "input",
+                        attrs={
+                            "type": "submit",
+                            "name": "field.actions.upload",
+                            "value": "Retry",
+                        },
+                    ),
+                )
+            ),
+        )
 
 
 class TestOCIRecipeBuildOperations(BrowserTestCase):
@@ -159,12 +190,13 @@ class TestOCIRecipeBuildOperations(BrowserTestCase):
     def setUp(self):
         super().setUp()
         self.useFixture(FakeLogger())
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
         self.build = self.factory.makeOCIRecipeBuild()
         self.build_url = canonical_url(self.build)
         self.requester = self.build.requester
         self.buildd_admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).buildd_admin])
+            member_of=[getUtility(ILaunchpadCelebrities).buildd_admin]
+        )
 
     def test_retry_build(self):
         # The requester of a build can retry it.
@@ -185,10 +217,14 @@ class TestOCIRecipeBuildOperations(BrowserTestCase):
         user = self.factory.makePerson()
         browser = self.getViewBrowser(self.build, user=user)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Retry this build")
+            LinkNotFoundError, browser.getLink, "Retry this build"
+        )
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, self.build_url + "/+retry",
-            user=user)
+            Unauthorized,
+            self.getUserBrowser,
+            self.build_url + "/+retry",
+            user=user,
+        )
 
     def test_retry_build_wrong_state(self):
         # If the build isn't in an unsuccessful terminal state, you can't
@@ -196,7 +232,8 @@ class TestOCIRecipeBuildOperations(BrowserTestCase):
         self.build.updateStatus(BuildStatus.FULLYBUILT)
         browser = self.getViewBrowser(self.build, user=self.requester)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Retry this build")
+            LinkNotFoundError, browser.getLink, "Retry this build"
+        )
 
     def test_cancel_build(self):
         # The requester of a build can cancel it.
@@ -218,8 +255,11 @@ class TestOCIRecipeBuildOperations(BrowserTestCase):
         browser = self.getViewBrowser(self.build, user=user)
         self.assertRaises(LinkNotFoundError, browser.getLink, "Cancel build")
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, self.build_url + "/+cancel",
-            user=user)
+            Unauthorized,
+            self.getUserBrowser,
+            self.build_url + "/+cancel",
+            user=user,
+        )
 
     def test_cancel_build_wrong_state(self):
         # If the build isn't queued, you can't cancel it.
@@ -250,7 +290,8 @@ class TestOCIRecipeBuildOperations(BrowserTestCase):
         browser.getControl("Rescore build").click()
         self.assertEqual(
             "Invalid integer data",
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_rescore_build_not_admin(self):
         # A non-admin user cannot cancel a build.
@@ -260,8 +301,11 @@ class TestOCIRecipeBuildOperations(BrowserTestCase):
         browser = self.getViewBrowser(self.build, user=user)
         self.assertRaises(LinkNotFoundError, browser.getLink, "Rescore build")
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, self.build_url + "/+rescore",
-            user=user)
+            Unauthorized,
+            self.getUserBrowser,
+            self.build_url + "/+rescore",
+            user=user,
+        )
 
     def test_rescore_build_wrong_state(self):
         # If the build isn't NEEDSBUILD, you can't rescore it.
@@ -278,22 +322,32 @@ class TestOCIRecipeBuildOperations(BrowserTestCase):
         with person_logged_in(self.requester):
             self.build.cancel()
         browser = self.getViewBrowser(
-            self.build, "+rescore", user=self.buildd_admin)
+            self.build, "+rescore", user=self.buildd_admin
+        )
         self.assertEqual(self.build_url, browser.url)
-        self.assertThat(browser.contents, soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "notification", "div", attrs={"class": "warning message"},
-                text="Cannot rescore this build because it is not queued.")))
+        self.assertThat(
+            browser.contents,
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "notification",
+                    "div",
+                    attrs={"class": "warning message"},
+                    text="Cannot rescore this build because it is not queued.",
+                )
+            ),
+        )
 
     def test_builder_history(self):
         Store.of(self.build).flush()
         self.build.updateStatus(
-            BuildStatus.FULLYBUILT, builder=self.factory.makeBuilder())
+            BuildStatus.FULLYBUILT, builder=self.factory.makeBuilder()
+        )
         title = self.build.title
         browser = self.getViewBrowser(self.build.builder, "+history")
         self.assertTextMatchesExpressionIgnoreWhitespace(
             "Build history.*%s" % re.escape(title),
-            extract_text(find_main_content(browser.contents)))
+            extract_text(find_main_content(browser.contents)),
+        )
         self.assertEqual(self.build_url, browser.getLink(title).url)
 
     def makeBuildingOCIRecipe(self):
diff --git a/lib/lp/oci/browser/tests/test_ocirecipesubscription.py b/lib/lp/oci/browser/tests/test_ocirecipesubscription.py
index fbd60d6..34362cb 100644
--- a/lib/lp/oci/browser/tests/test_ocirecipesubscription.py
+++ b/lib/lp/oci/browser/tests/test_ocirecipesubscription.py
@@ -10,18 +10,14 @@ from lp.app.enums import InformationType
 from lp.oci.tests.helpers import OCIConfigHelperMixin
 from lp.registry.enums import BranchSharingPolicy
 from lp.services.webapp import canonical_url
-from lp.testing import (
-    admin_logged_in,
-    BrowserTestCase,
-    person_logged_in,
-    )
+from lp.testing import BrowserTestCase, admin_logged_in, person_logged_in
 from lp.testing.layers import DatabaseFunctionalLayer
 from lp.testing.pages import (
     extract_text,
     find_main_content,
     find_tag_by_id,
     find_tags_by_class,
-    )
+)
 
 
 class BaseTestOCIRecipeView(OCIConfigHelperMixin, BrowserTestCase):
@@ -32,55 +28,74 @@ class BaseTestOCIRecipeView(OCIConfigHelperMixin, BrowserTestCase):
         super().setUp()
         self.setConfig()
         self.useFixture(FakeLogger())
-        self.person = self.factory.makePerson(name='recipe-owner')
+        self.person = self.factory.makePerson(name="recipe-owner")
 
     def makeOCIRecipe(self, oci_project=None, **kwargs):
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="recipe-repository",
-            paths=["refs/heads/v1.0-20.04"])
+            owner=self.person,
+            target=self.person,
+            name="recipe-repository",
+            paths=["refs/heads/v1.0-20.04"],
+        )
         if oci_project is None:
             project = self.factory.makeProduct(
-                owner=self.person, registrant=self.person)
+                owner=self.person, registrant=self.person
+            )
             oci_project = self.factory.makeOCIProject(
-                registrant=self.person, pillar=project,
-                ociprojectname='my-oci-project')
+                registrant=self.person,
+                pillar=project,
+                ociprojectname="my-oci-project",
+            )
         return self.factory.makeOCIRecipe(
-            registrant=self.person, owner=self.person, name="recipe-name",
-            git_ref=ref, oci_project=oci_project, **kwargs)
+            registrant=self.person,
+            owner=self.person,
+            name="recipe-name",
+            git_ref=ref,
+            oci_project=oci_project,
+            **kwargs,
+        )
 
     def getSubscriptionPortletText(self, browser):
         return extract_text(
-            find_tag_by_id(browser.contents, 'portlet-subscribers'))
+            find_tag_by_id(browser.contents, "portlet-subscribers")
+        )
 
     def extractMainText(self, browser):
         return extract_text(find_main_content(browser.contents))
 
     def extractInfoMessageContent(self, browser):
         return extract_text(
-            find_tags_by_class(browser.contents, 'informational message')[0])
+            find_tags_by_class(browser.contents, "informational message")[0]
+        )
 
 
 class TestPublicOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
-
     def test_subscribe_self(self):
         recipe = self.makeOCIRecipe()
         another_user = self.factory.makePerson(name="another-user")
         browser = self.getViewBrowser(recipe, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe yourself
             Subscribe someone else
             Subscribers
             Recipe-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
         # Go to "subscribe myself" page, and click the button.
         browser = self.getViewBrowser(
-            recipe, view_name="+subscribe", user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+            recipe, view_name="+subscribe", user=another_user
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe to OCI recipe
             recipe-name
             Subscribe to OCI recipe or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Subscribe").click()
 
         # We should be redirected back to OCI page.
@@ -88,13 +103,16 @@ class TestPublicOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
             self.assertEqual(canonical_url(recipe), browser.url)
 
         # And the new user should be listed in the subscribers list.
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Another-user
             Recipe-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
     def test_unsubscribe_self(self):
         recipe = self.makeOCIRecipe()
@@ -103,15 +121,21 @@ class TestPublicOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
             recipe.subscribe(another_user, recipe.owner)
         subscription = recipe.getSubscription(another_user)
         browser = self.getViewBrowser(subscription, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit subscription to OCI recipe for Another-user
             If you unsubscribe from an OCI recipe it will no longer show up on
             your personal pages. or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Unsubscribe").click()
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Another-user has been unsubscribed from this OCI recipe.
-            """, self.extractInfoMessageContent(browser))
+            """,
+            self.extractInfoMessageContent(browser),
+        )
         with person_logged_in(self.person):
             self.assertIsNone(recipe.getSubscription(another_user))
 
@@ -119,25 +143,32 @@ class TestPublicOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
         recipe = self.makeOCIRecipe()
         another_user = self.factory.makePerson(name="another-user")
         browser = self.getViewBrowser(recipe, user=recipe.owner)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Recipe-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
         # Go to "subscribe" page, and click the button.
         browser = self.getViewBrowser(
-            recipe, view_name="+addsubscriber", user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+            recipe, view_name="+addsubscriber", user=another_user
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe to OCI recipe
             Person:
             .*
             The person subscribed to the related OCI recipe.
             or
             Cancel
-            """, self.extractMainText(browser))
-        browser.getControl(name="field.person").value = 'another-user'
+            """,
+            self.extractMainText(browser),
+        )
+        browser.getControl(name="field.person").value = "another-user"
         browser.getControl("Subscribe").click()
 
         # We should be redirected back to OCI recipe page.
@@ -145,13 +176,16 @@ class TestPublicOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
             self.assertEqual(canonical_url(recipe), browser.url)
 
         # And the new user should be listed in the subscribers list.
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Another-user
             Recipe-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
     def test_unsubscribe_someone_else(self):
         recipe = self.makeOCIRecipe()
@@ -161,45 +195,63 @@ class TestPublicOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
 
         subscription = recipe.getSubscription(another_user)
         browser = self.getViewBrowser(subscription, user=recipe.owner)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit subscription to OCI recipe for Another-user
             If you unsubscribe from an OCI recipe it will no longer show up on
             your personal pages. or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Unsubscribe").click()
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Another-user has been unsubscribed from this OCI recipe.
-            """, self.extractInfoMessageContent(browser))
+            """,
+            self.extractInfoMessageContent(browser),
+        )
         with person_logged_in(self.person):
             self.assertIsNone(recipe.getSubscription(another_user))
 
 
 class TestPrivateOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
-
     def makePrivateOCIRecipe(self, **kwargs):
         project = self.factory.makeProduct(
-            owner=self.person, registrant=self.person,
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         oci_project = self.factory.makeOCIProject(
-            ociprojectname='my-oci-project', pillar=project)
+            ociprojectname="my-oci-project", pillar=project
+        )
         return self.makeOCIRecipe(
             information_type=InformationType.PROPRIETARY,
-            oci_project=oci_project)
+            oci_project=oci_project,
+        )
 
     def test_cannot_subscribe_to_private_snap(self):
         recipe = self.makePrivateOCIRecipe()
         another_user = self.factory.makePerson(name="another-user")
         # Unsubscribed user should not see the OCI recipe page.
         self.assertRaises(
-            Unauthorized, self.getViewBrowser, recipe, user=another_user)
+            Unauthorized, self.getViewBrowser, recipe, user=another_user
+        )
         # Nor the subscribe pages.
         self.assertRaises(
-            Unauthorized, self.getViewBrowser,
-            recipe, view_name="+subscribe", user=another_user)
+            Unauthorized,
+            self.getViewBrowser,
+            recipe,
+            view_name="+subscribe",
+            user=another_user,
+        )
         self.assertRaises(
-            Unauthorized, self.getViewBrowser,
-            recipe, view_name="+addsubscriber", user=another_user)
+            Unauthorized,
+            self.getViewBrowser,
+            recipe,
+            view_name="+addsubscriber",
+            user=another_user,
+        )
 
     def test_recipe_owner_can_subscribe_someone_to_private_recipe(self):
         recipe = self.makePrivateOCIRecipe()
@@ -207,28 +259,35 @@ class TestPrivateOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
 
         # Go to "subscribe" page, and click the button.
         browser = self.getViewBrowser(
-            recipe, view_name="+addsubscriber", user=self.person)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+            recipe, view_name="+addsubscriber", user=self.person
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe to OCI recipe
             Person:
             .*
             The person subscribed to the related OCI recipe.
             or
             Cancel
-            """, self.extractMainText(browser))
-        browser.getControl(name="field.person").value = 'another-user'
+            """,
+            self.extractMainText(browser),
+        )
+        browser.getControl(name="field.person").value = "another-user"
         browser.getControl("Subscribe").click()
 
         # Now the new user should be listed in the subscribers list,
         # and have access to the recipe page.
         browser = self.getViewBrowser(recipe, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Another-user
             Recipe-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
     def test_unsubscribe_self(self):
         recipe = self.makePrivateOCIRecipe()
@@ -237,14 +296,20 @@ class TestPrivateOCIRecipeSubscriptionViews(BaseTestOCIRecipeView):
             recipe.subscribe(another_user, self.person)
             subscription = recipe.getSubscription(another_user)
         browser = self.getViewBrowser(subscription, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit subscription to OCI recipe for Another-user
             If you unsubscribe from an OCI  recipe it will no longer show up on
             your personal pages. or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Unsubscribe").click()
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Another-user has been unsubscribed from this OCI recipe.
-            """, self.extractInfoMessageContent(browser))
+            """,
+            self.extractInfoMessageContent(browser),
+        )
         with person_logged_in(self.person):
             self.assertIsNone(recipe.getSubscription(another_user))
diff --git a/lib/lp/oci/enums.py b/lib/lp/oci/enums.py
index c0cb164..b41c5a7 100644
--- a/lib/lp/oci/enums.py
+++ b/lib/lp/oci/enums.py
@@ -4,32 +4,35 @@
 """Enums for the OCI app."""
 
 __all__ = [
-    'OCIRecipeBuildRequestStatus',
-    ]
+    "OCIRecipeBuildRequestStatus",
+]
 
-from lazr.enum import (
-    EnumeratedType,
-    Item,
-    )
+from lazr.enum import EnumeratedType, Item
 
 
 class OCIRecipeBuildRequestStatus(EnumeratedType):
     """The status of a request to build an OCI recipe."""
 
-    PENDING = Item("""
+    PENDING = Item(
+        """
         Pending
 
         This OCI recipe build request is pending.
-        """)
+        """
+    )
 
-    FAILED = Item("""
+    FAILED = Item(
+        """
         Failed
 
         This OCI recipe build request failed.
-        """)
+        """
+    )
 
-    COMPLETED = Item("""
+    COMPLETED = Item(
+        """
         Completed
 
         This OCI recipe build request completed successfully.
-        """)
+        """
+    )
diff --git a/lib/lp/oci/interfaces/ocipushrule.py b/lib/lp/oci/interfaces/ocipushrule.py
index 9c800fa..8a90b2b 100644
--- a/lib/lp/oci/interfaces/ocipushrule.py
+++ b/lib/lp/oci/interfaces/ocipushrule.py
@@ -4,10 +4,10 @@
 """Interfaces for handling credentials for OCI registry actions."""
 
 __all__ = [
-    'IOCIPushRule',
-    'IOCIPushRuleSet',
-    'OCIPushRuleAlreadyExists',
-    ]
+    "IOCIPushRule",
+    "IOCIPushRuleSet",
+    "OCIPushRuleAlreadyExists",
+]
 
 import http.client
 
@@ -20,13 +20,10 @@ from lazr.restful.declarations import (
     mutator_for,
     operation_for_version,
     operation_parameters,
-    )
+)
 from lazr.restful.fields import Reference
 from zope.interface import Interface
-from zope.schema import (
-    Int,
-    TextLine,
-    )
+from zope.schema import Int, TextLine
 
 from lp import _
 from lp.oci.interfaces.ocirecipe import IOCIRecipe
@@ -36,13 +33,14 @@ from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentials
 @error_status(http.client.CONFLICT)
 class OCIPushRuleAlreadyExists(Exception):
     """A new OCIPushRuleAlreadyExists was added with the
-       same details as an existing one.
+    same details as an existing one.
     """
 
     def __init__(self):
         super().__init__(
             "A push rule already exists with the same URL, image name, "
-            "and credentials")
+            "and credentials"
+        )
 
 
 class IOCIPushRuleView(Interface):
@@ -52,18 +50,25 @@ class IOCIPushRuleView(Interface):
 
     id = Int(title=_("ID"), required=False, readonly=True)
 
-    registry_url = exported(TextLine(
-        title=_("Registry URL"),
-        description=_(
-            "The registry URL for the credentials of this push rule"),
-        required=True,
-        readonly=True))
+    registry_url = exported(
+        TextLine(
+            title=_("Registry URL"),
+            description=_(
+                "The registry URL for the credentials of this push rule"
+            ),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    username = exported(TextLine(
-        title=_("Username"),
-        description=_("The username for the credentials, if available."),
-        required=True,
-        readonly=True))
+    username = exported(
+        TextLine(
+            title=_("Username"),
+            description=_("The username for the credentials, if available."),
+            required=True,
+            readonly=True,
+        )
+    )
 
 
 class IOCIPushRuleEditableAttributes(Interface):
@@ -77,24 +82,30 @@ class IOCIPushRuleEditableAttributes(Interface):
         title=_("OCI recipe"),
         description=_("The recipe for which the rule is defined."),
         required=True,
-        readonly=False)
+        readonly=False,
+    )
 
     registry_credentials = Reference(
         IOCIRegistryCredentials,
         title=_("Registry credentials"),
         description=_("The registry credentials to use."),
         required=True,
-        readonly=False)
+        readonly=False,
+    )
 
-    image_name = exported(TextLine(
-        title=_("Image name"),
-        description=_("The intended name of the image on the registry."),
-        required=True,
-        readonly=True))
+    image_name = exported(
+        TextLine(
+            title=_("Image name"),
+            description=_("The intended name of the image on the registry."),
+            required=True,
+            readonly=True,
+        )
+    )
 
     @mutator_for(image_name)
     @operation_parameters(
-        image_name=TextLine(title=_("Image name"), required=True))
+        image_name=TextLine(title=_("Image name"), required=True)
+    )
     @export_write_operation()
     @operation_for_version("devel")
     def setNewImageName(image_name):
@@ -113,9 +124,11 @@ class IOCIPushRuleEdit(Interface):
 
 
 @exported_as_webservice_entry(
-    publish_web_link=True, as_of="devel", singular_name="oci_push_rule")
-class IOCIPushRule(IOCIPushRuleEdit, IOCIPushRuleEditableAttributes,
-                   IOCIPushRuleView):
+    publish_web_link=True, as_of="devel", singular_name="oci_push_rule"
+)
+class IOCIPushRule(
+    IOCIPushRuleEdit, IOCIPushRuleEditableAttributes, IOCIPushRuleView
+):
     """A rule for pushing builds of an OCI recipe to a registry."""
 
 
diff --git a/lib/lp/oci/interfaces/ocirecipe.py b/lib/lp/oci/interfaces/ocirecipe.py
index 2a7256c..516a0c3 100644
--- a/lib/lp/oci/interfaces/ocirecipe.py
+++ b/lib/lp/oci/interfaces/ocirecipe.py
@@ -4,31 +4,32 @@
 """Interfaces related to recipes for OCI Images."""
 
 __all__ = [
-    'CannotModifyOCIRecipeProcessor',
-    'DuplicateOCIRecipeName',
-    'IOCIRecipe',
-    'IOCIRecipeBuildRequest',
-    'IOCIRecipeEdit',
-    'IOCIRecipeEditableAttributes',
-    'IOCIRecipeSet',
-    'IOCIRecipeView',
-    'NoSourceForOCIRecipe',
-    'NoSuchOCIRecipe',
-    'OCIRecipeBuildAlreadyPending',
-    'OCIRecipeFeatureDisabled',
-    'OCIRecipeBranchHasInvalidFormat',
-    'OCIRecipeNotOwner',
-    'OCIRecipePrivacyMismatch',
-    'OCI_RECIPE_ALLOW_CREATE',
-    'OCI_RECIPE_BUILD_DISTRIBUTION',
-    'OCI_RECIPE_WEBHOOKS_FEATURE_FLAG',
-    'UsingDistributionCredentials',
-    ]
+    "CannotModifyOCIRecipeProcessor",
+    "DuplicateOCIRecipeName",
+    "IOCIRecipe",
+    "IOCIRecipeBuildRequest",
+    "IOCIRecipeEdit",
+    "IOCIRecipeEditableAttributes",
+    "IOCIRecipeSet",
+    "IOCIRecipeView",
+    "NoSourceForOCIRecipe",
+    "NoSuchOCIRecipe",
+    "OCIRecipeBuildAlreadyPending",
+    "OCIRecipeFeatureDisabled",
+    "OCIRecipeBranchHasInvalidFormat",
+    "OCIRecipeNotOwner",
+    "OCIRecipePrivacyMismatch",
+    "OCI_RECIPE_ALLOW_CREATE",
+    "OCI_RECIPE_BUILD_DISTRIBUTION",
+    "OCI_RECIPE_WEBHOOKS_FEATURE_FLAG",
+    "UsingDistributionCredentials",
+]
 
 import http.client
 
 from lazr.lifecycle.snapshot import doNotSnapshot
 from lazr.restful.declarations import (
+    REQUEST_USER,
     call_with,
     error_status,
     export_factory_operation,
@@ -37,17 +38,9 @@ from lazr.restful.declarations import (
     exported_as_webservice_entry,
     operation_for_version,
     operation_parameters,
-    REQUEST_USER,
-    )
-from lazr.restful.fields import (
-    CollectionField,
-    Reference,
-    ReferenceChoice,
-    )
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
+)
+from lazr.restful.fields import CollectionField, Reference, ReferenceChoice
+from zope.interface import Attribute, Interface
 from zope.schema import (
     Bool,
     Choice,
@@ -58,7 +51,7 @@ from zope.schema import (
     Set,
     Text,
     TextLine,
-    )
+)
 from zope.security.interfaces import Unauthorized
 
 from lp import _
@@ -76,16 +69,12 @@ from lp.registry.interfaces.ociproject import IOCIProject
 from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.role import IHasOwner
 from lp.services.database.constants import DEFAULT
-from lp.services.fields import (
-    PersonChoice,
-    PublicPersonChoice,
-    )
+from lp.services.fields import PersonChoice, PublicPersonChoice
 from lp.services.webhooks.interfaces import IWebhookTarget
 
-
 OCI_RECIPE_WEBHOOKS_FEATURE_FLAG = "oci.recipe.webhooks.enabled"
-OCI_RECIPE_ALLOW_CREATE = 'oci.recipe.create.enabled'
-OCI_RECIPE_BUILD_DISTRIBUTION = 'oci.default_build_distribution'
+OCI_RECIPE_ALLOW_CREATE = "oci.recipe.create.enabled"
+OCI_RECIPE_BUILD_DISTRIBUTION = "oci.default_build_distribution"
 OCI_RECIPE_PRIVATE_FEATURE_FLAG = "oci.recipe.allow_private"
 
 
@@ -95,7 +84,8 @@ class OCIRecipeFeatureDisabled(Unauthorized):
 
     def __init__(self):
         super().__init__(
-            "You do not have permission to create new OCI recipes.")
+            "You do not have permission to create new OCI recipes."
+        )
 
 
 @error_status(http.client.UNAUTHORIZED)
@@ -109,7 +99,8 @@ class OCIRecipeBuildAlreadyPending(Exception):
 
     def __init__(self):
         super().__init__(
-            "An identical build of this OCI recipe is already pending.")
+            "An identical build of this OCI recipe is already pending."
+        )
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -119,6 +110,7 @@ class DuplicateOCIRecipeName(Exception):
 
 class NoSuchOCIRecipe(NameLookupFailed):
     """The requested OCI Recipe does not exist."""
+
     _message_prefix = "No such OCI recipe exists for this OCI project"
 
 
@@ -128,7 +120,8 @@ class UsingDistributionCredentials(Exception):
 
     def __init__(self):
         super().__init__(
-            "The OCI recipe is in a distribution that has credentials set.")
+            "The OCI recipe is in a distribution that has credentials set."
+        )
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -137,7 +130,8 @@ class NoSourceForOCIRecipe(Exception):
 
     def __init__(self):
         super().__init__(
-            "New OCI recipes must have a git branch and build file.")
+            "New OCI recipes must have a git branch and build file."
+        )
 
 
 @error_status(http.client.FORBIDDEN)
@@ -145,11 +139,12 @@ class CannotModifyOCIRecipeProcessor(Exception):
     """Tried to enable or disable a restricted processor on an OCI recipe."""
 
     _fmt = (
-        '%(processor)s is restricted, and may only be enabled or disabled '
-        'by administrators.')
+        "%(processor)s is restricted, and may only be enabled or disabled "
+        "by administrators."
+    )
 
     def __init__(self, processor):
-        super().__init__(self._fmt % {'processor': processor.name})
+        super().__init__(self._fmt % {"processor": processor.name})
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -158,8 +153,9 @@ class OCIRecipePrivacyMismatch(Exception):
 
     def __init__(self, message=None):
         super().__init__(
-            message or
-            "OCI recipe contains private information and cannot be public.")
+            message
+            or "OCI recipe contains private information and cannot be public."
+        )
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -169,50 +165,86 @@ class OCIRecipeBranchHasInvalidFormat(Exception):
     def __init__(self):
         super().__init__(
             "The branch name for the OCI recipe does not "
-            "match the APPVERSION-UBUNTUVERSION format (ex. v1.0-20.04)")
+            "match the APPVERSION-UBUNTUVERSION format (ex. v1.0-20.04)"
+        )
+
 
 @exported_as_webservice_entry(
-    publish_web_link=True, as_of="devel",
-    singular_name="oci_recipe_build_request")
+    publish_web_link=True,
+    as_of="devel",
+    singular_name="oci_recipe_build_request",
+)
 class IOCIRecipeBuildRequest(Interface):
     """A request to build an OCI Recipe."""
 
     id = Int(title=_("ID"), required=True, readonly=True)
 
-    date_requested = exported(Datetime(
-        title=_("The time when this request was made"),
-        required=True, readonly=True))
+    date_requested = exported(
+        Datetime(
+            title=_("The time when this request was made"),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    date_finished = exported(Datetime(
-        title=_("The time when this request finished"),
-        required=False, readonly=True))
+    date_finished = exported(
+        Datetime(
+            title=_("The time when this request finished"),
+            required=False,
+            readonly=True,
+        )
+    )
 
-    recipe = exported(Reference(
-        # Really IOCIRecipe, patched in lp.oci.interfaces.webservice.
-        Interface,
-        title=_("OCI Recipe"), required=True, readonly=True))
+    recipe = exported(
+        Reference(
+            # Really IOCIRecipe, patched in lp.oci.interfaces.webservice.
+            Interface,
+            title=_("OCI Recipe"),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    status = exported(Choice(
-        title=_("Status"), vocabulary=OCIRecipeBuildRequestStatus,
-        required=True, readonly=True))
+    status = exported(
+        Choice(
+            title=_("Status"),
+            vocabulary=OCIRecipeBuildRequestStatus,
+            required=True,
+            readonly=True,
+        )
+    )
 
-    error_message = exported(TextLine(
-        title=_("Error message"), required=False, readonly=True))
+    error_message = exported(
+        TextLine(title=_("Error message"), required=False, readonly=True)
+    )
 
-    builds = exported(doNotSnapshot(CollectionField(
-        title=_("Builds produced by this request"),
-        # Really IOCIRecipeBuild, patched in lp.oci.interfaces.webservice.
-        value_type=Reference(schema=Interface),
-        required=True, readonly=True)))
+    builds = exported(
+        doNotSnapshot(
+            CollectionField(
+                title=_("Builds produced by this request"),
+                # Really IOCIRecipeBuild, patched in
+                # lp.oci.interfaces.webservice.
+                value_type=Reference(schema=Interface),
+                required=True,
+                readonly=True,
+            )
+        )
+    )
 
     architectures = Set(
         title=_("If set, limit builds to these architecture tags."),
-        value_type=TextLine(), required=False, readonly=True)
+        value_type=TextLine(),
+        required=False,
+        readonly=True,
+    )
 
     uploaded_manifests = Dict(
         title=_("A dict of manifest information per build."),
-        key_type=Int(), value_type=Dict(),
-        required=False, readonly=True)
+        key_type=Int(),
+        value_type=Dict(),
+        required=False,
+        readonly=True,
+    )
 
     def addUploadedManifest(build_id, manifest_info):
         """Add the manifest information for one of the builds in this
@@ -224,51 +256,69 @@ class IOCIRecipeView(Interface):
     """`IOCIRecipe` attributes that require launchpad.View permission."""
 
     id = Int(title=_("ID"), required=True, readonly=True)
-    date_created = exported(Datetime(
-        title=_("Date created"), required=True, readonly=True))
-    date_last_modified = exported(Datetime(
-        title=_("Date last modified"), required=True, readonly=True))
+    date_created = exported(
+        Datetime(title=_("Date created"), required=True, readonly=True)
+    )
+    date_last_modified = exported(
+        Datetime(title=_("Date last modified"), required=True, readonly=True)
+    )
 
-    registrant = exported(PublicPersonChoice(
-        title=_("Registrant"),
-        description=_("The user who registered this recipe."),
-        vocabulary='ValidPersonOrTeam', required=True, readonly=True))
+    registrant = exported(
+        PublicPersonChoice(
+            title=_("Registrant"),
+            description=_("The user who registered this recipe."),
+            vocabulary="ValidPersonOrTeam",
+            required=True,
+            readonly=True,
+        )
+    )
 
     distribution = Reference(
-        IDistribution, title=_("Distribution"),
-        required=True, readonly=True,
-        description=_("The distribution that this recipe is associated with."))
+        IDistribution,
+        title=_("Distribution"),
+        required=True,
+        readonly=True,
+        description=_("The distribution that this recipe is associated with."),
+    )
 
     distro_series = Reference(
-        IDistroSeries, title=_("Distro series"),
-        required=True, readonly=True,
-        description=_("The series for which the recipe should be built."))
+        IDistroSeries,
+        title=_("Distro series"),
+        required=True,
+        readonly=True,
+        description=_("The series for which the recipe should be built."),
+    )
 
     available_processors = Attribute(
         "The architectures that are available to be enabled or disabled for "
-        "this OCI recipe.")
+        "this OCI recipe."
+    )
 
     pending_build_requests = Attribute(
-        "The list of build requests that didn't trigger builds yet.")
+        "The list of build requests that didn't trigger builds yet."
+    )
 
     # This should only be set by using IOCIProject.setOfficialRecipe
     official = Bool(
         title=_("OCI project official"),
         required=False,
         description=_("True if this recipe is official for its OCI project."),
-        readonly=True)
+        readonly=True,
+    )
 
     private = Bool(
         title=_("Is this OCI recipe private?"),
-        required=True, readonly=True,
-        description=_("True if this recipe is private. False otherwise."))
+        required=True,
+        readonly=True,
+        description=_("True if this recipe is private. False otherwise."),
+    )
 
-    pillar = Attribute('The pillar of this OCI recipe.')
+    pillar = Attribute("The pillar of this OCI recipe.")
 
     @call_with(check_permissions=True, user=REQUEST_USER)
     @operation_parameters(
-        processors=List(
-            value_type=Reference(schema=IProcessor), required=True))
+        processors=List(value_type=Reference(schema=IProcessor), required=True)
+    )
     @export_write_operation()
     @operation_for_version("devel")
     def setProcessors(processors, check_permissions=False, user=None):
@@ -284,66 +334,101 @@ class IOCIRecipeView(Interface):
         title=_("Completed builds of this OCI recipe."),
         description=_(
             "Completed builds of this OCI recipe, sorted in descending "
-            "order of finishing."),
+            "order of finishing."
+        ),
         # Really IOCIRecipeBuild, patched in _schema_circular_imports.
         value_type=Reference(schema=Interface),
-        required=True, readonly=True)
+        required=True,
+        readonly=True,
+    )
 
     completed_builds = CollectionField(
         title=_("Completed builds of this OCI recipe."),
         description=_(
             "Completed builds of this OCI recipe, sorted in descending "
-            "order of finishing."),
+            "order of finishing."
+        ),
         # Really IOCIRecipeBuild, patched in _schema_circular_imports.
-        value_type=Reference(schema=Interface), readonly=True)
+        value_type=Reference(schema=Interface),
+        readonly=True,
+    )
 
     completed_builds_without_build_request = CollectionField(
         title=_("Completed builds of this OCI recipe."),
         description=_(
             "Completed builds of this OCI recipe, sorted in descending "
             "order of finishing that do no have a corresponding "
-            "build request"),
+            "build request"
+        ),
         # Really IOCIRecipeBuild, patched in _schema_circular_imports.
-        value_type=Reference(schema=Interface), readonly=True)
+        value_type=Reference(schema=Interface),
+        readonly=True,
+    )
 
     pending_builds = CollectionField(
         title=_("Pending builds of this OCI recipe."),
         description=_(
             "Pending builds of this OCI recipe, sorted in descending "
-            "order of creation."),
+            "order of creation."
+        ),
         # Really IOCIRecipeBuild, patched in _schema_circular_imports.
-        value_type=Reference(schema=Interface), readonly=True)
+        value_type=Reference(schema=Interface),
+        readonly=True,
+    )
 
-    push_rules = exported(CollectionField(
-        title=_("Push rules for this OCI recipe."),
-        description=_("All of the push rules for registry upload "
-                      "that apply to this recipe."),
-        # Really IOCIPushRule, patched in _schema_cirular_imports.
-        value_type=Reference(schema=Interface), readonly=True))
+    push_rules = exported(
+        CollectionField(
+            title=_("Push rules for this OCI recipe."),
+            description=_(
+                "All of the push rules for registry upload "
+                "that apply to this recipe."
+            ),
+            # Really IOCIPushRule, patched in _schema_cirular_imports.
+            value_type=Reference(schema=Interface),
+            readonly=True,
+        )
+    )
 
     can_upload_to_registry = Bool(
-        title=_("Can upload to registry"), required=True, readonly=True,
+        title=_("Can upload to registry"),
+        required=True,
+        readonly=True,
         description=_(
             "Whether everything is set up to allow uploading builds of "
-            "this OCI recipe to a registry."))
+            "this OCI recipe to a registry."
+        ),
+    )
 
     is_valid_branch_format = Bool(
-        title=_("Is valid branch format"), required=True, readonly=True,
-        description=_("Whether the git branch name is the correct "
-                      "format for using as a tag name."))
+        title=_("Is valid branch format"),
+        required=True,
+        readonly=True,
+        description=_(
+            "Whether the git branch name is the correct "
+            "format for using as a tag name."
+        ),
+    )
 
     use_distribution_credentials = Bool(
-        title=_("Use Distribution credentials"), required=True, readonly=True,
-        description=_("Use the credentials on a Distribution for "
-                      "registry upload"))
+        title=_("Use Distribution credentials"),
+        required=True,
+        readonly=True,
+        description=_(
+            "Use the credentials on a Distribution for " "registry upload"
+        ),
+    )
 
     subscriptions = CollectionField(
         title=_("OCIRecipeSubscriptions associated with this OCI recipe."),
-        readonly=True, value_type=Reference(Interface))
+        readonly=True,
+        value_type=Reference(Interface),
+    )
 
     subscribers = CollectionField(
         title=_("Persons subscribed to this snap recipe."),
-        readonly=True, value_type=Reference(IPerson))
+        readonly=True,
+        value_type=Reference(IPerson),
+    )
 
     def requestBuild(requester, architecture):
         """Request that the OCI recipe is built.
@@ -377,8 +462,7 @@ class IOCIRecipeView(Interface):
         """
 
     def getBuildRequest(job_id):
-        """Get an OCIRecipeBuildRequest object for the given job_id.
-        """
+        """Get an OCIRecipeBuildRequest object for the given job_id."""
 
     def getAllowedInformationTypes(user):
         """Get a list of acceptable `InformationType`s for this OCI recipe.
@@ -413,25 +497,36 @@ class IOCIRecipeEdit(IWebhookTarget):
         registry_url=TextLine(
             title=_("Registry URL"),
             description=_("URL for the target registry"),
-            required=True),
+            required=True,
+        ),
         image_name=TextLine(
             title=_("Image name"),
             description=_("Name of the image to push to on the registry"),
-            required=True),
+            required=True,
+        ),
         credentials=Dict(
             title=_("Registry credentials"),
             description=_(
-                "The credentials to use in pushing the image to the registry"),
-            required=True),
+                "The credentials to use in pushing the image to the registry"
+            ),
+            required=True,
+        ),
         credentials_owner=PersonChoice(
             title=_("Registry credentials owner"),
             required=False,
-            vocabulary="AllUserTeamsParticipationPlusSelf"))
+            vocabulary="AllUserTeamsParticipationPlusSelf",
+        ),
+    )
     # Really IOCIPushRule, patched in lp.oci.interfaces.webservice.
     @export_factory_operation(Interface, [])
     @operation_for_version("devel")
-    def newPushRule(registrant, registry_url, image_name, credentials,
-                    credentials_owner=None):
+    def newPushRule(
+        registrant,
+        registry_url,
+        image_name,
+        credentials,
+        credentials_owner=None,
+    ):
         """Add a new rule for pushing builds of this recipe to a registry."""
 
 
@@ -441,98 +536,155 @@ class IOCIRecipeEditableAttributes(IHasOwner):
     These attributes need launchpad.View to see, and launchpad.Edit to change.
     """
 
-    name = exported(TextLine(
-        title=_("Name"),
-        description=_("The name of this recipe."),
-        constraint=name_validator,
-        required=True,
-        readonly=False))
+    name = exported(
+        TextLine(
+            title=_("Name"),
+            description=_("The name of this recipe."),
+            constraint=name_validator,
+            required=True,
+            readonly=False,
+        )
+    )
 
-    owner = exported(PersonChoice(
-        title=_("Owner"),
-        required=True,
-        vocabulary="AllUserTeamsParticipationPlusSelf",
-        description=_("The owner of this OCI recipe."),
-        readonly=False))
+    owner = exported(
+        PersonChoice(
+            title=_("Owner"),
+            required=True,
+            vocabulary="AllUserTeamsParticipationPlusSelf",
+            description=_("The owner of this OCI recipe."),
+            readonly=False,
+        )
+    )
 
-    information_type = exported(Choice(
-        title=_("Information type"), vocabulary=InformationType,
-        required=True, readonly=False, default=InformationType.PUBLIC,
-        description=_(
-            "The type of information contained in this OCI recipe.")))
+    information_type = exported(
+        Choice(
+            title=_("Information type"),
+            vocabulary=InformationType,
+            required=True,
+            readonly=False,
+            default=InformationType.PUBLIC,
+            description=_(
+                "The type of information contained in this OCI recipe."
+            ),
+        )
+    )
 
-    oci_project = exported(Reference(
-        IOCIProject,
-        title=_("OCI project"),
-        description=_("The OCI project that this recipe is for."),
-        required=True,
-        readonly=True))
+    oci_project = exported(
+        Reference(
+            IOCIProject,
+            title=_("OCI project"),
+            description=_("The OCI project that this recipe is for."),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    git_ref = exported(Reference(
-        IGitRef, title=_("Git branch"), required=True, readonly=False,
-        description=_(
-            "The Git branch containing a Dockerfile at the location "
-            "defined by the build_file attribute.")))
+    git_ref = exported(
+        Reference(
+            IGitRef,
+            title=_("Git branch"),
+            required=True,
+            readonly=False,
+            description=_(
+                "The Git branch containing a Dockerfile at the location "
+                "defined by the build_file attribute."
+            ),
+        )
+    )
 
     git_repository = ReferenceChoice(
         title=_("Git repository"),
-        schema=IGitRepository, vocabulary="GitRepository",
-        required=False, readonly=False,
+        schema=IGitRepository,
+        vocabulary="GitRepository",
+        required=False,
+        readonly=False,
         description=_(
             "A Git repository with a branch containing a Dockerfile "
-            "at the location defined by the build_file attribute."))
+            "at the location defined by the build_file attribute."
+        ),
+    )
 
     git_path = TextLine(
-        title=_("Git branch path"), required=True, readonly=False,
+        title=_("Git branch path"),
+        required=True,
+        readonly=False,
         description=_(
             "The path of the Git branch containing a Dockerfile "
-            "at the location defined by the build_file attribute."))
+            "at the location defined by the build_file attribute."
+        ),
+    )
 
-    description = exported(Text(
-        title=_("Description"),
-        description=_("A short description of this recipe."),
-        required=False,
-        readonly=False))
+    description = exported(
+        Text(
+            title=_("Description"),
+            description=_("A short description of this recipe."),
+            required=False,
+            readonly=False,
+        )
+    )
 
-    build_file = exported(TextLine(
-        title=_("Build file path"),
-        description=_("The relative path to the file within this recipe's "
-                      "branch that defines how to build the recipe."),
-        constraint=path_does_not_escape,
-        required=True,
-        readonly=False))
-
-    build_args = exported(Dict(
-        title=_("Build ARG variables"),
-        description=_("The dictionary of ARG variables to be used when "
-                      "building this recipe."),
-        key_type=TextLine(title=_("ARG name")),
-        value_type=TextLine(title=_("ARG value")),
-        required=False,
-        readonly=False))
+    build_file = exported(
+        TextLine(
+            title=_("Build file path"),
+            description=_(
+                "The relative path to the file within this recipe's "
+                "branch that defines how to build the recipe."
+            ),
+            constraint=path_does_not_escape,
+            required=True,
+            readonly=False,
+        )
+    )
 
-    build_path = exported(TextLine(
-        title=_("Build directory context"),
-        description=_("Directory to use for build context "
-                      "and OCIRecipe.build_file location."),
-        constraint=path_does_not_escape,
-        required=True,
-        readonly=False))
+    build_args = exported(
+        Dict(
+            title=_("Build ARG variables"),
+            description=_(
+                "The dictionary of ARG variables to be used when "
+                "building this recipe."
+            ),
+            key_type=TextLine(title=_("ARG name")),
+            value_type=TextLine(title=_("ARG value")),
+            required=False,
+            readonly=False,
+        )
+    )
 
-    build_daily = exported(Bool(
-        title=_("Build daily"),
-        required=True,
-        default=False,
-        description=_("If True, this recipe should be built daily."),
-        readonly=False))
-
-    image_name = exported(TextLine(
-        title=_("Image name"),
-        description=_("Image name to use on upload to registry. "
-                      "Defaults to recipe name if not set. "
-                      "Only used when Distribution credentials are set."),
-        required=False,
-        readonly=False))
+    build_path = exported(
+        TextLine(
+            title=_("Build directory context"),
+            description=_(
+                "Directory to use for build context "
+                "and OCIRecipe.build_file location."
+            ),
+            constraint=path_does_not_escape,
+            required=True,
+            readonly=False,
+        )
+    )
+
+    build_daily = exported(
+        Bool(
+            title=_("Build daily"),
+            required=True,
+            default=False,
+            description=_("If True, this recipe should be built daily."),
+            readonly=False,
+        )
+    )
+
+    image_name = exported(
+        TextLine(
+            title=_("Image name"),
+            description=_(
+                "Image name to use on upload to registry. "
+                "Defaults to recipe name if not set. "
+                "Only used when Distribution credentials are set."
+            ),
+            required=False,
+            readonly=False,
+        )
+    )
 
 
 class IOCIRecipeAdminAttributes(Interface):
@@ -542,39 +694,68 @@ class IOCIRecipeAdminAttributes(Interface):
     """
 
     require_virtualized = Bool(
-        title=_("Require virtualized builders"), required=True, readonly=False,
-        description=_("Only build this OCI recipe on virtual builders."))
+        title=_("Require virtualized builders"),
+        required=True,
+        readonly=False,
+        description=_("Only build this OCI recipe on virtual builders."),
+    )
 
-    processors = exported(CollectionField(
-        title=_("Processors"),
-        description=_(
-            "The architectures for which the OCI recipe should be built."),
-        value_type=Reference(schema=IProcessor),
-        readonly=False))
+    processors = exported(
+        CollectionField(
+            title=_("Processors"),
+            description=_(
+                "The architectures for which the OCI recipe should be built."
+            ),
+            value_type=Reference(schema=IProcessor),
+            readonly=False,
+        )
+    )
 
-    allow_internet = exported(Bool(
-        title=_("Allow external network access"),
-        required=True, readonly=False,
-        description=_(
-            "Allow access to external network resources via a proxy.  "
-            "Resources hosted on Launchpad itself are always allowed.")))
+    allow_internet = exported(
+        Bool(
+            title=_("Allow external network access"),
+            required=True,
+            readonly=False,
+            description=_(
+                "Allow access to external network resources via a proxy.  "
+                "Resources hosted on Launchpad itself are always allowed."
+            ),
+        )
+    )
 
 
 @exported_as_webservice_entry(
-    publish_web_link=True, as_of="devel", singular_name="oci_recipe")
-class IOCIRecipe(IOCIRecipeView, IOCIRecipeEdit, IOCIRecipeEditableAttributes,
-                 IOCIRecipeAdminAttributes):
+    publish_web_link=True, as_of="devel", singular_name="oci_recipe"
+)
+class IOCIRecipe(
+    IOCIRecipeView,
+    IOCIRecipeEdit,
+    IOCIRecipeEditableAttributes,
+    IOCIRecipeAdminAttributes,
+):
     """A recipe for building Open Container Initiative images."""
 
 
 class IOCIRecipeSet(Interface):
     """A utility to create and access OCI Recipes."""
 
-    def new(name, registrant, owner, oci_project, git_ref, build_file,
-            description=None, official=False, require_virtualized=True,
-            build_daily=False, processors=None, date_created=DEFAULT,
-            allow_internet=True, build_args=None,
-            information_type=InformationType.PUBLIC):
+    def new(
+        name,
+        registrant,
+        owner,
+        oci_project,
+        git_ref,
+        build_file,
+        description=None,
+        official=False,
+        require_virtualized=True,
+        build_daily=False,
+        processors=None,
+        date_created=DEFAULT,
+        allow_internet=True,
+        build_args=None,
+        information_type=InformationType.PUBLIC,
+    ):
         """Create an IOCIRecipe."""
 
     def exists(owner, oci_project, name):
diff --git a/lib/lp/oci/interfaces/ocirecipebuild.py b/lib/lp/oci/interfaces/ocirecipebuild.py
index a43082b..7f9c71a 100644
--- a/lib/lp/oci/interfaces/ocirecipebuild.py
+++ b/lib/lp/oci/interfaces/ocirecipebuild.py
@@ -4,21 +4,18 @@
 """Interfaces for a build record for OCI recipes."""
 
 __all__ = [
-    'CannotScheduleRegistryUpload',
-    'IOCIFile',
-    'IOCIFileSet',
-    'IOCIRecipeBuild',
-    'IOCIRecipeBuildSet',
-    'OCIRecipeBuildRegistryUploadStatus',
-    'OCIRecipeBuildSetRegistryUploadStatus',
-    ]
+    "CannotScheduleRegistryUpload",
+    "IOCIFile",
+    "IOCIFileSet",
+    "IOCIRecipeBuild",
+    "IOCIRecipeBuildSet",
+    "OCIRecipeBuildRegistryUploadStatus",
+    "OCIRecipeBuildSetRegistryUploadStatus",
+]
 
 import http.client
 
-from lazr.enum import (
-    EnumeratedType,
-    Item,
-    )
+from lazr.enum import EnumeratedType, Item
 from lazr.restful.declarations import (
     error_status,
     export_read_operation,
@@ -26,24 +23,10 @@ from lazr.restful.declarations import (
     exported,
     exported_as_webservice_entry,
     operation_for_version,
-    )
-from lazr.restful.fields import (
-    CollectionField,
-    Reference,
-    )
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
-from zope.schema import (
-    Bool,
-    Choice,
-    Datetime,
-    Dict,
-    Int,
-    List,
-    TextLine,
-    )
+)
+from lazr.restful.fields import CollectionField, Reference
+from zope.interface import Attribute, Interface
+from zope.schema import Bool, Choice, Datetime, Dict, Int, List, TextLine
 
 from lp import _
 from lp.app.interfaces.launchpad import IPrivacy
@@ -51,15 +34,12 @@ from lp.buildmaster.interfaces.buildfarmjob import (
     IBuildFarmJobAdmin,
     IBuildFarmJobEdit,
     ISpecificBuildFarmJobSource,
-    )
+)
 from lp.buildmaster.interfaces.packagebuild import (
     IPackageBuild,
     IPackageBuildView,
-    )
-from lp.oci.interfaces.ocirecipe import (
-    IOCIRecipe,
-    IOCIRecipeBuildRequest,
-    )
+)
+from lp.oci.interfaces.ocirecipe import IOCIRecipe, IOCIRecipeBuildRequest
 from lp.services.database.constants import DEFAULT
 from lp.services.fields import PublicPersonChoice
 from lp.services.librarian.interfaces import ILibraryFileAlias
@@ -78,36 +58,46 @@ class OCIRecipeBuildRegistryUploadStatus(EnumeratedType):
     that process.
     """
 
-    UNSCHEDULED = Item("""
+    UNSCHEDULED = Item(
+        """
         Unscheduled
 
         No upload of this OCI build to a registry is scheduled.
-        """)
+        """
+    )
 
-    PENDING = Item("""
+    PENDING = Item(
+        """
         Pending
 
         This OCI build is queued for upload to a registry.
-        """)
+        """
+    )
 
-    FAILEDTOUPLOAD = Item("""
+    FAILEDTOUPLOAD = Item(
+        """
         Failed to upload
 
         The last attempt to upload this OCI build to a registry failed.
-        """)
+        """
+    )
 
-    UPLOADED = Item("""
+    UPLOADED = Item(
+        """
         Uploaded
 
         This OCI build was successfully uploaded to a registry.
-        """)
+        """
+    )
 
-    SUPERSEDED = Item("""
+    SUPERSEDED = Item(
+        """
         Superseded
 
         The upload has been cancelled because another build will upload a
         more recent version.
-    """)
+    """
+    )
 
 
 class OCIRecipeBuildSetRegistryUploadStatus(EnumeratedType):
@@ -117,35 +107,45 @@ class OCIRecipeBuildSetRegistryUploadStatus(EnumeratedType):
     that process.
     """
 
-    UNSCHEDULED = Item("""
+    UNSCHEDULED = Item(
+        """
         Unscheduled
 
         No upload of these OCI builds to a registry is scheduled.
-        """)
+        """
+    )
 
-    PENDING = Item("""
+    PENDING = Item(
+        """
         Pending
 
         These OCI builds are queued for upload to a registry.
-        """)
+        """
+    )
 
-    FAILEDTOUPLOAD = Item("""
+    FAILEDTOUPLOAD = Item(
+        """
         Failed to upload
 
         The last attempt to upload these OCI builds to a registry failed.
-        """)
+        """
+    )
 
-    UPLOADED = Item("""
+    UPLOADED = Item(
+        """
         Uploaded
 
         These OCI builds were successfully uploaded to a registry.
-        """)
+        """
+    )
 
-    PARTIAL = Item("""
+    PARTIAL = Item(
+        """
         Partial
 
         Some OCI builds have uploaded to a registry.
-    """)
+    """
+    )
 
 
 class IOCIRecipeBuildView(IPackageBuildView, IPrivacy):
@@ -154,30 +154,51 @@ class IOCIRecipeBuildView(IPackageBuildView, IPrivacy):
     build_request = Reference(
         IOCIRecipeBuildRequest,
         title=_("The build request that caused this build to be created."),
-        required=False, readonly=True)
+        required=False,
+        readonly=True,
+    )
 
-    requester = exported(PublicPersonChoice(
-        title=_("Requester"),
-        description=_("The person who requested this OCI recipe build."),
-        vocabulary='ValidPersonOrTeam', required=True, readonly=True))
+    requester = exported(
+        PublicPersonChoice(
+            title=_("Requester"),
+            description=_("The person who requested this OCI recipe build."),
+            vocabulary="ValidPersonOrTeam",
+            required=True,
+            readonly=True,
+        )
+    )
 
-    recipe = exported(Reference(
-        IOCIRecipe,
-        title=_("The OCI recipe to build."),
-        required=True,
-        readonly=True))
+    recipe = exported(
+        Reference(
+            IOCIRecipe,
+            title=_("The OCI recipe to build."),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    eta = exported(Datetime(
-        title=_("The datetime when the build job is estimated to complete."),
-        readonly=True))
+    eta = exported(
+        Datetime(
+            title=_(
+                "The datetime when the build job is estimated to complete."
+            ),
+            readonly=True,
+        )
+    )
 
-    estimate = exported(Bool(
-        title=_("If true, the date value is an estimate."), readonly=True))
+    estimate = exported(
+        Bool(title=_("If true, the date value is an estimate."), readonly=True)
+    )
 
-    date = exported(Datetime(
-        title=_(
-            "The date when the build completed or is estimated to complete."),
-        readonly=True))
+    date = exported(
+        Datetime(
+            title=_(
+                "The date when the build completed or is estimated to "
+                "complete."
+            ),
+            readonly=True,
+        )
+    )
 
     def getFiles():
         """Retrieve the build's `IOCIFile` records.
@@ -213,17 +234,26 @@ class IOCIRecipeBuildView(IPackageBuildView, IPrivacy):
         :return: The corresponding `ILibraryFileAlias`.
         """
 
-    distro_arch_series = exported(Reference(
-        IDistroArchSeries,
-        title=_("The series and architecture for which to build."),
-        required=True, readonly=True))
+    distro_arch_series = exported(
+        Reference(
+            IDistroArchSeries,
+            title=_("The series and architecture for which to build."),
+            required=True,
+            readonly=True,
+        )
+    )
 
     arch_tag = exported(
-        TextLine(title=_("Architecture tag"), required=True, readonly=True))
+        TextLine(title=_("Architecture tag"), required=True, readonly=True)
+    )
 
-    score = exported(Int(
-        title=_("Score of the related build farm job (if any)."),
-        required=False, readonly=True))
+    score = exported(
+        Int(
+            title=_("Score of the related build farm job (if any)."),
+            required=False,
+            readonly=True,
+        )
+    )
 
     manifest = Attribute(_("The manifest of the image."))
 
@@ -233,33 +263,48 @@ class IOCIRecipeBuildView(IPackageBuildView, IPrivacy):
         title=_("Registry upload jobs for this build."),
         # Really IOCIRegistryUploadJob.
         value_type=Reference(schema=Interface),
-        readonly=True)
+        readonly=True,
+    )
 
     # Really IOCIRegistryUploadJob
     last_registry_upload_job = Reference(
-        title=_("Last registry upload job for this build."), schema=Interface)
-
-    registry_upload_status = exported(Choice(
-        title=_("Registry upload status"),
-        vocabulary=OCIRecipeBuildRegistryUploadStatus,
-        required=True, readonly=False
-    ))
-
-    registry_upload_error_summary = exported(TextLine(
-        title=_("Registry upload error summary"),
-        description=_(
-            "The error summary, if any, from the last attempt to upload this "
-            "build to a registry."),
-        required=False, readonly=True))
-
-    registry_upload_errors = exported(List(
-        title=_("Detailed registry upload errors"),
-        description=_(
-            "A list of errors, as described in "
-            "https://docs.docker.com/registry/spec/api/#errors, from the last "
-            "attempt to upload this build to a registry."),
-        value_type=Dict(key_type=TextLine()),
-        required=False, readonly=True))
+        title=_("Last registry upload job for this build."), schema=Interface
+    )
+
+    registry_upload_status = exported(
+        Choice(
+            title=_("Registry upload status"),
+            vocabulary=OCIRecipeBuildRegistryUploadStatus,
+            required=True,
+            readonly=False,
+        )
+    )
+
+    registry_upload_error_summary = exported(
+        TextLine(
+            title=_("Registry upload error summary"),
+            description=_(
+                "The error summary, if any, from the last attempt to upload "
+                "this build to a registry."
+            ),
+            required=False,
+            readonly=True,
+        )
+    )
+
+    registry_upload_errors = exported(
+        List(
+            title=_("Detailed registry upload errors"),
+            description=_(
+                "A list of errors, as described in "
+                "https://docs.docker.com/registry/spec/api/#errors, from the "
+                "last attempt to upload this build to a registry."
+            ),
+            value_type=Dict(key_type=TextLine()),
+            required=False,
+            readonly=True,
+        )
+    )
 
     def hasMoreRecentBuild():
         """Checks if this recipe has a more recent build currently building or
@@ -295,17 +340,21 @@ class IOCIRecipeBuildAdmin(IBuildFarmJobAdmin):
 
 
 @exported_as_webservice_entry(
-    publish_web_link=True, as_of="devel", singular_name="oci_recipe_build")
-class IOCIRecipeBuild(IOCIRecipeBuildAdmin, IOCIRecipeBuildEdit,
-                      IOCIRecipeBuildView, IPackageBuild):
+    publish_web_link=True, as_of="devel", singular_name="oci_recipe_build"
+)
+class IOCIRecipeBuild(
+    IOCIRecipeBuildAdmin,
+    IOCIRecipeBuildEdit,
+    IOCIRecipeBuildView,
+    IPackageBuild,
+):
     """A build record for an OCI recipe."""
 
 
 class IOCIRecipeBuildSet(ISpecificBuildFarmJobSource):
     """A utility to create and access OCIRecipeBuilds."""
 
-    def new(requester, recipe, distro_arch_series,
-            date_created=DEFAULT):
+    def new(requester, recipe, distro_arch_series, date_created=DEFAULT):
         """Create an `IOCIRecipeBuild`."""
 
     def preloadBuildsData(builds):
@@ -318,23 +367,33 @@ class IOCIFile(Interface):
     build = Reference(
         IOCIRecipeBuild,
         title=_("The OCI recipe build producing this file."),
-        required=True, readonly=True)
+        required=True,
+        readonly=True,
+    )
 
     library_file = Reference(
-        ILibraryFileAlias, title=_("A file in the librarian."),
-        required=True, readonly=True)
+        ILibraryFileAlias,
+        title=_("A file in the librarian."),
+        required=True,
+        readonly=True,
+    )
 
     layer_file_digest = TextLine(
-        title=_("Content-addressable hash of the file''s contents, "
-                "used for reassembling image layers when pushing "
-                "a build to a registry. This hash is in an opaque format "
-                "generated by the OCI build tool."),
-        required=False, readonly=True)
+        title=_(
+            "Content-addressable hash of the file''s contents, "
+            "used for reassembling image layers when pushing "
+            "a build to a registry. This hash is in an opaque format "
+            "generated by the OCI build tool."
+        ),
+        required=False,
+        readonly=True,
+    )
 
     date_last_used = Datetime(
         title=_("The datetime this file was last used in a build."),
         required=True,
-        readonly=False)
+        readonly=False,
+    )
 
 
 class IOCIFileSet(Interface):
diff --git a/lib/lp/oci/interfaces/ocirecipebuildjob.py b/lib/lp/oci/interfaces/ocirecipebuildjob.py
index df411e2..07f0ac6 100644
--- a/lib/lp/oci/interfaces/ocirecipebuildjob.py
+++ b/lib/lp/oci/interfaces/ocirecipebuildjob.py
@@ -4,40 +4,36 @@
 """OCIRecipe build job interfaces"""
 
 __all__ = [
-    'IOCIRecipeBuildJob',
-    'IOCIRegistryUploadJob',
-    'IOCIRegistryUploadJobSource',
-    ]
+    "IOCIRecipeBuildJob",
+    "IOCIRegistryUploadJob",
+    "IOCIRegistryUploadJobSource",
+]
 
 from lazr.restful.fields import Reference
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
-from zope.schema import (
-    Dict,
-    List,
-    TextLine,
-    )
+from zope.interface import Attribute, Interface
+from zope.schema import Dict, List, TextLine
 
 from lp import _
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
-from lp.services.job.interfaces.job import (
-    IJob,
-    IJobSource,
-    IRunnableJob,
-    )
+from lp.services.job.interfaces.job import IJob, IJobSource, IRunnableJob
 
 
 class IOCIRecipeBuildJob(Interface):
     """A job related to an OCI image."""
+
     job = Reference(
-        title=_("The common Job attributes."), schema=IJob,
-        required=True, readonly=True)
+        title=_("The common Job attributes."),
+        schema=IJob,
+        required=True,
+        readonly=True,
+    )
 
     build = Reference(
         title=_("The OCI Recipe Build to use for this job."),
-        schema=IOCIRecipeBuild, required=True, readonly=True)
+        schema=IOCIRecipeBuild,
+        required=True,
+        readonly=True,
+    )
 
     json_data = Attribute(_("A dict of data about the job."))
 
@@ -46,20 +42,23 @@ class IOCIRegistryUploadJob(IRunnableJob):
     """A Job that uploads an OCI image to a registry."""
 
     error_summary = TextLine(
-        title=_("Error summary"), required=False, readonly=True)
+        title=_("Error summary"), required=False, readonly=True
+    )
 
     errors = List(
         title=_("Detailed registry upload errors"),
         description=_(
             "A list of errors, as described in "
             "https://docs.docker.com/registry/spec/api/#errors, from the last "
-            "attempt to run this job."),
+            "attempt to run this job."
+        ),
         value_type=Dict(key_type=TextLine()),
-        required=False, readonly=True)
+        required=False,
+        readonly=True,
+    )
 
 
 class IOCIRegistryUploadJobSource(IJobSource):
-
     def create(build):
         """Upload an OCI image to a registry.
 
diff --git a/lib/lp/oci/interfaces/ocirecipejob.py b/lib/lp/oci/interfaces/ocirecipejob.py
index a6e727d..a319673 100644
--- a/lib/lp/oci/interfaces/ocirecipejob.py
+++ b/lib/lp/oci/interfaces/ocirecipejob.py
@@ -4,48 +4,38 @@
 """Interfaces related to OCI recipe jobs."""
 
 __all__ = [
-    'IOCIRecipeJob',
-    'IOCIRecipeRequestBuildsJob',
-    'IOCIRecipeRequestBuildsJobSource',
-    ]
+    "IOCIRecipeJob",
+    "IOCIRecipeRequestBuildsJob",
+    "IOCIRecipeRequestBuildsJobSource",
+]
 
 from lazr.restful.fields import Reference
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
-from zope.schema import (
-    Datetime,
-    Dict,
-    Int,
-    List,
-    TextLine,
-    )
+from zope.interface import Attribute, Interface
+from zope.schema import Datetime, Dict, Int, List, TextLine
 
 from lp import _
-from lp.oci.interfaces.ocirecipe import (
-    IOCIRecipe,
-    IOCIRecipeBuildRequest,
-    )
+from lp.oci.interfaces.ocirecipe import IOCIRecipe, IOCIRecipeBuildRequest
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
 from lp.registry.interfaces.person import IPerson
-from lp.services.job.interfaces.job import (
-    IJob,
-    IJobSource,
-    IRunnableJob,
-    )
+from lp.services.job.interfaces.job import IJob, IJobSource, IRunnableJob
 
 
 class IOCIRecipeJob(Interface):
     """A job related to an OCI Recipe."""
 
     job = Reference(
-        title=_("The common Job attributes."), schema=IJob,
-        required=True, readonly=True)
+        title=_("The common Job attributes."),
+        schema=IJob,
+        required=True,
+        readonly=True,
+    )
 
     recipe = Reference(
         title=_("The OCI recipe to use for this job."),
-        schema=IOCIRecipe, required=True, readonly=True)
+        schema=IOCIRecipe,
+        required=True,
+        readonly=True,
+    )
 
     metadata = Attribute(_("A dict of data about the job."))
 
@@ -54,33 +44,47 @@ class IOCIRecipeRequestBuildsJob(IRunnableJob):
     """A Job that processes a request for builds of an OCI recipe."""
 
     requester = Reference(
-        title=_("The person requesting the builds."), schema=IPerson,
-        required=True, readonly=True)
+        title=_("The person requesting the builds."),
+        schema=IPerson,
+        required=True,
+        readonly=True,
+    )
 
     build_request = Reference(
         title=_("The build request corresponding to this job."),
-        schema=IOCIRecipeBuildRequest, required=True, readonly=True)
+        schema=IOCIRecipeBuildRequest,
+        required=True,
+        readonly=True,
+    )
 
     builds = List(
         title=_("The builds created by this request."),
-        value_type=Reference(schema=IOCIRecipeBuild), required=True,
-        readonly=True)
+        value_type=Reference(schema=IOCIRecipeBuild),
+        required=True,
+        readonly=True,
+    )
 
     date_created = Datetime(
         title=_("Time when this job was created."),
-        required=True, readonly=True)
+        required=True,
+        readonly=True,
+    )
 
     date_finished = Datetime(
-        title=_("Time when this job finished."),
-        required=True, readonly=True)
+        title=_("Time when this job finished."), required=True, readonly=True
+    )
 
     error_message = TextLine(
-        title=_("Error message"), required=False, readonly=True)
+        title=_("Error message"), required=False, readonly=True
+    )
 
     uploaded_manifests = Dict(
         title=_("A dict of manifest information per build."),
-        key_type=Int(), value_type=Dict(),
-        required=False, readonly=True)
+        key_type=Int(),
+        value_type=Dict(),
+        required=False,
+        readonly=True,
+    )
 
     def addUploadedManifest(build_id, manifest_info):
         """Add the manifest information for one of the builds in this
@@ -92,7 +96,6 @@ class IOCIRecipeRequestBuildsJob(IRunnableJob):
 
 
 class IOCIRecipeRequestBuildsJobSource(IJobSource):
-
     def create(oci_recipe, requester, architectures=None):
         """Request builds of an OCI Recipe.
 
@@ -104,8 +107,7 @@ class IOCIRecipeRequestBuildsJobSource(IJobSource):
         """
 
     def getByOCIRecipeAndID(recipe, job_id):
-        """Retrieve the build job by OCI recipe and the given job ID.
-        """
+        """Retrieve the build job by OCI recipe and the given job ID."""
 
     def findByOCIRecipe(recipe, statuses=None, job_ids=None):
         """Find jobs for an OCI recipe.
diff --git a/lib/lp/oci/interfaces/ocirecipesubscription.py b/lib/lp/oci/interfaces/ocirecipesubscription.py
index b519c0e..3a63983 100644
--- a/lib/lp/oci/interfaces/ocirecipesubscription.py
+++ b/lib/lp/oci/interfaces/ocirecipesubscription.py
@@ -3,16 +3,11 @@
 
 """OCIRecipe subscription model."""
 
-__all__ = [
-    'IOCIRecipeSubscription'
-]
+__all__ = ["IOCIRecipeSubscription"]
 
 from lazr.restful.fields import Reference
 from zope.interface import Interface
-from zope.schema import (
-    Datetime,
-    Int,
-    )
+from zope.schema import Datetime, Int
 
 from lp import _
 from lp.oci.interfaces.ocirecipe import IOCIRecipe
@@ -22,19 +17,27 @@ from lp.services.fields import PersonChoice
 class IOCIRecipeSubscription(Interface):
     """A person subscription to a specific OCIRecipe recipe."""
 
-    id = Int(title=_('ID'), readonly=True, required=True)
+    id = Int(title=_("ID"), readonly=True, required=True)
     person = PersonChoice(
-        title=_('Person'), required=True, vocabulary='ValidPersonOrTeam',
+        title=_("Person"),
+        required=True,
+        vocabulary="ValidPersonOrTeam",
         readonly=True,
-        description=_("The person subscribed to the related OCI recipe."))
+        description=_("The person subscribed to the related OCI recipe."),
+    )
     recipe = Reference(
-        IOCIRecipe, title=_("OCI recipe"), required=True, readonly=True)
+        IOCIRecipe, title=_("OCI recipe"), required=True, readonly=True
+    )
     subscribed_by = PersonChoice(
-        title=_('Subscribed by'), required=True,
-        vocabulary='ValidPersonOrTeam', readonly=True,
-        description=_("The person who created this subscription."))
+        title=_("Subscribed by"),
+        required=True,
+        vocabulary="ValidPersonOrTeam",
+        readonly=True,
+        description=_("The person who created this subscription."),
+    )
     date_created = Datetime(
-        title=_('Date subscribed'), required=True, readonly=True)
+        title=_("Date subscribed"), required=True, readonly=True
+    )
 
     def canBeUnsubscribedByUser(user):
         """Can the user unsubscribe the subscriber from the OCI recipe?"""
diff --git a/lib/lp/oci/interfaces/ociregistryclient.py b/lib/lp/oci/interfaces/ociregistryclient.py
index e149a28..f0241c2 100644
--- a/lib/lp/oci/interfaces/ociregistryclient.py
+++ b/lib/lp/oci/interfaces/ociregistryclient.py
@@ -4,11 +4,11 @@
 """Interface for communication with an OCI registry."""
 
 __all__ = [
-    'BlobUploadFailed',
-    'IOCIRegistryClient',
-    'ManifestUploadFailed',
-    'MultipleOCIRegistryError',
-    'OCIRegistryError',
+    "BlobUploadFailed",
+    "IOCIRegistryClient",
+    "ManifestUploadFailed",
+    "MultipleOCIRegistryError",
+    "OCIRegistryError",
 ]
 
 from zope.interface import Interface
@@ -31,8 +31,11 @@ class MultipleOCIRegistryError(OCIRegistryError):
 
     @property
     def errors(self):
-        return [i.errors for i in self.exceptions
-                if isinstance(i, OCIRegistryError)]
+        return [
+            i.errors
+            for i in self.exceptions
+            if isinstance(i, OCIRegistryError)
+        ]
 
 
 class BlobUploadFailed(OCIRegistryError):
diff --git a/lib/lp/oci/interfaces/ociregistrycredentials.py b/lib/lp/oci/interfaces/ociregistrycredentials.py
index a10400c..53c0ea2 100644
--- a/lib/lp/oci/interfaces/ociregistrycredentials.py
+++ b/lib/lp/oci/interfaces/ociregistrycredentials.py
@@ -4,32 +4,23 @@
 """Interfaces for handling credentials for OCI registry actions."""
 
 __all__ = [
-    'IOCIRegistryCredentials',
-    'IOCIRegistryCredentialsSet',
-    'OCIRegistryCredentialsAlreadyExist',
-    'OCIRegistryCredentialsNotOwner',
-    'user_can_edit_credentials_for_owner',
-    ]
+    "IOCIRegistryCredentials",
+    "IOCIRegistryCredentialsSet",
+    "OCIRegistryCredentialsAlreadyExist",
+    "OCIRegistryCredentialsNotOwner",
+    "user_can_edit_credentials_for_owner",
+]
 
 import http.client
 
 from lazr.restful.declarations import error_status
 from zope.interface import Interface
-from zope.schema import (
-    Int,
-    TextLine,
-    )
+from zope.schema import Int, TextLine
 from zope.security.interfaces import Unauthorized
 
 from lp import _
-from lp.registry.interfaces.role import (
-    IHasOwner,
-    IPersonRoles,
-    )
-from lp.services.fields import (
-    PersonChoice,
-    URIField,
-    )
+from lp.registry.interfaces.role import IHasOwner, IPersonRoles
+from lp.services.fields import PersonChoice, URIField
 
 
 @error_status(http.client.CONFLICT)
@@ -41,7 +32,8 @@ class OCIRegistryCredentialsAlreadyExist(Exception):
     def __init__(self):
         super().__init__(
             "Credentials already exist with the same URL, username, and "
-            "region.")
+            "region."
+        )
 
 
 @error_status(http.client.UNAUTHORIZED)
@@ -60,13 +52,15 @@ class IOCIRegistryCredentialsView(Interface):
         title=_("Username"),
         description=_("The username for the credentials, if available."),
         required=True,
-        readonly=True)
+        readonly=True,
+    )
 
     region = TextLine(
         title=_("Region"),
         description=_("The registry region, if available."),
         required=False,
-        readonly=True)
+        readonly=True,
+    )
 
 
 class IOCIRegistryCredentialsEditableAttributes(IHasOwner):
@@ -75,17 +69,21 @@ class IOCIRegistryCredentialsEditableAttributes(IHasOwner):
         title=_("Owner"),
         required=True,
         vocabulary="AllUserTeamsParticipationPlusSelf",
-        description=_("The owner of these credentials. "
-                      "Only the owner is entitled to create "
-                      "push rules using them."),
-        readonly=False)
+        description=_(
+            "The owner of these credentials. "
+            "Only the owner is entitled to create "
+            "push rules using them."
+        ),
+        readonly=False,
+    )
 
     url = URIField(
-        allowed_schemes=['http', 'https'],
+        allowed_schemes=["http", "https"],
         title=_("URL"),
         description=_("The registry URL."),
         required=True,
-        readonly=False)
+        readonly=False,
+    )
 
 
 class IOCIRegistryCredentialsEdit(Interface):
@@ -100,9 +98,11 @@ class IOCIRegistryCredentialsEdit(Interface):
         """Delete these credentials."""
 
 
-class IOCIRegistryCredentials(IOCIRegistryCredentialsEdit,
-                              IOCIRegistryCredentialsEditableAttributes,
-                              IOCIRegistryCredentialsView):
+class IOCIRegistryCredentials(
+    IOCIRegistryCredentialsEdit,
+    IOCIRegistryCredentialsEditableAttributes,
+    IOCIRegistryCredentialsView,
+):
     """Credentials for pushing to an OCI registry."""
 
 
diff --git a/lib/lp/oci/interfaces/webservice.py b/lib/lp/oci/interfaces/webservice.py
index 9945259..01923a2 100644
--- a/lib/lp/oci/interfaces/webservice.py
+++ b/lib/lp/oci/interfaces/webservice.py
@@ -4,20 +4,20 @@
 """All the interfaces that are exposed through the webservice."""
 
 __all__ = [
-    'IOCIProject',
-    'IOCIProjectSeries',
-    'IOCIPushRule',
-    'IOCIRecipe',
-    'IOCIRecipeBuild',
-    'IOCIRecipeBuildRequest'
-    ]
+    "IOCIProject",
+    "IOCIProjectSeries",
+    "IOCIPushRule",
+    "IOCIRecipe",
+    "IOCIRecipeBuild",
+    "IOCIRecipeBuildRequest",
+]
 
 from lp.oci.interfaces.ocipushrule import IOCIPushRule
 from lp.oci.interfaces.ocirecipe import (
     IOCIRecipe,
     IOCIRecipeBuildRequest,
     IOCIRecipeEdit,
-    )
+)
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
 from lp.registry.interfaces.ociproject import IOCIProject
 from lp.registry.interfaces.ociprojectseries import IOCIProjectSeries
@@ -26,24 +26,24 @@ from lp.services.webservice.apihelpers import (
     patch_entry_return_type,
     patch_plain_parameter_type,
     patch_reference_property,
-    )
-
+)
 
 # IOCIProject
-patch_collection_property(IOCIProject, 'series', IOCIProjectSeries)
-patch_entry_return_type(IOCIProject, 'newRecipe', IOCIRecipe)
+patch_collection_property(IOCIProject, "series", IOCIProjectSeries)
+patch_entry_return_type(IOCIProject, "newRecipe", IOCIRecipe)
 patch_plain_parameter_type(
-    IOCIProject, 'setOfficialRecipeStatus', 'recipe', IOCIRecipe)
+    IOCIProject, "setOfficialRecipeStatus", "recipe", IOCIRecipe
+)
 
 # IOCIRecipe
-patch_collection_property(IOCIRecipe, 'builds', IOCIRecipeBuild)
-patch_collection_property(IOCIRecipe, 'completed_builds', IOCIRecipeBuild)
-patch_collection_property(IOCIRecipe, 'pending_builds', IOCIRecipeBuild)
-patch_collection_property(IOCIRecipe, 'push_rules', IOCIPushRule)
+patch_collection_property(IOCIRecipe, "builds", IOCIRecipeBuild)
+patch_collection_property(IOCIRecipe, "completed_builds", IOCIRecipeBuild)
+patch_collection_property(IOCIRecipe, "pending_builds", IOCIRecipeBuild)
+patch_collection_property(IOCIRecipe, "push_rules", IOCIPushRule)
 
 # IOCIRecipeRequestBuild
-patch_reference_property(IOCIRecipeBuildRequest, 'recipe', IOCIRecipe)
-patch_collection_property(IOCIRecipeBuildRequest, 'builds', IOCIRecipeBuild)
+patch_reference_property(IOCIRecipeBuildRequest, "recipe", IOCIRecipe)
+patch_collection_property(IOCIRecipeBuildRequest, "builds", IOCIRecipeBuild)
 
 
-patch_entry_return_type(IOCIRecipeEdit, 'newPushRule', IOCIPushRule)
+patch_entry_return_type(IOCIRecipeEdit, "newPushRule", IOCIPushRule)
diff --git a/lib/lp/oci/model/ocipushrule.py b/lib/lp/oci/model/ocipushrule.py
index 872b2e5..483946a 100644
--- a/lib/lp/oci/model/ocipushrule.py
+++ b/lib/lp/oci/model/ocipushrule.py
@@ -4,41 +4,38 @@
 """Registry credentials for use by an `OCIPushRule`."""
 
 __all__ = [
-    'OCIDistributionPushRule',
-    'OCIPushRule',
-    'OCIPushRuleSet',
-    ]
-
-from storm.locals import (
-    Int,
-    Reference,
-    Storm,
-    Unicode,
-    )
+    "OCIDistributionPushRule",
+    "OCIPushRule",
+    "OCIPushRuleSet",
+]
+
+from storm.locals import Int, Reference, Storm, Unicode
 from zope.interface import implementer
 
 from lp.oci.interfaces.ocipushrule import (
     IOCIPushRule,
     IOCIPushRuleSet,
     OCIPushRuleAlreadyExists,
-    )
+)
 from lp.services.database.interfaces import IStore
 
 
 @implementer(IOCIPushRule)
 class OCIPushRule(Storm):
 
-    __storm_table__ = 'OCIPushRule'
+    __storm_table__ = "OCIPushRule"
 
     id = Int(primary=True)
 
-    recipe_id = Int(name='recipe', allow_none=False)
-    recipe = Reference(recipe_id, 'OCIRecipe.id')
+    recipe_id = Int(name="recipe", allow_none=False)
+    recipe = Reference(recipe_id, "OCIRecipe.id")
 
     registry_credentials_id = Int(
-        name='registry_credentials', allow_none=False)
+        name="registry_credentials", allow_none=False
+    )
     registry_credentials = Reference(
-        registry_credentials_id, 'OCIRegistryCredentials.id')
+        registry_credentials_id, "OCIRegistryCredentials.id"
+    )
 
     image_name = Unicode(name="image_name", allow_none=False)
 
@@ -51,11 +48,15 @@ class OCIPushRule(Storm):
         return self.registry_credentials.username
 
     def setNewImageName(self, new_image_name):
-        result = IStore(OCIPushRule).find(
-            OCIPushRule,
-            OCIPushRule.registry_credentials == self.registry_credentials,
-            OCIPushRule.image_name == new_image_name
-        ).one()
+        result = (
+            IStore(OCIPushRule)
+            .find(
+                OCIPushRule,
+                OCIPushRule.registry_credentials == self.registry_credentials,
+                OCIPushRule.image_name == new_image_name,
+            )
+            .one()
+        )
         if result:
             raise OCIPushRuleAlreadyExists()
         self.image_name = new_image_name
@@ -93,13 +94,13 @@ class OCIDistributionPushRule:
 
 @implementer(IOCIPushRuleSet)
 class OCIPushRuleSet:
-
     def new(self, recipe, registry_credentials, image_name):
         """See `IOCIPushRuleSet`."""
         for existing in recipe.push_rules:
             credentials_match = (
-                existing.registry_credentials == registry_credentials)
-            image_match = (existing.image_name == image_name)
+                existing.registry_credentials == registry_credentials
+            )
+            image_match = existing.image_name == image_name
             if credentials_match and image_match:
                 raise OCIPushRuleAlreadyExists()
         push_rule = OCIPushRule(recipe, registry_credentials, image_name)
@@ -112,12 +113,10 @@ class OCIPushRuleSet:
 
     def findByRecipe(self, recipe):
         store = IStore(OCIPushRule)
-        return store.find(
-            OCIPushRule,
-            OCIPushRule.recipe == recipe)
+        return store.find(OCIPushRule, OCIPushRule.recipe == recipe)
 
     def findByRegistryCredentials(self, credentials):
         store = IStore(OCIPushRule)
         return store.find(
-            OCIPushRule,
-            OCIPushRule.registry_credentials == credentials)
+            OCIPushRule, OCIPushRule.registry_credentials == credentials
+        )
diff --git a/lib/lp/oci/model/ocirecipe.py b/lib/lp/oci/model/ocirecipe.py
index 73edd1a..53392f4 100644
--- a/lib/lp/oci/model/ocirecipe.py
+++ b/lib/lp/oci/model/ocirecipe.py
@@ -5,58 +5,31 @@
 
 from lp.soyuz.interfaces.binarypackagebuild import BuildSetStatus
 
-
 __all__ = [
-    'get_ocirecipe_privacy_filter',
-    'OCIRecipe',
-    'OCIRecipeBuildRequest',
-    'OCIRecipeSet',
-    ]
+    "get_ocirecipe_privacy_filter",
+    "OCIRecipe",
+    "OCIRecipeBuildRequest",
+    "OCIRecipeSet",
+]
 
 
-from lazr.lifecycle.event import ObjectCreatedEvent
 import pytz
+from lazr.lifecycle.event import ObjectCreatedEvent
 from storm.databases.postgres import JSON
-from storm.expr import (
-    And,
-    Coalesce,
-    Desc,
-    Exists,
-    Join,
-    Not,
-    Or,
-    Select,
-    SQL,
-    )
-from storm.locals import (
-    Bool,
-    DateTime,
-    Int,
-    Reference,
-    Store,
-    Storm,
-    Unicode,
-    )
-from zope.component import (
-    getAdapter,
-    getUtility,
-    )
+from storm.expr import SQL, And, Coalesce, Desc, Exists, Join, Not, Or, Select
+from storm.locals import Bool, DateTime, Int, Reference, Store, Storm, Unicode
+from zope.component import getAdapter, getUtility
 from zope.event import notify
 from zope.interface import implementer
 from zope.security.interfaces import Unauthorized
-from zope.security.proxy import (
-    isinstance as zope_isinstance,
-    removeSecurityProxy,
-    )
+from zope.security.proxy import isinstance as zope_isinstance
+from zope.security.proxy import removeSecurityProxy
 
-from lp.app.enums import (
-    InformationType,
-    PUBLIC_INFORMATION_TYPES,
-    )
+from lp.app.enums import PUBLIC_INFORMATION_TYPES, InformationType
 from lp.app.errors import (
     SubscriptionPrivacyViolation,
     UserCannotUnsubscribePerson,
-    )
+)
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.interfaces.security import IAuthorization
 from lp.app.interfaces.services import IService
@@ -72,6 +45,8 @@ from lp.code.model.gitrepository import GitRepository
 from lp.oci.enums import OCIRecipeBuildRequestStatus
 from lp.oci.interfaces.ocipushrule import IOCIPushRuleSet
 from lp.oci.interfaces.ocirecipe import (
+    OCI_RECIPE_ALLOW_CREATE,
+    OCI_RECIPE_BUILD_DISTRIBUTION,
     CannotModifyOCIRecipeProcessor,
     DuplicateOCIRecipeName,
     IOCIRecipe,
@@ -79,24 +54,17 @@ from lp.oci.interfaces.ocirecipe import (
     IOCIRecipeSet,
     NoSourceForOCIRecipe,
     NoSuchOCIRecipe,
-    OCI_RECIPE_ALLOW_CREATE,
-    OCI_RECIPE_BUILD_DISTRIBUTION,
     OCIRecipeBranchHasInvalidFormat,
     OCIRecipeBuildAlreadyPending,
     OCIRecipeFeatureDisabled,
     OCIRecipeNotOwner,
     OCIRecipePrivacyMismatch,
     UsingDistributionCredentials,
-    )
+)
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuildSet
 from lp.oci.interfaces.ocirecipejob import IOCIRecipeRequestBuildsJobSource
-from lp.oci.interfaces.ociregistrycredentials import (
-    IOCIRegistryCredentialsSet,
-    )
-from lp.oci.model.ocipushrule import (
-    OCIDistributionPushRule,
-    OCIPushRule,
-    )
+from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentialsSet
+from lp.oci.model.ocipushrule import OCIDistributionPushRule, OCIPushRule
 from lp.oci.model.ocirecipebuild import OCIRecipeBuild
 from lp.oci.model.ocirecipejob import OCIRecipeJob
 from lp.oci.model.ocirecipesubscription import OCIRecipeSubscription
@@ -104,49 +72,40 @@ from lp.registry.errors import PrivatePersonLinkageError
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifactGrantSource,
     IAccessArtifactSource,
-    )
+)
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.ociproject import IOCIProject
 from lp.registry.interfaces.person import (
     IPerson,
     IPersonSet,
     validate_public_person,
-    )
+)
 from lp.registry.interfaces.role import IPersonRoles
 from lp.registry.model.accesspolicy import (
     AccessPolicyGrant,
     reconcile_access_for_artifacts,
-    )
+)
 from lp.registry.model.distribution import Distribution
 from lp.registry.model.distroseries import DistroSeries
 from lp.registry.model.person import Person
 from lp.registry.model.series import ACTIVE_STATUSES
 from lp.registry.model.teammembership import TeamParticipation
 from lp.services.database.bulk import load_related
-from lp.services.database.constants import (
-    DEFAULT,
-    UTC_NOW,
-    )
+from lp.services.database.constants import DEFAULT, UTC_NOW
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.database.enumcol import DBEnum
-from lp.services.database.interfaces import (
-    IMasterStore,
-    IStore,
-    )
+from lp.services.database.interfaces import IMasterStore, IStore
 from lp.services.database.stormexpr import (
     Array,
     ArrayAgg,
     ArrayIntersects,
     Greatest,
     NullsLast,
-    )
+)
 from lp.services.features import getFeatureFlag
 from lp.services.job.interfaces.job import JobStatus
 from lp.services.job.model.job import Job
-from lp.services.propertycache import (
-    cachedproperty,
-    get_property_cache,
-    )
+from lp.services.propertycache import cachedproperty, get_property_cache
 from lp.services.webhooks.interfaces import IWebhookSet
 from lp.services.webhooks.model import WebhookTargetMixin
 from lp.soyuz.model.distroarchseries import DistroArchSeries
@@ -164,15 +123,17 @@ def oci_recipe_modified(recipe, event):
 @implementer(IOCIRecipe)
 class OCIRecipe(Storm, WebhookTargetMixin):
 
-    __storm_table__ = 'OCIRecipe'
+    __storm_table__ = "OCIRecipe"
 
     id = Int(primary=True)
     date_created = DateTime(
-        name="date_created", tzinfo=pytz.UTC, allow_none=False)
+        name="date_created", tzinfo=pytz.UTC, allow_none=False
+    )
     date_last_modified = DateTime(
-        name="date_last_modified", tzinfo=pytz.UTC, allow_none=False)
+        name="date_last_modified", tzinfo=pytz.UTC, allow_none=False
+    )
 
-    registrant_id = Int(name='registrant', allow_none=False)
+    registrant_id = Int(name="registrant", allow_none=False)
     registrant = Reference(registrant_id, "Person.id")
 
     def _validate_owner(self, attr, value):
@@ -181,11 +142,12 @@ class OCIRecipe(Storm, WebhookTargetMixin):
                 validate_public_person(self, attr, value)
             except PrivatePersonLinkageError:
                 raise OCIRecipePrivacyMismatch(
-                    "A public OCI recipe cannot have a private owner.")
+                    "A public OCI recipe cannot have a private owner."
+                )
         return value
 
-    owner_id = Int(name='owner', allow_none=False, validator=_validate_owner)
-    owner = Reference(owner_id, 'Person.id')
+    owner_id = Int(name="owner", allow_none=False, validator=_validate_owner)
+    owner = Reference(owner_id, "Person.id")
 
     def _valid_information_type(self, attr, value):
         if value not in PUBLIC_INFORMATION_TYPES:
@@ -199,11 +161,13 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         return value
 
     _information_type = DBEnum(
-        enum=InformationType, default=InformationType.PUBLIC,
+        enum=InformationType,
+        default=InformationType.PUBLIC,
         name="information_type",
-        validator=_valid_information_type)
+        validator=_valid_information_type,
+    )
 
-    oci_project_id = Int(name='oci_project', allow_none=False)
+    oci_project_id = Int(name="oci_project", allow_none=False)
     oci_project = Reference(oci_project_id, "OCIProject.id")
 
     name = Unicode(name="name", allow_none=False)
@@ -217,32 +181,50 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         if not self.private and value is not None:
             if IStore(GitRepository).get(GitRepository, value).private:
                 raise OCIRecipePrivacyMismatch(
-                    "A public OCI recipe cannot have a private repository.")
+                    "A public OCI recipe cannot have a private repository."
+                )
         return value
 
     git_repository_id = Int(
-        name="git_repository", allow_none=True,
-        validator=_validate_git_repository)
+        name="git_repository",
+        allow_none=True,
+        validator=_validate_git_repository,
+    )
     git_repository = Reference(git_repository_id, "GitRepository.id")
     git_path = Unicode(name="git_path", allow_none=True)
     build_file = Unicode(name="build_file", allow_none=False)
     build_path = Unicode(name="build_path", allow_none=False)
     _build_args = JSON(name="build_args", allow_none=True)
 
-    require_virtualized = Bool(name="require_virtualized", default=True,
-                               allow_none=False)
+    require_virtualized = Bool(
+        name="require_virtualized", default=True, allow_none=False
+    )
 
-    allow_internet = Bool(name='allow_internet', allow_none=False)
+    allow_internet = Bool(name="allow_internet", allow_none=False)
 
     build_daily = Bool(name="build_daily", default=False)
 
     _image_name = Unicode(name="image_name", allow_none=True)
 
-    def __init__(self, name, registrant, owner, oci_project, git_ref,
-                 description=None, official=False, require_virtualized=True,
-                 build_file=None, build_daily=False, date_created=DEFAULT,
-                 allow_internet=True, build_args=None, build_path=None,
-                 image_name=None, information_type=InformationType.PUBLIC):
+    def __init__(
+        self,
+        name,
+        registrant,
+        owner,
+        oci_project,
+        git_ref,
+        description=None,
+        official=False,
+        require_virtualized=True,
+        build_file=None,
+        build_daily=False,
+        date_created=DEFAULT,
+        allow_internet=True,
+        build_args=None,
+        build_path=None,
+        image_name=None,
+        information_type=InformationType.PUBLIC,
+    ):
         if not getFeatureFlag(OCI_RECIPE_ALLOW_CREATE):
             raise OCIRecipeFeatureDisabled()
         super().__init__()
@@ -266,8 +248,11 @@ class OCIRecipe(Storm, WebhookTargetMixin):
 
     def __repr__(self):
         return "<OCIRecipe ~%s/%s/+oci/%s/+recipe/%s>" % (
-            self.owner.name, self.oci_project.pillar.name,
-            self.oci_project.name, self.name)
+            self.owner.name,
+            self.oci_project.pillar.name,
+            self.oci_project.name,
+            self.name,
+        )
 
     @property
     def information_type(self):
@@ -318,8 +303,9 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         Takes the privacy and pillar and makes the related AccessArtifact
         and AccessPolicyArtifacts match.
         """
-        reconcile_access_for_artifacts([self], self.information_type,
-                                       [self.pillar])
+        reconcile_access_for_artifacts(
+            [self], self.information_type, [self.pillar]
+        )
 
     def getAllowedInformationTypes(self, user):
         """See `IOCIRecipe`."""
@@ -335,70 +321,85 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         return not store.find(
             OCIRecipe,
             OCIRecipe.id == self.id,
-            get_ocirecipe_privacy_filter(user)).is_empty()
+            get_ocirecipe_privacy_filter(user),
+        ).is_empty()
 
     def getSubscription(self, person):
         """See `IOCIRecipe`."""
         if person is None:
             return None
-        return Store.of(self).find(
-            OCIRecipeSubscription,
-            OCIRecipeSubscription.person == person,
-            OCIRecipeSubscription.recipe == self).one()
+        return (
+            Store.of(self)
+            .find(
+                OCIRecipeSubscription,
+                OCIRecipeSubscription.person == person,
+                OCIRecipeSubscription.recipe == self,
+            )
+            .one()
+        )
 
     def userCanBeSubscribed(self, person):
         """Checks if the given person can subscribe to this OCI recipe."""
         return not (
-            self.private and
-            person.is_team and
-            person.anyone_can_join())
+            self.private and person.is_team and person.anyone_can_join()
+        )
 
     @property
     def subscriptions(self):
         return Store.of(self).find(
-            OCIRecipeSubscription,
-            OCIRecipeSubscription.recipe == self)
+            OCIRecipeSubscription, OCIRecipeSubscription.recipe == self
+        )
 
     @property
     def subscribers(self):
         return Store.of(self).find(
             Person,
             OCIRecipeSubscription.person_id == Person.id,
-            OCIRecipeSubscription.recipe == self)
+            OCIRecipeSubscription.recipe == self,
+        )
 
     def subscribe(self, person, subscribed_by, ignore_permissions=False):
         """See `IOCIRecipe`."""
         if not self.userCanBeSubscribed(person):
             raise SubscriptionPrivacyViolation(
                 "Open and delegated teams cannot be subscribed to private "
-                "OCI recipes.")
+                "OCI recipes."
+            )
         subscription = self.getSubscription(person)
         if subscription is None:
             subscription = OCIRecipeSubscription(
-                person=person, recipe=self, subscribed_by=subscribed_by)
+                person=person, recipe=self, subscribed_by=subscribed_by
+            )
             Store.of(subscription).flush()
         service = getUtility(IService, "sharing")
         ocirecipes = service.getVisibleArtifacts(
-            person, ocirecipes=[self], ignore_permissions=True)["ocirecipes"]
+            person, ocirecipes=[self], ignore_permissions=True
+        )["ocirecipes"]
         if not ocirecipes:
             service.ensureAccessGrants(
-                [person], subscribed_by, ocirecipes=[self],
-                ignore_permissions=ignore_permissions)
+                [person],
+                subscribed_by,
+                ocirecipes=[self],
+                ignore_permissions=ignore_permissions,
+            )
 
     def unsubscribe(self, person, unsubscribed_by, ignore_permissions=False):
         """See `IOCIRecipe`."""
         subscription = self.getSubscription(person)
         if subscription is None:
             return
-        if (not ignore_permissions
-                and not subscription.canBeUnsubscribedByUser(unsubscribed_by)):
+        if (
+            not ignore_permissions
+            and not subscription.canBeUnsubscribedByUser(unsubscribed_by)
+        ):
             raise UserCannotUnsubscribePerson(
-                '%s does not have permission to unsubscribe %s.' % (
-                    unsubscribed_by.displayname,
-                    person.displayname))
+                "%s does not have permission to unsubscribe %s."
+                % (unsubscribed_by.displayname, person.displayname)
+            )
         artifact = getUtility(IAccessArtifactSource).find([self])
         getUtility(IAccessArtifactGrantSource).revokeByArtifact(
-            artifact, [person])
+            artifact, [person]
+        )
         store = Store.of(subscription)
         store.remove(subscription)
         IStore(self).flush()
@@ -409,7 +410,8 @@ class OCIRecipe(Storm, WebhookTargetMixin):
 
     def _deleteOCIRecipeSubscriptions(self):
         subscriptions = Store.of(self).find(
-            OCIRecipeSubscription, OCIRecipeSubscription.recipe == self)
+            OCIRecipeSubscription, OCIRecipeSubscription.recipe == self
+        )
         subscriptions.remove()
 
     def destroySelf(self):
@@ -423,30 +425,41 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         buildqueue_records = store.find(
             BuildQueue,
             BuildQueue._build_farm_job_id == OCIRecipeBuild.build_farm_job_id,
-            OCIRecipeBuild.recipe == self)
+            OCIRecipeBuild.recipe == self,
+        )
         for buildqueue_record in buildqueue_records:
             buildqueue_record.destroySelf()
-        build_farm_job_ids = list(store.find(
-            OCIRecipeBuild.build_farm_job_id, OCIRecipeBuild.recipe == self))
-
-        store.execute("""
+        build_farm_job_ids = list(
+            store.find(
+                OCIRecipeBuild.build_farm_job_id, OCIRecipeBuild.recipe == self
+            )
+        )
+
+        store.execute(
+            """
             DELETE FROM OCIFile
             USING OCIRecipeBuild
             WHERE
                 OCIFile.build = OCIRecipeBuild.id AND
                 OCIRecipeBuild.recipe = ?
-            """, (self.id,))
-        store.execute("""
+            """,
+            (self.id,),
+        )
+        store.execute(
+            """
             DELETE FROM OCIRecipeBuildJob
             USING OCIRecipeBuild
             WHERE
                 OCIRecipeBuildJob.build = OCIRecipeBuild.id AND
                 OCIRecipeBuild.recipe = ?
-            """, (self.id,))
+            """,
+            (self.id,),
+        )
 
         affected_jobs = Select(
             [OCIRecipeJob.job_id],
-            And(OCIRecipeJob.job == Job.id, OCIRecipeJob.recipe == self))
+            And(OCIRecipeJob.job == Job.id, OCIRecipeJob.recipe == self),
+        )
         builds = store.find(OCIRecipeBuild, OCIRecipeBuild.recipe == self)
         builds.remove()
         store.find(Job, Job.id.is_in(affected_jobs)).remove()
@@ -455,7 +468,8 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         getUtility(IWebhookSet).delete(self.webhooks)
         store.remove(self)
         store.find(
-            BuildFarmJob, BuildFarmJob.id.is_in(build_farm_job_ids)).remove()
+            BuildFarmJob, BuildFarmJob.id.is_in(build_farm_job_ids)
+        ).remove()
 
     @cachedproperty
     def _git_ref(self):
@@ -492,8 +506,9 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         distro = getUtility(IDistributionSet).getByName(distro_name)
         if not distro:
             raise ValueError(
-                "'%s' is not a valid value for feature flag '%s'" %
-                (distro_name, OCI_RECIPE_BUILD_DISTRIBUTION))
+                "'%s' is not a valid value for feature flag '%s'"
+                % (distro_name, OCI_RECIPE_BUILD_DISTRIBUTION)
+            )
         return distro
 
     @property
@@ -501,8 +516,9 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         # For OCI builds we default to the series set by the feature flag.
         # If the feature flag is not set we default to the current series of
         # the recipe's distribution.
-        oci_series = getFeatureFlag('oci.build_series.%s'
-                                    % self.distribution.name)
+        oci_series = getFeatureFlag(
+            "oci.build_series.%s" % self.distribution.name
+        )
         if oci_series:
             return self.distribution.getSeries(oci_series)
         else:
@@ -514,26 +530,37 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         clauses = [Processor.id == DistroArchSeries.processor_id]
         if self.distro_series is not None:
             enabled_archs_resultset = removeSecurityProxy(
-                self.distro_series.enabled_architectures)
-            clauses.append(DistroArchSeries.id.is_in(
-                enabled_archs_resultset.get_select_expr(DistroArchSeries.id)))
+                self.distro_series.enabled_architectures
+            )
+            clauses.append(
+                DistroArchSeries.id.is_in(
+                    enabled_archs_resultset.get_select_expr(
+                        DistroArchSeries.id
+                    )
+                )
+            )
         else:
             # We might not know the series if the OCI project's distribution
             # has no series at all, which can happen in tests.  Fall back to
             # just returning enabled architectures for any active series,
             # which is a bit of a hack but works.
-            clauses.extend([
-                DistroArchSeries.enabled,
-                DistroArchSeries.distroseriesID == DistroSeries.id,
-                DistroSeries.status.is_in(ACTIVE_STATUSES),
-                ])
+            clauses.extend(
+                [
+                    DistroArchSeries.enabled,
+                    DistroArchSeries.distroseriesID == DistroSeries.id,
+                    DistroSeries.status.is_in(ACTIVE_STATUSES),
+                ]
+            )
         return Store.of(self).find(Processor, *clauses).config(distinct=True)
 
     def _getProcessors(self):
-        return list(Store.of(self).find(
-            Processor,
-            Processor.id == OCIRecipeArch.processor_id,
-            OCIRecipeArch.recipe == self))
+        return list(
+            Store.of(self).find(
+                Processor,
+                Processor.id == OCIRecipeArch.processor_id,
+                OCIRecipeArch.recipe == self,
+            )
+        )
 
     def setProcessors(self, processors, check_permissions=False, user=None):
         """See `IOCIRecipe`."""
@@ -542,21 +569,25 @@ class OCIRecipe(Storm, WebhookTargetMixin):
             if user is not None:
                 roles = IPersonRoles(user)
                 authz = lambda perm: getAdapter(self, IAuthorization, perm)
-                if authz('launchpad.Admin').checkAuthenticated(roles):
+                if authz("launchpad.Admin").checkAuthenticated(roles):
                     can_modify = lambda proc: True
-                elif authz('launchpad.Edit').checkAuthenticated(roles):
+                elif authz("launchpad.Edit").checkAuthenticated(roles):
                     can_modify = lambda proc: not proc.restricted
             if can_modify is None:
                 raise Unauthorized(
-                    'Permission launchpad.Admin or launchpad.Edit required '
-                    'on %s.' % self)
+                    "Permission launchpad.Admin or launchpad.Edit required "
+                    "on %s." % self
+                )
         else:
             can_modify = lambda proc: True
 
-        enablements = dict(Store.of(self).find(
-            (Processor, OCIRecipeArch),
-            Processor.id == OCIRecipeArch.processor_id,
-            OCIRecipeArch.recipe == self))
+        enablements = dict(
+            Store.of(self).find(
+                (Processor, OCIRecipeArch),
+                Processor.id == OCIRecipeArch.processor_id,
+                OCIRecipeArch.recipe == self,
+            )
+        )
         for proc in enablements:
             if proc not in processors:
                 if not can_modify(proc):
@@ -582,26 +613,31 @@ class OCIRecipe(Storm, WebhookTargetMixin):
             and das.processor in self.processors
             and (
                 das.processor.supports_virtualized
-                or not self.require_virtualized))
+                or not self.require_virtualized
+            )
+        )
 
     def _isArchitectureAllowed(self, das, pocket):
-        return (
-            das.getChroot(pocket=pocket) is not None
-            and self._isBuildableArchitectureAllowed(das))
+        return das.getChroot(
+            pocket=pocket
+        ) is not None and self._isBuildableArchitectureAllowed(das)
 
     def getAllowedArchitectures(self, distro_series=None):
         """See `IOCIRecipe`."""
         if distro_series is None:
             distro_series = self.distro_series
         return [
-            das for das in distro_series.buildable_architectures
-            if self._isBuildableArchitectureAllowed(das)]
+            das
+            for das in distro_series.buildable_architectures
+            if self._isBuildableArchitectureAllowed(das)
+        ]
 
     def _checkRequestBuild(self, requester):
         if not requester.inTeam(self.owner):
             raise OCIRecipeNotOwner(
-                "%s cannot create OCI image builds owned by %s." %
-                (requester.display_name, self.owner.display_name))
+                "%s cannot create OCI image builds owned by %s."
+                % (requester.display_name, self.owner.display_name)
+            )
 
     def _hasPendingBuilds(self, distro_arch_series):
         """Checks if this OCIRecipe has pending builds for all processors
@@ -615,7 +651,8 @@ class OCIRecipe(Storm, WebhookTargetMixin):
             OCIRecipeBuild,
             OCIRecipeBuild.recipe == self.id,
             OCIRecipeBuild.processor_id.is_in([p.id for p in processors]),
-            OCIRecipeBuild.status == BuildStatus.NEEDSBUILD)
+            OCIRecipeBuild.status == BuildStatus.NEEDSBUILD,
+        )
         pending_processors = {i.processor for i in pending}
         return len(pending_processors) == len(processors)
 
@@ -625,7 +662,8 @@ class OCIRecipe(Storm, WebhookTargetMixin):
             raise OCIRecipeBuildAlreadyPending
 
         build = getUtility(IOCIRecipeBuildSet).new(
-            requester, self, distro_arch_series, build_request=build_request)
+            requester, self, distro_arch_series, build_request=build_request
+        )
         build.queueBuild()
         notify(ObjectCreatedEvent(build, user=requester))
         return build
@@ -633,19 +671,25 @@ class OCIRecipe(Storm, WebhookTargetMixin):
     def getBuildRequest(self, job_id):
         return OCIRecipeBuildRequest(self, job_id)
 
-    def requestBuildsFromJob(self, requester, build_request=None,
-                             architectures=None):
+    def requestBuildsFromJob(
+        self, requester, build_request=None, architectures=None
+    ):
         self._checkRequestBuild(requester)
         distro_arch_series = set(self.getAllowedArchitectures())
 
         builds = []
         for das in distro_arch_series:
-            if (architectures is not None
-                    and das.architecturetag not in architectures):
+            if (
+                architectures is not None
+                and das.architecturetag not in architectures
+            ):
                 continue
             try:
-                builds.append(self.requestBuild(
-                    requester, das, build_request=build_request))
+                builds.append(
+                    self.requestBuild(
+                        requester, das, build_request=build_request
+                    )
+                )
             except OCIRecipeBuildAlreadyPending:
                 pass
 
@@ -659,14 +703,16 @@ class OCIRecipe(Storm, WebhookTargetMixin):
     def requestBuilds(self, requester, architectures=None):
         self._checkRequestBuild(requester)
         job = getUtility(IOCIRecipeRequestBuildsJobSource).create(
-            self, requester, architectures)
+            self, requester, architectures
+        )
         return self.getBuildRequest(job.job_id)
 
     @property
     def pending_build_requests(self):
         util = getUtility(IOCIRecipeRequestBuildsJobSource)
         return util.findByOCIRecipe(
-            self, statuses=(JobStatus.WAITING, JobStatus.RUNNING))
+            self, statuses=(JobStatus.WAITING, JobStatus.RUNNING)
+        )
 
     @property
     def push_rules(self):
@@ -676,11 +722,10 @@ class OCIRecipe(Storm, WebhookTargetMixin):
             push_rule = OCIDistributionPushRule(
                 self,
                 self.oci_project.distribution.oci_registry_credentials,
-                self.image_name)
+                self.image_name,
+            )
             return [push_rule]
-        rules = IStore(self).find(
-            OCIPushRule,
-            OCIPushRule.recipe == self.id)
+        rules = IStore(self).find(OCIPushRule, OCIPushRule.recipe == self.id)
         return list(rules)
 
     @property
@@ -691,13 +736,13 @@ class OCIRecipe(Storm, WebhookTargetMixin):
             BuildStatus.BUILDING,
             BuildStatus.UPLOADING,
             BuildStatus.CANCELLING,
-            ]
+        ]
 
     def _getBuilds(self, filter_term, order_by):
         """The actual query to get the builds."""
         query_args = [
             OCIRecipeBuild.recipe == self,
-            ]
+        ]
         if filter_term is not None:
             query_args.append(filter_term)
         result = Store.of(self).find(OCIRecipeBuild, *query_args)
@@ -713,22 +758,34 @@ class OCIRecipe(Storm, WebhookTargetMixin):
     def builds(self):
         """See `IOCIRecipe`."""
         order_by = (
-            NullsLast(Desc(Greatest(
-                OCIRecipeBuild.date_started,
-                OCIRecipeBuild.date_finished))),
+            NullsLast(
+                Desc(
+                    Greatest(
+                        OCIRecipeBuild.date_started,
+                        OCIRecipeBuild.date_finished,
+                    )
+                )
+            ),
             Desc(OCIRecipeBuild.date_created),
-            Desc(OCIRecipeBuild.id))
+            Desc(OCIRecipeBuild.id),
+        )
         return self._getBuilds(None, order_by)
 
     @property
     def completed_builds(self):
         """See `IOCIRecipe`."""
-        filter_term = (Not(OCIRecipeBuild.status.is_in(self._pending_states)))
+        filter_term = Not(OCIRecipeBuild.status.is_in(self._pending_states))
         order_by = (
-            NullsLast(Desc(Greatest(
-                OCIRecipeBuild.date_started,
-                OCIRecipeBuild.date_finished))),
-            Desc(OCIRecipeBuild.id))
+            NullsLast(
+                Desc(
+                    Greatest(
+                        OCIRecipeBuild.date_started,
+                        OCIRecipeBuild.date_finished,
+                    )
+                )
+            ),
+            Desc(OCIRecipeBuild.id),
+        )
         return self._getBuilds(filter_term, order_by)
 
     @property
@@ -736,19 +793,25 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         """See `IOCIRecipe`."""
         filter_term = (
             Not(OCIRecipeBuild.status.is_in(self._pending_states)),
-            OCIRecipeBuild.build_request_id == None)
+            OCIRecipeBuild.build_request_id == None,
+        )
         order_by = (
-            NullsLast(Desc(Greatest(
-                OCIRecipeBuild.date_started,
-                OCIRecipeBuild.date_finished))),
-            Desc(OCIRecipeBuild.id))
+            NullsLast(
+                Desc(
+                    Greatest(
+                        OCIRecipeBuild.date_started,
+                        OCIRecipeBuild.date_finished,
+                    )
+                )
+            ),
+            Desc(OCIRecipeBuild.id),
+        )
         return self._getBuilds(filter_term, order_by)
 
-
     @property
     def pending_builds(self):
         """See `IOCIRecipe`."""
-        filter_term = (OCIRecipeBuild.status.is_in(self._pending_states))
+        filter_term = OCIRecipeBuild.status.is_in(self._pending_states)
         # We want to order by date_created but this is the same as ordering
         # by id (since id increases monotonically) and is less expensive.
         order_by = Desc(OCIRecipeBuild.id)
@@ -776,8 +839,14 @@ class OCIRecipe(Storm, WebhookTargetMixin):
     def image_name(self, value):
         self._image_name = value
 
-    def newPushRule(self, registrant, registry_url, image_name, credentials,
-                    credentials_owner=None):
+    def newPushRule(
+        self,
+        registrant,
+        registry_url,
+        image_name,
+        credentials,
+        credentials_owner=None,
+    ):
         """See `IOCIRecipe`."""
         if credentials_owner is None:
             # Ideally this should probably be a required parameter, but
@@ -788,9 +857,11 @@ class OCIRecipe(Storm, WebhookTargetMixin):
         if self.use_distribution_credentials:
             raise UsingDistributionCredentials()
         oci_credentials = getUtility(IOCIRegistryCredentialsSet).getOrCreate(
-            registrant, credentials_owner, registry_url, credentials)
+            registrant, credentials_owner, registry_url, credentials
+        )
         push_rule = getUtility(IOCIPushRuleSet).new(
-            self, oci_credentials, image_name)
+            self, oci_credentials, image_name
+        )
         Store.of(push_rule).flush()
         return push_rule
 
@@ -814,22 +885,38 @@ class OCIRecipeArch(Storm):
 
 @implementer(IOCIRecipeSet)
 class OCIRecipeSet:
-
-    def new(self, name, registrant, owner, oci_project, git_ref, build_file,
-            description=None, official=False, require_virtualized=True,
-            build_daily=False, processors=None, date_created=DEFAULT,
-            allow_internet=True, build_args=None, build_path=None,
-            image_name=None, information_type=InformationType.PUBLIC):
+    def new(
+        self,
+        name,
+        registrant,
+        owner,
+        oci_project,
+        git_ref,
+        build_file,
+        description=None,
+        official=False,
+        require_virtualized=True,
+        build_daily=False,
+        processors=None,
+        date_created=DEFAULT,
+        allow_internet=True,
+        build_args=None,
+        build_path=None,
+        image_name=None,
+        information_type=InformationType.PUBLIC,
+    ):
         """See `IOCIRecipeSet`."""
         if not registrant.inTeam(owner):
             if owner.is_team:
                 raise OCIRecipeNotOwner(
-                    "%s is not a member of %s." %
-                    (registrant.displayname, owner.displayname))
+                    "%s is not a member of %s."
+                    % (registrant.displayname, owner.displayname)
+                )
             else:
                 raise OCIRecipeNotOwner(
-                    "%s cannot create OCI images owned by %s." %
-                    (registrant.displayname, owner.displayname))
+                    "%s cannot create OCI images owned by %s."
+                    % (registrant.displayname, owner.displayname)
+                )
 
         if not (git_ref and build_file):
             raise NoSourceForOCIRecipe
@@ -845,31 +932,52 @@ class OCIRecipeSet:
 
         store = IMasterStore(OCIRecipe)
         oci_recipe = OCIRecipe(
-            name, registrant, owner, oci_project, git_ref, description,
-            official, require_virtualized, build_file, build_daily,
-            date_created, allow_internet, build_args, build_path, image_name,
-            information_type)
+            name,
+            registrant,
+            owner,
+            oci_project,
+            git_ref,
+            description,
+            official,
+            require_virtualized,
+            build_file,
+            build_daily,
+            date_created,
+            allow_internet,
+            build_args,
+            build_path,
+            image_name,
+            information_type,
+        )
         store.add(oci_recipe)
         oci_recipe._reconcileAccess()
 
         # Automatically subscribe the owner to the OCI recipe.
-        oci_recipe.subscribe(oci_recipe.owner, registrant,
-                             ignore_permissions=True)
+        oci_recipe.subscribe(
+            oci_recipe.owner, registrant, ignore_permissions=True
+        )
 
         if processors is None:
             processors = [
-                p for p in oci_recipe.available_processors
-                if p.build_by_default]
+                p
+                for p in oci_recipe.available_processors
+                if p.build_by_default
+            ]
         oci_recipe.setProcessors(processors)
 
         return oci_recipe
 
     def _getByName(self, owner, oci_project, name):
-        return IStore(OCIRecipe).find(
-            OCIRecipe,
-            OCIRecipe.owner == owner,
-            OCIRecipe.name == name,
-            OCIRecipe.oci_project == oci_project).one()
+        return (
+            IStore(OCIRecipe)
+            .find(
+                OCIRecipe,
+                OCIRecipe.owner == owner,
+                OCIRecipe.name == name,
+                OCIRecipe.oci_project == oci_project,
+            )
+            .one()
+        )
 
     def exists(self, owner, oci_project, name):
         """See `IOCIRecipeSet`."""
@@ -898,17 +1006,20 @@ class OCIRecipeSet:
         return IStore(OCIRecipe).find(
             OCIRecipe,
             OCIRecipe.oci_project == oci_project,
-            get_ocirecipe_privacy_filter(visible_by_user))
+            get_ocirecipe_privacy_filter(visible_by_user),
+        )
 
     def findByContext(self, context, visible_by_user):
         if IPerson.providedBy(context):
             return self.findByOwner(context).find(
-                get_ocirecipe_privacy_filter(visible_by_user))
+                get_ocirecipe_privacy_filter(visible_by_user)
+            )
         elif IOCIProject.providedBy(context):
             return self.findByOCIProject(context, visible_by_user)
         else:
             raise NotImplementedError(
-                "Unknown OCI recipe context: %s" % context)
+                "Unknown OCI recipe context: %s" % context
+            )
 
     def findByGitRepository(self, repository, paths=None):
         """See `IOCIRecipeSet`."""
@@ -920,7 +1031,8 @@ class OCIRecipeSet:
     def detachFromGitRepository(self, repository):
         """See `IOCIRecipeSet`."""
         self.findByGitRepository(repository).set(
-            git_repository_id=None, git_path=None, date_last_modified=UTC_NOW)
+            git_repository_id=None, git_path=None, date_last_modified=UTC_NOW
+        )
 
     def preloadDataForOCIRecipes(self, recipes, user=None):
         """See `IOCIRecipeSet`."""
@@ -931,8 +1043,11 @@ class OCIRecipeSet:
             person_ids.add(recipe.registrant_id)
             person_ids.add(recipe.owner_id)
 
-        list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
-            person_ids, need_validity=True))
+        list(
+            getUtility(IPersonSet).getPrecachedPersonsFromIDs(
+                person_ids, need_validity=True
+            )
+        )
 
         # Preload projects
         oci_projects = [recipe.oci_project for recipe in recipes]
@@ -940,12 +1055,13 @@ class OCIRecipeSet:
 
         # Preload repos
         repos = load_related(GitRepository, recipes, ["git_repository_id"])
-        load_related(Person, repos, ['owner_id', 'registrant_id'])
+        load_related(Person, repos, ["owner_id", "registrant_id"])
         GenericGitCollection.preloadDataForRepositories(repos)
 
         # Preload GitRefs.
         git_refs = GitRef.findByReposAndPaths(
-            [(r.git_repository, r.git_path) for r in recipes])
+            [(r.git_repository, r.git_path) for r in recipes]
+        )
         for recipe in recipes:
             git_ref = git_refs.get((recipe.git_repository, recipe.git_path))
             if git_ref is not None:
@@ -957,45 +1073,49 @@ class OCIRecipeSet:
         def collect_builds(*states):
             wanted = []
             for state in states:
-                candidates = [build for build in builds
-                              if build.status == state]
+                candidates = [
+                    build for build in builds if build.status == state
+                ]
                 wanted.extend(candidates)
             return wanted
-        failed = collect_builds(BuildStatus.FAILEDTOBUILD,
-                                BuildStatus.MANUALDEPWAIT,
-                                BuildStatus.CHROOTWAIT,
-                                BuildStatus.FAILEDTOUPLOAD)
+
+        failed = collect_builds(
+            BuildStatus.FAILEDTOBUILD,
+            BuildStatus.MANUALDEPWAIT,
+            BuildStatus.CHROOTWAIT,
+            BuildStatus.FAILEDTOUPLOAD,
+        )
         needsbuild = collect_builds(BuildStatus.NEEDSBUILD)
-        building = collect_builds(BuildStatus.BUILDING,
-                                  BuildStatus.UPLOADING)
+        building = collect_builds(BuildStatus.BUILDING, BuildStatus.UPLOADING)
         successful = collect_builds(BuildStatus.FULLYBUILT)
-        cancelled = collect_builds(BuildStatus.CANCELLING,
-                                   BuildStatus.CANCELLED)
+        cancelled = collect_builds(
+            BuildStatus.CANCELLING, BuildStatus.CANCELLED
+        )
 
         # Note: the BuildStatus DBItems are used here to summarize the
         # status of a set of builds:s
         if len(building) != 0:
             return {
-                'status': BuildSetStatus.BUILDING,
-                'builds': building,
-                }
+                "status": BuildSetStatus.BUILDING,
+                "builds": building,
+            }
         # If there are no builds, this is a 'pending build request'
         # and needs building
         elif len(needsbuild) != 0 or len(builds) == 0:
             return {
-                'status': BuildSetStatus.NEEDSBUILD,
-                'builds': needsbuild,
-                }
+                "status": BuildSetStatus.NEEDSBUILD,
+                "builds": needsbuild,
+            }
         elif len(failed) != 0 or len(cancelled) != 0:
             return {
-                'status': BuildSetStatus.FAILEDTOBUILD,
-                'builds': failed,
-                }
+                "status": BuildSetStatus.FAILEDTOBUILD,
+                "builds": failed,
+            }
         else:
             return {
-                'status': BuildSetStatus.FULLYBUILT,
-                'builds': successful,
-                }
+                "status": BuildSetStatus.FULLYBUILT,
+                "builds": successful,
+            }
 
 
 @implementer(IOCIRecipeBuildRequest)
@@ -1005,6 +1125,7 @@ class OCIRecipeBuildRequest:
     This is not directly backed by a database table; instead, it is a
     webservice-friendly view of an asynchronous build request.
     """
+
     def __init__(self, oci_recipe, id):
         self.recipe = oci_recipe
         self.id = id
@@ -1012,8 +1133,7 @@ class OCIRecipeBuildRequest:
     @cachedproperty
     def job(self):
         util = getUtility(IOCIRecipeRequestBuildsJobSource)
-        return util.getByOCIRecipeAndID(
-            self.recipe, self.id)
+        return util.getByOCIRecipeAndID(self.recipe, self.id)
 
     @property
     def date_requested(self):
@@ -1070,7 +1190,8 @@ def get_ocirecipe_privacy_filter(user):
     # information_type, we should be able to change this.
     private_recipe = SQL(
         "COALESCE(information_type NOT IN ?, false)",
-        params=[tuple(i.value for i in PUBLIC_INFORMATION_TYPES)])
+        params=[tuple(i.value for i in PUBLIC_INFORMATION_TYPES)],
+    )
     if user is None:
         return private_recipe == False
 
@@ -1080,28 +1201,45 @@ def get_ocirecipe_privacy_filter(user):
             Select(
                 ArrayAgg(TeamParticipation.teamID),
                 tables=TeamParticipation,
-                where=(TeamParticipation.person == user)
-            )), False)
+                where=(TeamParticipation.person == user),
+            ),
+        ),
+        False,
+    )
 
     policy_grant_query = Coalesce(
         ArrayIntersects(
             Array(SQL("%s.access_policy" % OCIRecipe.__storm_table__)),
             Select(
                 ArrayAgg(AccessPolicyGrant.policy_id),
-                tables=(AccessPolicyGrant,
-                        Join(TeamParticipation,
-                             TeamParticipation.teamID ==
-                             AccessPolicyGrant.grantee_id)),
-                where=(TeamParticipation.person == user)
-            )), False)
+                tables=(
+                    AccessPolicyGrant,
+                    Join(
+                        TeamParticipation,
+                        TeamParticipation.teamID
+                        == AccessPolicyGrant.grantee_id,
+                    ),
+                ),
+                where=(TeamParticipation.person == user),
+            ),
+        ),
+        False,
+    )
 
     admin_team_id = getUtility(ILaunchpadCelebrities).admin.id
-    user_is_admin = Exists(Select(
-        TeamParticipation.personID,
-        tables=[TeamParticipation],
-        where=And(
-            TeamParticipation.teamID == admin_team_id,
-            TeamParticipation.person == user)))
+    user_is_admin = Exists(
+        Select(
+            TeamParticipation.personID,
+            tables=[TeamParticipation],
+            where=And(
+                TeamParticipation.teamID == admin_team_id,
+                TeamParticipation.person == user,
+            ),
+        )
+    )
     return Or(
-        private_recipe == False, artifact_grant_query, policy_grant_query,
-        user_is_admin)
+        private_recipe == False,
+        artifact_grant_query,
+        policy_grant_query,
+        user_is_admin,
+    )
diff --git a/lib/lp/oci/model/ocirecipebuild.py b/lib/lp/oci/model/ocirecipebuild.py
index c01448d..e62d8b8 100644
--- a/lib/lp/oci/model/ocirecipebuild.py
+++ b/lib/lp/oci/model/ocirecipebuild.py
@@ -4,10 +4,10 @@
 """A build record for OCI Recipes."""
 
 __all__ = [
-    'OCIFile',
-    'OCIRecipeBuild',
-    'OCIRecipeBuildSet',
-    ]
+    "OCIFile",
+    "OCIRecipeBuild",
+    "OCIRecipeBuildSet",
+]
 
 from datetime import timedelta
 
@@ -22,7 +22,7 @@ from storm.locals import (
     Reference,
     Store,
     Unicode,
-    )
+)
 from storm.store import EmptyResultSet
 from zope.component import getUtility
 from zope.interface import implementer
@@ -33,7 +33,7 @@ from lp.buildmaster.enums import (
     BuildFarmJobType,
     BuildQueueStatus,
     BuildStatus,
-    )
+)
 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSource
 from lp.buildmaster.model.buildfarmjob import SpecificBuildFarmJobSourceMixin
 from lp.buildmaster.model.packagebuild import PackageBuildMixin
@@ -46,12 +46,12 @@ from lp.oci.interfaces.ocirecipebuild import (
     IOCIRecipeBuild,
     IOCIRecipeBuildSet,
     OCIRecipeBuildRegistryUploadStatus,
-    )
+)
 from lp.oci.interfaces.ocirecipebuildjob import IOCIRegistryUploadJobSource
 from lp.oci.model.ocirecipebuildjob import (
     OCIRecipeBuildJob,
     OCIRecipeBuildJobType,
-    )
+)
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.model.person import Person
@@ -60,48 +60,40 @@ from lp.services.database.bulk import load_related
 from lp.services.database.constants import DEFAULT
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.database.enumcol import DBEnum
-from lp.services.database.interfaces import (
-    IMasterStore,
-    IStore,
-    )
+from lp.services.database.interfaces import IMasterStore, IStore
 from lp.services.database.stormbase import StormBase
 from lp.services.job.interfaces.job import JobStatus
 from lp.services.job.model.job import Job
 from lp.services.librarian.browser import ProxiedLibraryFileAlias
-from lp.services.librarian.model import (
-    LibraryFileAlias,
-    LibraryFileContent,
-    )
+from lp.services.librarian.model import LibraryFileAlias, LibraryFileContent
 from lp.services.macaroons.interfaces import (
+    NO_USER,
     BadMacaroonContext,
     IMacaroonIssuer,
-    NO_USER,
-    )
+)
 from lp.services.macaroons.model import MacaroonIssuerBase
-from lp.services.propertycache import (
-    cachedproperty,
-    get_property_cache,
-    )
+from lp.services.propertycache import cachedproperty, get_property_cache
 from lp.services.webapp.snapshot import notify_modified
 
 
 @implementer(IOCIFile)
 class OCIFile(StormBase):
 
-    __storm_table__ = 'OCIFile'
+    __storm_table__ = "OCIFile"
 
-    id = Int(name='id', primary=True)
+    id = Int(name="id", primary=True)
 
-    build_id = Int(name='build', allow_none=False)
-    build = Reference(build_id, 'OCIRecipeBuild.id')
+    build_id = Int(name="build", allow_none=False)
+    build = Reference(build_id, "OCIRecipeBuild.id")
 
-    library_file_id = Int(name='library_file', allow_none=False)
-    library_file = Reference(library_file_id, 'LibraryFileAlias.id')
+    library_file_id = Int(name="library_file", allow_none=False)
+    library_file = Reference(library_file_id, "LibraryFileAlias.id")
 
-    layer_file_digest = Unicode(name='layer_file_digest', allow_none=True)
+    layer_file_digest = Unicode(name="layer_file_digest", allow_none=True)
 
     date_last_used = DateTime(
-        name='date_last_used', tzinfo=pytz.UTC, allow_none=False)
+        name="date_last_used", tzinfo=pytz.UTC, allow_none=False
+    )
 
     def __init__(self, build, library_file, layer_file_digest=None):
         """Construct a `OCIFile`."""
@@ -113,67 +105,78 @@ class OCIFile(StormBase):
 
 @implementer(IOCIFileSet)
 class OCIFileSet:
-
     def getByLayerDigest(self, layer_file_digest):
-        return IStore(OCIFile).find(
-            OCIFile,
-            OCIFile.layer_file_digest == layer_file_digest).order_by(
-                OCIFile.id).first()
+        return (
+            IStore(OCIFile)
+            .find(OCIFile, OCIFile.layer_file_digest == layer_file_digest)
+            .order_by(OCIFile.id)
+            .first()
+        )
 
 
 @implementer(IOCIRecipeBuild)
 class OCIRecipeBuild(PackageBuildMixin, StormBase):
 
-    __storm_table__ = 'OCIRecipeBuild'
+    __storm_table__ = "OCIRecipeBuild"
 
     job_type = BuildFarmJobType.OCIRECIPEBUILD
 
-    id = Int(name='id', primary=True)
+    id = Int(name="id", primary=True)
 
-    build_request_id = Int(name='build_request', allow_none=True)
+    build_request_id = Int(name="build_request", allow_none=True)
 
-    requester_id = Int(name='requester', allow_none=False)
-    requester = Reference(requester_id, 'Person.id')
+    requester_id = Int(name="requester", allow_none=False)
+    requester = Reference(requester_id, "Person.id")
 
-    recipe_id = Int(name='recipe', allow_none=False)
-    recipe = Reference(recipe_id, 'OCIRecipe.id')
+    recipe_id = Int(name="recipe", allow_none=False)
+    recipe = Reference(recipe_id, "OCIRecipe.id")
 
-    processor_id = Int(name='processor', allow_none=False)
-    processor = Reference(processor_id, 'Processor.id')
+    processor_id = Int(name="processor", allow_none=False)
+    processor = Reference(processor_id, "Processor.id")
 
-    virtualized = Bool(name='virtualized')
+    virtualized = Bool(name="virtualized")
 
     date_created = DateTime(
-        name='date_created', tzinfo=pytz.UTC, allow_none=False)
-    date_started = DateTime(name='date_started', tzinfo=pytz.UTC)
-    date_finished = DateTime(name='date_finished', tzinfo=pytz.UTC)
+        name="date_created", tzinfo=pytz.UTC, allow_none=False
+    )
+    date_started = DateTime(name="date_started", tzinfo=pytz.UTC)
+    date_finished = DateTime(name="date_finished", tzinfo=pytz.UTC)
     date_first_dispatched = DateTime(
-        name='date_first_dispatched', tzinfo=pytz.UTC)
+        name="date_first_dispatched", tzinfo=pytz.UTC
+    )
 
-    builder_id = Int(name='builder')
-    builder = Reference(builder_id, 'Builder.id')
+    builder_id = Int(name="builder")
+    builder = Reference(builder_id, "Builder.id")
 
-    status = DBEnum(name='status', enum=BuildStatus, allow_none=False)
+    status = DBEnum(name="status", enum=BuildStatus, allow_none=False)
 
-    log_id = Int(name='log')
-    log = Reference(log_id, 'LibraryFileAlias.id')
+    log_id = Int(name="log")
+    log = Reference(log_id, "LibraryFileAlias.id")
 
-    upload_log_id = Int(name='upload_log')
-    upload_log = Reference(upload_log_id, 'LibraryFileAlias.id')
+    upload_log_id = Int(name="upload_log")
+    upload_log = Reference(upload_log_id, "LibraryFileAlias.id")
 
-    dependencies = Unicode(name='dependencies')
+    dependencies = Unicode(name="dependencies")
 
-    failure_count = Int(name='failure_count', allow_none=False)
+    failure_count = Int(name="failure_count", allow_none=False)
 
-    build_farm_job_id = Int(name='build_farm_job', allow_none=False)
-    build_farm_job = Reference(build_farm_job_id, 'BuildFarmJob.id')
+    build_farm_job_id = Int(name="build_farm_job", allow_none=False)
+    build_farm_job = Reference(build_farm_job_id, "BuildFarmJob.id")
 
     # We only care about the pocket from a building environment POV,
     # it is not a target, nor referenced in the final build.
     pocket = PackagePublishingPocket.UPDATES
 
-    def __init__(self, build_farm_job, requester, recipe,
-                 processor, virtualized, date_created, build_request=None):
+    def __init__(
+        self,
+        build_farm_job,
+        requester,
+        recipe,
+        processor,
+        virtualized,
+        date_created,
+        build_request=None,
+    ):
         """Construct an `OCIRecipeBuild`."""
         self.requester = requester
         self.recipe = recipe
@@ -193,17 +196,24 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
 
     def __repr__(self):
         return "<OCIRecipeBuild ~%s/%s/+oci/%s/+recipe/%s/+build/%d>" % (
-            self.recipe.owner.name, self.recipe.oci_project.pillar.name,
-            self.recipe.oci_project.name, self.recipe.name, self.id)
+            self.recipe.owner.name,
+            self.recipe.oci_project.pillar.name,
+            self.recipe.oci_project.name,
+            self.recipe.name,
+            self.id,
+        )
 
     @property
     def title(self):
         # XXX cjwatson 2020-02-19: This should use a DAS architecture tag
         # rather than a processor name once we can do that.
         return "%s build of /~%s/%s/+oci/%s/+recipe/%s" % (
-            self.processor.name, self.recipe.owner.name,
-            self.recipe.oci_project.pillar.name, self.recipe.oci_project.name,
-            self.recipe.name)
+            self.processor.name,
+            self.recipe.owner.name,
+            self.recipe.oci_project.pillar.name,
+            self.recipe.oci_project.name,
+            self.recipe.name,
+        )
 
     @property
     def score(self):
@@ -234,10 +244,11 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
         # https://code.launchpad.net/
         # ~cjwatson/launchpad/snap-build-record-code/+merge/365356
         return (
-            self.recipe.private or
-            self.recipe.owner.private or
-            self.recipe.git_repository is None or
-            self.recipe.git_repository.private)
+            self.recipe.private
+            or self.recipe.owner.private
+            or self.recipe.git_repository is None
+            or self.recipe.git_repository.private
+        )
 
     private = is_private
 
@@ -259,7 +270,8 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
             (OCIRecipeBuild.date_started, OCIRecipeBuild.date_finished),
             OCIRecipeBuild.recipe == self.recipe_id,
             OCIRecipeBuild.processor == self.processor_id,
-            OCIRecipeBuild.status == BuildStatus.FULLYBUILT)
+            OCIRecipeBuild.status == BuildStatus.FULLYBUILT,
+        )
         result.order_by(Desc(OCIRecipeBuild.date_finished))
         durations = [row[1] - row[0] for row in result[:9]]
         if len(durations) == 0:
@@ -273,7 +285,8 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
             (OCIFile, LibraryFileAlias, LibraryFileContent),
             OCIFile.build == self.id,
             LibraryFileAlias.id == OCIFile.library_file_id,
-            LibraryFileContent.id == LibraryFileAlias.contentID)
+            LibraryFileContent.id == LibraryFileAlias.contentID,
+        )
         return result.order_by([LibraryFileAlias.filename, OCIFile.id])
 
     def getFileByName(self, filename):
@@ -281,13 +294,22 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
         origin = [
             LibraryFileAlias,
             LeftJoin(OCIFile, LibraryFileAlias.id == OCIFile.library_file_id),
-            ]
-        file_object = Store.of(self).using(*origin).find(
-            LibraryFileAlias,
-            Or(
-                LibraryFileAlias.id.is_in((self.log_id, self.upload_log_id)),
-                OCIFile.build == self.id),
-            LibraryFileAlias.filename == filename).one()
+        ]
+        file_object = (
+            Store.of(self)
+            .using(*origin)
+            .find(
+                LibraryFileAlias,
+                Or(
+                    LibraryFileAlias.id.is_in(
+                        (self.log_id, self.upload_log_id)
+                    ),
+                    OCIFile.build == self.id,
+                ),
+                LibraryFileAlias.filename == filename,
+            )
+            .one()
+        )
 
         if file_object is not None and file_object.filename == filename:
             return file_object
@@ -353,23 +375,34 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
     @property
     def distro_arch_series(self):
         return self.recipe.distro_series.getDistroArchSeriesByProcessor(
-            self.processor)
+            self.processor
+        )
 
     @property
     def arch_tag(self):
         """See `IOCIRecipeBuild`."""
         return self.distro_arch_series.architecturetag
 
-    def updateStatus(self, status, builder=None, worker_status=None,
-                     date_started=None, date_finished=None,
-                     force_invalid_transition=False):
+    def updateStatus(
+        self,
+        status,
+        builder=None,
+        worker_status=None,
+        date_started=None,
+        date_finished=None,
+        force_invalid_transition=False,
+    ):
         """See `IBuildFarmJob`."""
         edited_fields = set()
         with notify_modified(self, edited_fields) as previous_obj:
             super().updateStatus(
-                status, builder=builder, worker_status=worker_status,
-                date_started=date_started, date_finished=date_finished,
-                force_invalid_transition=force_invalid_transition)
+                status,
+                builder=builder,
+                worker_status=worker_status,
+                date_started=date_started,
+                date_finished=date_finished,
+                force_invalid_transition=force_invalid_transition,
+            )
             if self.status != previous_obj.status:
                 edited_fields.add("status")
         # notify_modified evaluates all attributes mentioned in the
@@ -385,19 +418,25 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
         # XXX twom 2019-12-11 This should send mail
 
     def getLayerFileByDigest(self, layer_file_digest):
-        file_object = Store.of(self).find(
-            (OCIFile, LibraryFileAlias, LibraryFileContent),
-            OCIFile.build == self.id,
-            LibraryFileAlias.id == OCIFile.library_file_id,
-            LibraryFileContent.id == LibraryFileAlias.contentID,
-            OCIFile.layer_file_digest == layer_file_digest).one()
+        file_object = (
+            Store.of(self)
+            .find(
+                (OCIFile, LibraryFileAlias, LibraryFileContent),
+                OCIFile.build == self.id,
+                LibraryFileAlias.id == OCIFile.library_file_id,
+                LibraryFileContent.id == LibraryFileAlias.contentID,
+                OCIFile.layer_file_digest == layer_file_digest,
+            )
+            .one()
+        )
         if file_object is not None:
             return file_object
         raise NotFoundError(layer_file_digest)
 
     def addFile(self, lfa, layer_file_digest=None):
         oci_file = OCIFile(
-            build=self, library_file=lfa, layer_file_digest=layer_file_digest)
+            build=self, library_file=lfa, layer_file_digest=layer_file_digest
+        )
         IMasterStore(OCIFile).add(oci_file)
         return oci_file
 
@@ -420,10 +459,12 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
         layer_files = Store.of(self).find(
             OCIFile,
             OCIFile.build == self.id,
-            OCIFile.layer_file_digest is not None)
+            OCIFile.layer_file_digest is not None,
+        )
         layer_files_present = not layer_files.is_empty()
-        metadata_present = (self.manifest is not None
-                            and self.digests is not None)
+        metadata_present = (
+            self.manifest is not None and self.digests is not None
+        )
         return layer_files_present and metadata_present
 
     @property
@@ -431,7 +472,8 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
         jobs = Store.of(self).find(
             OCIRecipeBuildJob,
             OCIRecipeBuildJob.build == self,
-            OCIRecipeBuildJob.job_type == OCIRecipeBuildJobType.REGISTRY_UPLOAD
+            OCIRecipeBuildJob.job_type
+            == OCIRecipeBuildJobType.REGISTRY_UPLOAD,
         )
         jobs.order_by(Desc(OCIRecipeBuildJob.job_id))
 
@@ -439,7 +481,8 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
             load_related(Job, rows, ["job_id"])
 
         return DecoratedResultSet(
-            jobs, lambda job: job.makeDerived(), pre_iter_hook=preload_jobs)
+            jobs, lambda job: job.makeDerived(), pre_iter_hook=preload_jobs
+        )
 
     @cachedproperty
     def last_registry_upload_job(self):
@@ -474,21 +517,29 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
         if not self.recipe.can_upload_to_registry:
             raise CannotScheduleRegistryUpload(
                 "Cannot upload this build to registries because the recipe is "
-                "not properly configured.")
+                "not properly configured."
+            )
         if not self.was_built or self.getFiles().is_empty():
             raise CannotScheduleRegistryUpload(
-                "Cannot upload this build because it has no files.")
-        if (self.registry_upload_status ==
-                OCIRecipeBuildRegistryUploadStatus.PENDING):
+                "Cannot upload this build because it has no files."
+            )
+        if (
+            self.registry_upload_status
+            == OCIRecipeBuildRegistryUploadStatus.PENDING
+        ):
             raise CannotScheduleRegistryUpload(
-                "An upload of this build is already in progress.")
-        elif (self.registry_upload_status ==
-                OCIRecipeBuildRegistryUploadStatus.UPLOADED):
+                "An upload of this build is already in progress."
+            )
+        elif (
+            self.registry_upload_status
+            == OCIRecipeBuildRegistryUploadStatus.UPLOADED
+        ):
             # XXX cjwatson 2020-04-22: This won't be quite right in the case
             # where a recipe has multiple push rules.
             raise CannotScheduleRegistryUpload(
                 "Cannot upload this build because it has already been "
-                "uploaded.")
+                "uploaded."
+            )
         getUtility(IOCIRegistryUploadJobSource).create(self)
 
     def hasMoreRecentBuild(self):
@@ -498,7 +549,8 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
             OCIRecipeBuild.recipe == self.recipe,
             OCIRecipeBuild.processor == self.processor,
             OCIRecipeBuild.status == BuildStatus.FULLYBUILT,
-            OCIRecipeBuild.date_created > self.date_created)
+            OCIRecipeBuild.date_created > self.date_created,
+        )
         return not recent_builds.is_empty()
 
 
@@ -506,19 +558,33 @@ class OCIRecipeBuild(PackageBuildMixin, StormBase):
 class OCIRecipeBuildSet(SpecificBuildFarmJobSourceMixin):
     """See `IOCIRecipeBuildSet`."""
 
-    def new(self, requester, recipe, distro_arch_series,
-            date_created=DEFAULT, build_request=None):
+    def new(
+        self,
+        requester,
+        recipe,
+        distro_arch_series,
+        date_created=DEFAULT,
+        build_request=None,
+    ):
         """See `IOCIRecipeBuildSet`."""
         virtualized = (
             not distro_arch_series.processor.supports_nonvirtualized
-            or recipe.require_virtualized)
+            or recipe.require_virtualized
+        )
 
         store = IMasterStore(OCIRecipeBuild)
         build_farm_job = getUtility(IBuildFarmJobSource).new(
-            OCIRecipeBuild.job_type, BuildStatus.NEEDSBUILD, date_created)
+            OCIRecipeBuild.job_type, BuildStatus.NEEDSBUILD, date_created
+        )
         ocirecipebuild = OCIRecipeBuild(
-            build_farm_job, requester, recipe, distro_arch_series.processor,
-            virtualized, date_created, build_request=build_request)
+            build_farm_job,
+            requester,
+            recipe,
+            distro_arch_series.processor,
+            virtualized,
+            date_created,
+            build_request=build_request,
+        )
         store.add(ocirecipebuild)
         store.flush()
         return ocirecipebuild
@@ -527,6 +593,7 @@ class OCIRecipeBuildSet(SpecificBuildFarmJobSourceMixin):
         """See `IOCIRecipeBuildSet`."""
         # Circular import.
         from lp.oci.model.ocirecipe import OCIRecipe
+
         load_related(Person, builds, ["requester_id"])
         lfas = load_related(LibraryFileAlias, builds, ["log_id"])
         load_related(LibraryFileContent, lfas, ["contentID"])
@@ -543,16 +610,22 @@ class OCIRecipeBuildSet(SpecificBuildFarmJobSourceMixin):
 
     def getByBuildFarmJob(self, build_farm_job):
         """See `ISpecificBuildFarmJobSource`."""
-        return Store.of(build_farm_job).find(
-            OCIRecipeBuild, build_farm_job_id=build_farm_job.id).one()
+        return (
+            Store.of(build_farm_job)
+            .find(OCIRecipeBuild, build_farm_job_id=build_farm_job.id)
+            .one()
+        )
 
     def getByBuildFarmJobs(self, build_farm_jobs):
         """See `ISpecificBuildFarmJobSource`."""
         if len(build_farm_jobs) == 0:
             return EmptyResultSet()
         rows = Store.of(build_farm_jobs[0]).find(
-            OCIRecipeBuild, OCIRecipeBuild.build_farm_job_id.is_in(
-                bfj.id for bfj in build_farm_jobs))
+            OCIRecipeBuild,
+            OCIRecipeBuild.build_farm_job_id.is_in(
+                bfj.id for bfj in build_farm_jobs
+            ),
+        )
         return DecoratedResultSet(rows, pre_iter_hook=self.preloadBuildsData)
 
 
@@ -571,7 +644,8 @@ class OCIRecipeBuildMacaroonIssuer(MacaroonIssuerBase):
             raise BadMacaroonContext(context)
         if not removeSecurityProxy(context).is_private:
             raise BadMacaroonContext(
-                context, "Refusing to issue macaroon for public build.")
+                context, "Refusing to issue macaroon for public build."
+            )
         return removeSecurityProxy(context).id
 
     def checkVerificationContext(self, context, **kwargs):
@@ -580,8 +654,9 @@ class OCIRecipeBuildMacaroonIssuer(MacaroonIssuerBase):
             raise BadMacaroonContext(context)
         return context
 
-    def verifyPrimaryCaveat(self, verified, caveat_value, context, user=None,
-                            **kwargs):
+    def verifyPrimaryCaveat(
+        self, verified, caveat_value, context, user=None, **kwargs
+    ):
         """See `MacaroonIssuerBase`.
 
         For verification, the context is an `IGitRepository`.  We check that
@@ -607,9 +682,14 @@ class OCIRecipeBuildMacaroonIssuer(MacaroonIssuerBase):
             build_id = int(caveat_value)
         except ValueError:
             return False
-        return not IStore(OCIRecipeBuild).find(
-            OCIRecipeBuild,
-            OCIRecipeBuild.id == build_id,
-            OCIRecipeBuild.recipe_id == OCIRecipe.id,
-            OCIRecipe.git_repository == context,
-            OCIRecipeBuild.status == BuildStatus.BUILDING).is_empty()
+        return (
+            not IStore(OCIRecipeBuild)
+            .find(
+                OCIRecipeBuild,
+                OCIRecipeBuild.id == build_id,
+                OCIRecipeBuild.recipe_id == OCIRecipe.id,
+                OCIRecipe.git_repository == context,
+                OCIRecipeBuild.status == BuildStatus.BUILDING,
+            )
+            .is_empty()
+        )
diff --git a/lib/lp/oci/model/ocirecipebuildbehaviour.py b/lib/lp/oci/model/ocirecipebuildbehaviour.py
index 6939ca5..d35259c 100644
--- a/lib/lp/oci/model/ocirecipebuildbehaviour.py
+++ b/lib/lp/oci/model/ocirecipebuildbehaviour.py
@@ -7,13 +7,13 @@ Dispatches OCI image build jobs to build-farm workers.
 """
 
 __all__ = [
-    'OCIRecipeBuildBehaviour',
-    ]
+    "OCIRecipeBuildBehaviour",
+]
 
 
-from datetime import datetime
 import json
 import os
+from datetime import datetime
 
 import pytz
 from twisted.internet import defer
@@ -23,16 +23,13 @@ from zope.security.proxy import removeSecurityProxy
 
 from lp.buildmaster.builderproxy import BuilderProxyMixin
 from lp.buildmaster.enums import BuildBaseImageType
-from lp.buildmaster.interfaces.builder import (
-    BuildDaemonError,
-    CannotBuild,
-    )
+from lp.buildmaster.interfaces.builder import BuildDaemonError, CannotBuild
 from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
     IBuildFarmJobBehaviour,
-    )
+)
 from lp.buildmaster.model.buildfarmjobbehaviour import (
     BuildFarmJobBehaviourBase,
-    )
+)
 from lp.code.interfaces.codehosting import LAUNCHPAD_SERVICES
 from lp.oci.interfaces.ocirecipebuild import IOCIFileSet
 from lp.registry.interfaces.series import SeriesStatus
@@ -40,9 +37,7 @@ from lp.services.config import config
 from lp.services.librarian.utils import copy_and_close
 from lp.services.twistedsupport import cancel_on_timeout
 from lp.services.webapp import canonical_url
-from lp.soyuz.adapters.archivedependencies import (
-    get_sources_list_for_building,
-    )
+from lp.soyuz.adapters.archivedependencies import get_sources_list_for_building
 
 
 @implementer(IBuildFarmJobBehaviour)
@@ -56,10 +51,13 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
 
         # Examples:
         #   buildlog_oci_ubuntu_wily_amd64_name_FULLYBUILT.txt
-        return 'buildlog_oci_%s_%s_%s_%s_%s.txt' % (
-            series.distribution.name, series.name,
-            self.build.processor.name, self.build.recipe.name,
-            self.build.status.name)
+        return "buildlog_oci_%s_%s_%s_%s_%s.txt" % (
+            series.distribution.name,
+            series.name,
+            self.build.processor.name,
+            self.build.recipe.name,
+            self.build.status.name,
+        )
 
     def verifyBuildRequest(self, logger):
         """Assert some pre-build checks.
@@ -71,20 +69,26 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
         build = self.build
         if build.virtualized and not self._builder.virtualized:
             raise AssertionError(
-                "Attempt to build virtual item on a non-virtual builder.")
+                "Attempt to build virtual item on a non-virtual builder."
+            )
 
         chroot = build.distro_arch_series.getChroot(pocket=build.pocket)
         if chroot is None:
             raise CannotBuild(
-                "Missing chroot for %s" % build.distro_arch_series.displayname)
+                "Missing chroot for %s" % build.distro_arch_series.displayname
+            )
 
     def issueMacaroon(self):
         """See `IBuildFarmJobBehaviour`."""
         return cancel_on_timeout(
             self._authserver.callRemote(
                 "issueMacaroon",
-                "oci-recipe-build", "OCIRecipeBuild", self.build.id),
-            config.builddmaster.authentication_timeout)
+                "oci-recipe-build",
+                "OCIRecipeBuild",
+                self.build.id,
+            ),
+            config.builddmaster.authentication_timeout,
+        )
 
     def _getBuildInfoArgs(self):
         def format_user(user):
@@ -93,7 +97,9 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
             hide_email = not user.preferredemail or user.hide_email_addresses
             return {
                 "name": user.name,
-                "email": (None if hide_email else user.preferredemail.email)}
+                "email": (None if hide_email else user.preferredemail.email),
+            }
+
         build = self.build
         build_request = build.build_request
         builds = list(build_request.builds) if build_request else [build]
@@ -111,13 +117,16 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
         }
         if build_request:
             info["build_request_id"] = build_request.id
-            info["build_request_timestamp"] = (
-                build_request.date_requested.isoformat())
-        info["architectures"] = [i.distro_arch_series.architecturetag
-                                 for i in builds]
+            info[
+                "build_request_timestamp"
+            ] = build_request.date_requested.isoformat()
+        info["architectures"] = [
+            i.distro_arch_series.architecturetag for i in builds
+        ]
         info["build_urls"] = {
             i.distro_arch_series.architecturetag: canonical_url(i)
-            for i in builds}
+            for i in builds
+        }
         return info
 
     @defer.inlineCallbacks
@@ -131,39 +140,49 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
         # XXX twom 2020-02-17 This may need to be more complex, and involve
         # distribution name.
         args["name"] = build.recipe.name
-        args["archives"], args["trusted_keys"] = (
-            yield get_sources_list_for_building(
-                self, build.distro_arch_series, None,
-                tools_source=None, tools_fingerprint=None,
-                logger=logger))
-
-        args['build_file'] = build.recipe.build_file
+        (
+            args["archives"],
+            args["trusted_keys"],
+        ) = yield get_sources_list_for_building(
+            self,
+            build.distro_arch_series,
+            None,
+            tools_source=None,
+            tools_fingerprint=None,
+            logger=logger,
+        )
+
+        args["build_file"] = build.recipe.build_file
 
         # Do our work on a new dict, so we don't try to update the
         # copy on the model
         build_args = {
-            "LAUNCHPAD_BUILD_ARCH": build.distro_arch_series.architecturetag}
+            "LAUNCHPAD_BUILD_ARCH": build.distro_arch_series.architecturetag
+        }
         # We have to remove the security proxy that Zope applies to this
         # dict, since otherwise we'll be unable to serialise it to
         # XML-RPC.
         build_args.update(removeSecurityProxy(build.recipe.build_args))
-        args['build_args'] = build_args
-        args['build_path'] = build.recipe.build_path
-        args['metadata'] = self._getBuildInfoArgs()
+        args["build_args"] = build_args
+        args["build_path"] = build.recipe.build_path
+        args["metadata"] = self._getBuildInfoArgs()
 
         if build.recipe.git_ref is not None:
             if build.recipe.git_repository.private:
                 macaroon_raw = yield self.issueMacaroon()
                 url = build.recipe.git_repository.getCodebrowseUrl(
-                    username=LAUNCHPAD_SERVICES, password=macaroon_raw)
+                    username=LAUNCHPAD_SERVICES, password=macaroon_raw
+                )
                 args["git_repository"] = url
             else:
-                args["git_repository"] = (
-                    build.recipe.git_repository.git_https_url)
+                args[
+                    "git_repository"
+                ] = build.recipe.git_repository.git_https_url
         else:
             raise CannotBuild(
-                "Source repository for ~%s/%s has been deleted." %
-                (build.recipe.owner.name, build.recipe.name))
+                "Source repository for ~%s/%s has been deleted."
+                % (build.recipe.owner.name, build.recipe.name)
+            )
 
         if build.recipe.git_path != "HEAD":
             args["git_path"] = build.recipe.git_ref.name
@@ -174,9 +193,10 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
         # If the evaluated output file name is not within our
         # upload path, then we don't try to copy this or any
         # subsequent files.
-        if not os.path.normpath(file_path).startswith(upload_path + '/'):
+        if not os.path.normpath(file_path).startswith(upload_path + "/"):
             raise BuildDaemonError(
-                "Build returned a file named '%s'." % file_name)
+                "Build returned a file named '%s'." % file_name
+            )
 
     @defer.inlineCallbacks
     def _fetchIntermediaryFile(self, name, filemap, upload_path):
@@ -191,15 +211,14 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
     def _extractLayerFiles(self, upload_path, section, config, digests, files):
         # These are different sets of ids, in the same order
         # layer_id is the filename, diff_id is the internal (docker) id
-        for diff_id in config['rootfs']['diff_ids']:
+        for diff_id in config["rootfs"]["diff_ids"]:
             for digests_section in digests:
-                layer_id = digests_section[diff_id]['layer_id']
+                layer_id = digests_section[diff_id]["layer_id"]
                 # This is in the form '<id>/layer.tar', we only need the first
-                layer_filename = "{}.tar.gz".format(layer_id.split('/')[0])
-                digest = digests_section[diff_id]['digest']
+                layer_filename = "{}.tar.gz".format(layer_id.split("/")[0])
+                digest = digests_section[diff_id]["digest"]
                 # Check if the file already exists in the librarian
-                oci_file = getUtility(IOCIFileSet).getByLayerDigest(
-                    digest)
+                oci_file = getUtility(IOCIFileSet).getByLayerDigest(digest)
                 if oci_file:
                     librarian_file = oci_file.library_file
                     unsecure_file = removeSecurityProxy(oci_file)
@@ -212,7 +231,7 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
                 # so we can add it to the build artifacts
                 layer_path = os.path.join(upload_path, layer_filename)
                 librarian_file.open()
-                copy_and_close(librarian_file, open(layer_path, 'wb'))
+                copy_and_close(librarian_file, open(layer_path, "wb"))
 
     def _convertToRetrievableFile(self, upload_path, file_name, filemap):
         file_path = os.path.join(upload_path, file_name)
@@ -227,20 +246,25 @@ class OCIRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
         # We don't want to download all of the files that have been created,
         # just the ones that are mentioned in the manifest and config.
         manifest = yield self._fetchIntermediaryFile(
-            'manifest.json', filemap, upload_path)
+            "manifest.json", filemap, upload_path
+        )
         digests = yield self._fetchIntermediaryFile(
-            'digests.json', filemap, upload_path)
+            "digests.json", filemap, upload_path
+        )
 
         files = set()
         for section in manifest:
             config = yield self._fetchIntermediaryFile(
-                section['Config'], filemap, upload_path)
+                section["Config"], filemap, upload_path
+            )
             self._extractLayerFiles(
-                upload_path, section, config, digests, files)
+                upload_path, section, config, digests, files
+            )
 
         files_to_download = [
             self._convertToRetrievableFile(upload_path, filename, filemap)
-            for filename in files]
+            for filename in files
+        ]
         yield self._worker.getFiles(files_to_download, logger=logger)
 
     def verifySuccessfulBuild(self):
diff --git a/lib/lp/oci/model/ocirecipebuildjob.py b/lib/lp/oci/model/ocirecipebuildjob.py
index 4d7f098..f8d4f3c 100644
--- a/lib/lp/oci/model/ocirecipebuildjob.py
+++ b/lib/lp/oci/model/ocirecipebuildjob.py
@@ -4,41 +4,31 @@
 """OCIRecipe build jobs."""
 
 __all__ = [
-    'OCIRecipeBuildJob',
-    'OCIRecipeBuildJobType',
-    ]
+    "OCIRecipeBuildJob",
+    "OCIRecipeBuildJobType",
+]
 
-from datetime import timedelta
 import random
+from datetime import timedelta
 
+import transaction
 from lazr.delegates import delegate_to
-from lazr.enum import (
-    DBEnumeratedType,
-    DBItem,
-    )
+from lazr.enum import DBEnumeratedType, DBItem
 from storm.databases.postgres import JSON
-from storm.locals import (
-    Int,
-    Reference,
-    Store,
-    )
-import transaction
+from storm.locals import Int, Reference, Store
 from zope.component import getUtility
-from zope.interface import (
-    implementer,
-    provider,
-    )
+from zope.interface import implementer, provider
 
 from lp.app.errors import NotFoundError
 from lp.oci.interfaces.ocirecipebuildjob import (
     IOCIRecipeBuildJob,
     IOCIRegistryUploadJob,
     IOCIRegistryUploadJobSource,
-    )
+)
 from lp.oci.interfaces.ociregistryclient import (
     IOCIRegistryClient,
     OCIRegistryError,
-    )
+)
 from lp.services.config import config
 from lp.services.database.enumcol import DBEnum
 from lp.services.database.interfaces import IStore
@@ -46,13 +36,10 @@ from lp.services.database.locking import (
     AdvisoryLockHeld,
     LockType,
     try_advisory_lock,
-    )
+)
 from lp.services.database.stormbase import StormBase
 from lp.services.job.interfaces.job import JobStatus
-from lp.services.job.model.job import (
-    EnumeratedSubclass,
-    Job,
-    )
+from lp.services.job.model.job import EnumeratedSubclass, Job
 from lp.services.job.runner import BaseRunnableJob
 from lp.services.propertycache import get_property_cache
 from lp.services.webapp.snapshot import notify_modified
@@ -61,28 +48,31 @@ from lp.services.webapp.snapshot import notify_modified
 class OCIRecipeBuildJobType(DBEnumeratedType):
     """Values that `OCIBuildJobType.job_type` can take."""
 
-    REGISTRY_UPLOAD = DBItem(0, """
+    REGISTRY_UPLOAD = DBItem(
+        0,
+        """
         Registry upload
 
         This job uploads an OCI Image to the registry.
-        """)
+        """,
+    )
 
 
 @implementer(IOCIRecipeBuildJob)
 class OCIRecipeBuildJob(StormBase):
     """See `IOCIRecipeBuildJob`."""
 
-    __storm_table__ = 'OCIRecipeBuildJob'
+    __storm_table__ = "OCIRecipeBuildJob"
 
-    job_id = Int(name='job', primary=True, allow_none=False)
-    job = Reference(job_id, 'Job.id')
+    job_id = Int(name="job", primary=True, allow_none=False)
+    job = Reference(job_id, "Job.id")
 
-    build_id = Int(name='build', allow_none=False)
-    build = Reference(build_id, 'OCIRecipeBuild.id')
+    build_id = Int(name="build", allow_none=False)
+    build = Reference(build_id, "OCIRecipeBuild.id")
 
     job_type = DBEnum(enum=OCIRecipeBuildJobType, allow_none=True)
 
-    json_data = JSON('json_data', allow_none=False)
+    json_data = JSON("json_data", allow_none=False)
 
     def __init__(self, build, job_type, json_data, **job_args):
         """Constructor.
@@ -107,7 +97,6 @@ class OCIRecipeBuildJob(StormBase):
 
 @delegate_to(IOCIRecipeBuildJob)
 class OCIRecipeBuildJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
-
     def __init__(self, oci_build_job):
         self.context = oci_build_job
 
@@ -116,9 +105,13 @@ class OCIRecipeBuildJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
         try:
             build = self.build
             return "<%s for ~%s/%s/+oci/%s/+recipe/%s/+build/%d>" % (
-                self.__class__.__name__, build.recipe.owner.name,
+                self.__class__.__name__,
+                build.recipe.owner.name,
                 build.recipe.oci_project.pillar.name,
-                build.recipe.oci_project.name, build.recipe.name, build.id)
+                build.recipe.oci_project.name,
+                build.recipe.name,
+                build.id,
+            )
         except Exception:
             # There might be errors while trying to do the full
             # representation of this object (database transaction errors,
@@ -137,11 +130,13 @@ class OCIRecipeBuildJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
             or its `job_type` does not match the desired subclass.
         """
         oci_build_job = IStore(OCIRecipeBuildJob).get(
-            OCIRecipeBuildJob, job_id)
+            OCIRecipeBuildJob, job_id
+        )
         if oci_build_job.job_type != cls.class_job_type:
             raise NotFoundError(
-                "No object found with id %d and type %s" %
-                (job_id, cls.class_job_type.title))
+                "No object found with id %d and type %s"
+                % (job_id, cls.class_job_type.title)
+            )
         return cls(oci_build_job)
 
     @classmethod
@@ -151,18 +146,24 @@ class OCIRecipeBuildJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
             OCIRecipeBuildJob,
             OCIRecipeBuildJob.job_type == cls.class_job_type,
             OCIRecipeBuildJob.job == Job.id,
-            Job.id.is_in(Job.ready_jobs))
+            Job.id.is_in(Job.ready_jobs),
+        )
         return (cls(job) for job in jobs)
 
     def getOopsVars(self):
         """See `IRunnableJob`."""
         oops_vars = super().getOopsVars()
-        oops_vars.extend([
-            ('job_type', self.context.job_type.title),
-            ('build_id', self.context.build.id),
-            ('recipe_owner_id', self.context.build.recipe.owner.id),
-            ('oci_project_name', self.context.build.recipe.oci_project.name)
-            ])
+        oops_vars.extend(
+            [
+                ("job_type", self.context.job_type.title),
+                ("build_id", self.context.build.id),
+                ("recipe_owner_id", self.context.build.recipe.owner.id),
+                (
+                    "oci_project_name",
+                    self.context.build.recipe.oci_project.name,
+                ),
+            ]
+        )
         return oops_vars
 
 
@@ -183,7 +184,7 @@ class OCIRegistryUploadJob(OCIRecipeBuildJobDerived):
 
     # This is a known slow task that will exceed the timeouts for
     # the normal job queue, so put it on a queue with longer timeouts
-    task_queue = 'launchpad_job_slow'
+    task_queue = "launchpad_job_slow"
 
     soft_time_limit = timedelta(minutes=60)
     lease_duration = timedelta(minutes=60)
@@ -191,7 +192,10 @@ class OCIRegistryUploadJob(OCIRecipeBuildJobDerived):
     class ManifestListUploadError(Exception):
         pass
 
-    retry_error_types = (ManifestListUploadError, AdvisoryLockHeld,)
+    retry_error_types = (
+        ManifestListUploadError,
+        AdvisoryLockHeld,
+    )
     max_retries = 5
 
     config = config.IOCIRegistryUploadJobSource
@@ -205,7 +209,8 @@ class OCIRegistryUploadJob(OCIRecipeBuildJobDerived):
                 "build_uploaded": False,
             }
             oci_build_job = OCIRecipeBuildJob(
-                build, cls.class_job_type, json_data)
+                build, cls.class_job_type, json_data
+            )
             job = cls(oci_build_job)
             job.celeryRunOnCommit()
             del get_property_cache(build).last_registry_upload_job
@@ -220,23 +225,25 @@ class OCIRegistryUploadJob(OCIRecipeBuildJobDerived):
         delays = (10, 15, 20, 30)
         try:
             return timedelta(
-                minutes=delays[self.attempt_count - 1],
-                seconds=dithering_secs)
+                minutes=delays[self.attempt_count - 1], seconds=dithering_secs
+            )
         except IndexError:
             return timedelta(minutes=10, seconds=dithering_secs)
 
     # Ideally we'd just override Job._set_status or similar, but
     # lazr.delegates makes that difficult, so we use this to override all
     # the individual Job lifecycle methods instead.
-    def _do_lifecycle(self, method_name, manage_transaction=False,
-                      *args, **kwargs):
+    def _do_lifecycle(
+        self, method_name, manage_transaction=False, *args, **kwargs
+    ):
         edited_fields = set()
         with notify_modified(self.build, edited_fields) as before_modification:
             getattr(super(), method_name)(
-                *args, manage_transaction=manage_transaction, **kwargs)
+                *args, manage_transaction=manage_transaction, **kwargs
+            )
             upload_status = self.build.registry_upload_status
             if upload_status != before_modification.registry_upload_status:
-                edited_fields.add('registry_upload_status')
+                edited_fields.add("registry_upload_status")
         if edited_fields and manage_transaction:
             transaction.commit()
 
@@ -302,9 +309,9 @@ class OCIRegistryUploadJob(OCIRecipeBuildJobDerived):
         builds = set()
         for build, upload_jobs in uploads_per_build.items():
             has_finished_upload = any(
-                i.status == JobStatus.COMPLETED
-                or i.job_id == self.job_id
-                for i in upload_jobs)
+                i.status == JobStatus.COMPLETED or i.job_id == self.job_id
+                for i in upload_jobs
+            )
             if has_finished_upload:
                 builds.add(build)
         return builds
@@ -330,9 +337,11 @@ class OCIRegistryUploadJob(OCIRecipeBuildJobDerived):
         """See `IRunnableJob`."""
         client = getUtility(IOCIRegistryClient)
         try:
-            with try_advisory_lock(LockType.REGISTRY_UPLOAD,
-                                   self.build.recipe.id,
-                                   Store.of(self.build.recipe)):
+            with try_advisory_lock(
+                LockType.REGISTRY_UPLOAD,
+                self.build.recipe.id,
+                Store.of(self.build.recipe),
+            ):
                 try:
                     if not self.build_uploaded:
                         client.upload(self.build)
diff --git a/lib/lp/oci/model/ocirecipejob.py b/lib/lp/oci/model/ocirecipejob.py
index 4ba7d5e..f628f43 100644
--- a/lib/lp/oci/model/ocirecipejob.py
+++ b/lib/lp/oci/model/ocirecipejob.py
@@ -4,25 +4,19 @@
 """A build job for OCI Recipe."""
 
 __all__ = [
-    'OCIRecipeJob',
-    ]
+    "OCIRecipeJob",
+]
 
+import transaction
 from lazr.delegates import delegate_to
-from lazr.enum import (
-    DBEnumeratedType,
-    DBItem,
-    )
+from lazr.enum import DBEnumeratedType, DBItem
 from storm.databases.postgres import JSON
 from storm.expr import Desc
 from storm.properties import Int
 from storm.references import Reference
 from storm.store import EmptyResultSet
-import transaction
 from zope.component import getUtility
-from zope.interface import (
-    implementer,
-    provider,
-    )
+from zope.interface import implementer, provider
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.errors import NotFoundError
@@ -31,27 +25,21 @@ from lp.oci.interfaces.ocirecipe import IOCIRecipeSet
 from lp.oci.interfaces.ocirecipebuild import (
     OCIRecipeBuildRegistryUploadStatus,
     OCIRecipeBuildSetRegistryUploadStatus,
-    )
+)
 from lp.oci.interfaces.ocirecipejob import (
     IOCIRecipeJob,
     IOCIRecipeRequestBuildsJob,
     IOCIRecipeRequestBuildsJobSource,
-    )
+)
 from lp.oci.model.ocirecipebuild import OCIRecipeBuild
 from lp.registry.interfaces.person import IPersonSet
 from lp.services.config import config
 from lp.services.database.bulk import load_related
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.database.enumcol import DBEnum
-from lp.services.database.interfaces import (
-    IMasterStore,
-    IStore,
-    )
+from lp.services.database.interfaces import IMasterStore, IStore
 from lp.services.database.stormbase import StormBase
-from lp.services.job.model.job import (
-    EnumeratedSubclass,
-    Job,
-    )
+from lp.services.job.model.job import EnumeratedSubclass, Job
 from lp.services.job.runner import BaseRunnableJob
 from lp.services.mail.sendmail import format_address_for_person
 from lp.services.propertycache import cachedproperty
@@ -62,28 +50,31 @@ from lp.soyuz.interfaces.binarypackagebuild import BuildSetStatus
 class OCIRecipeJobType(DBEnumeratedType):
     """Values that `IOCIRecipeJob.job_type` can take."""
 
-    REQUEST_BUILDS = DBItem(0, """
+    REQUEST_BUILDS = DBItem(
+        0,
+        """
         Request builds
 
         This job requests builds of an OCI recipe.
-        """)
+        """,
+    )
 
 
 @implementer(IOCIRecipeJob)
 class OCIRecipeJob(StormBase):
     """See `IOCIRecipeJob`."""
 
-    __storm_table__ = 'OCIRecipeJob'
+    __storm_table__ = "OCIRecipeJob"
 
-    job_id = Int(name='job', primary=True, allow_none=False)
-    job = Reference(job_id, 'Job.id')
+    job_id = Int(name="job", primary=True, allow_none=False)
+    job = Reference(job_id, "Job.id")
 
-    recipe_id = Int(name='recipe', allow_none=False)
-    recipe = Reference(recipe_id, 'OCIRecipe.id')
+    recipe_id = Int(name="recipe", allow_none=False)
+    recipe = Reference(recipe_id, "OCIRecipe.id")
 
     job_type = DBEnum(enum=OCIRecipeJobType, allow_none=False)
 
-    metadata = JSON('json_data', allow_none=False)
+    metadata = JSON("json_data", allow_none=False)
 
     def __init__(self, recipe, job_type, metadata, **job_args):
         """Constructor.
@@ -108,14 +99,12 @@ class OCIRecipeJob(StormBase):
 
 @delegate_to(IOCIRecipeJob)
 class OCIRecipeJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
-
     def __init__(self, recipe_job):
         self.context = recipe_job
 
     def __repr__(self):
         """An informative representation of the job."""
-        return "<%s for %s>" % (
-            self.__class__.__name__, self.recipe)
+        return "<%s for %s>" % (self.__class__.__name__, self.recipe)
 
     @classmethod
     def get(cls, job_id):
@@ -129,8 +118,9 @@ class OCIRecipeJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
         recipe_job = IStore(IOCIRecipeJob).get(IOCIRecipeJob, job_id)
         if recipe_job.job_type != cls.class_job_type:
             raise NotFoundError(
-                "No object found with id %d and type %s" %
-                (job_id, cls.class_job_type.title))
+                "No object found with id %d and type %s"
+                % (job_id, cls.class_job_type.title)
+            )
         return cls(recipe_job)
 
     @classmethod
@@ -140,19 +130,22 @@ class OCIRecipeJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
             OCIRecipeJob,
             OCIRecipeJob.job_type == cls.class_job_type,
             OCIRecipeJob.job == Job.id,
-            Job.id.is_in(Job.ready_jobs))
+            Job.id.is_in(Job.ready_jobs),
+        )
         return (cls(job) for job in jobs)
 
     def getOopsVars(self):
         """See `IRunnableJob`."""
         oops_vars = super().getOopsVars()
-        oops_vars.extend([
-            ("job_id", self.context.job.id),
-            ("job_type", self.context.job_type.title),
-            ("oci_project_name", self.context.recipe.oci_project.name),
-            ("recipe_owner_name", self.context.recipe.owner.name),
-            ("recipe_name", self.context.recipe.name),
-            ])
+        oops_vars.extend(
+            [
+                ("job_id", self.context.job.id),
+                ("job_type", self.context.job_type.title),
+                ("oci_project_name", self.context.recipe.oci_project.name),
+                ("recipe_owner_name", self.context.recipe.owner.name),
+                ("recipe_name", self.context.recipe.name),
+            ]
+        )
         return oops_vars
 
 
@@ -173,9 +166,10 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
         metadata = {
             "requester": requester.id,
             "architectures": (
-                list(architectures) if architectures is not None else None),
+                list(architectures) if architectures is not None else None
+            ),
             # A dict of build_id: manifest location
-            "uploaded_manifests": {}
+            "uploaded_manifests": {},
         }
         recipe_job = OCIRecipeJob(recipe, cls.class_job_type, metadata)
         job = cls(recipe_job)
@@ -184,10 +178,15 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
 
     @classmethod
     def getByOCIRecipeAndID(cls, recipe, job_id):
-        job = IStore(OCIRecipeJob).find(
-            OCIRecipeJob,
-            OCIRecipeJob.recipe == recipe,
-            OCIRecipeJob.job_id == job_id).one()
+        job = (
+            IStore(OCIRecipeJob)
+            .find(
+                OCIRecipeJob,
+                OCIRecipeJob.recipe == recipe,
+                OCIRecipeJob.job_id == job_id,
+            )
+            .one()
+        )
         if job is None:
             raise NotFoundError("Could not find job ID %s" % job_id)
         return cls(job)
@@ -196,21 +195,24 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
     def findByOCIRecipe(cls, recipe, statuses=None, job_ids=None):
         conditions = [
             OCIRecipeJob.recipe == recipe,
-            OCIRecipeJob.job_type == cls.class_job_type]
+            OCIRecipeJob.job_type == cls.class_job_type,
+        ]
         if statuses is not None:
             conditions.append(Job._status.is_in(statuses))
         if job_ids is not None:
             conditions.append(OCIRecipeJob.job_id.is_in(job_ids))
-        oci_jobs = IStore(OCIRecipeJob).find(
-            OCIRecipeJob,
-            OCIRecipeJob.job_id == Job.id,
-            *conditions).order_by(Desc(OCIRecipeJob.job_id))
+        oci_jobs = (
+            IStore(OCIRecipeJob)
+            .find(OCIRecipeJob, OCIRecipeJob.job_id == Job.id, *conditions)
+            .order_by(Desc(OCIRecipeJob.job_id))
+        )
 
         def preload_jobs(rows):
             load_related(Job, rows, ["job_id"])
 
         return DecoratedResultSet(
-            oci_jobs, lambda oci_job: cls(oci_job), pre_iter_hook=preload_jobs)
+            oci_jobs, lambda oci_job: cls(oci_job), pre_iter_hook=preload_jobs
+        )
 
     def getOperationDescription(self):
         return "requesting builds of %s" % self.recipe
@@ -258,10 +260,15 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
         # Sort this by architecture/processor name, so it's consistent
         # when displayed
         if build_ids:
-            return IStore(OCIRecipeBuild).find(
-                OCIRecipeBuild, OCIRecipeBuild.id.is_in(build_ids),
-                OCIRecipeBuild.processor_id == Processor.id).order_by(
-                    Desc(Processor.name))
+            return (
+                IStore(OCIRecipeBuild)
+                .find(
+                    OCIRecipeBuild,
+                    OCIRecipeBuild.id.is_in(build_ids),
+                    OCIRecipeBuild.processor_id == Processor.id,
+                )
+                .order_by(Desc(Processor.name))
+            )
         else:
             return EmptyResultSet()
 
@@ -280,7 +287,9 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
         return {
             # Converts keys to integer since saving json to database
             # converts them to strings.
-            int(k): v for k, v in self.metadata["uploaded_manifests"].items()}
+            int(k): v
+            for k, v in self.metadata["uploaded_manifests"].items()
+        }
 
     def addUploadedManifest(self, build_id, manifest_info):
         self.metadata["uploaded_manifests"][int(build_id)] = manifest_info
@@ -290,8 +299,8 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
         builds = self.builds
         # This just returns a dict, but Zope is really helpful here
         status = removeSecurityProxy(
-                getUtility(IOCIRecipeSet).getStatusSummaryForBuilds(
-                    list(builds)))
+            getUtility(IOCIRecipeSet).getStatusSummaryForBuilds(list(builds))
+        )
 
         # This has a really long name!
         singleStatus = OCIRecipeBuildRegistryUploadStatus
@@ -299,13 +308,16 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
 
         # Set the pending upload status if either we're not done uploading,
         # or there was no upload requested in the first place (no push rules)
-        if status['status'] == BuildSetStatus.FULLYBUILT:
+        if status["status"] == BuildSetStatus.FULLYBUILT:
             upload_status = [
-                (x.registry_upload_status == singleStatus.UPLOADED or
-                 x.registry_upload_status == singleStatus.UNSCHEDULED)
-                for x in status['builds']]
+                (
+                    x.registry_upload_status == singleStatus.UPLOADED
+                    or x.registry_upload_status == singleStatus.UNSCHEDULED
+                )
+                for x in status["builds"]
+            ]
             if not all(upload_status):
-                status['status'] = BuildSetStatus.FULLYBUILT_PENDING
+                status["status"] = BuildSetStatus.FULLYBUILT_PENDING
 
         # Are we expecting an upload to be or to have been attempted?
         # This is slightly complicated as the upload depends on the push
@@ -318,36 +330,38 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
         # If all of the builds haven't finished, but the recipe currently
         # has push rules specified, then we will attempt an upload
         # in the future
-        if any(not x.date_finished and x.recipe.can_upload_to_registry
-               for x in builds):
+        if any(
+            not x.date_finished and x.recipe.can_upload_to_registry
+            for x in builds
+        ):
             upload_requested = True
-        status['upload_requested'] = upload_requested
+        status["upload_requested"] = upload_requested
 
         # Convert the set of registry statuses into a single line
         # for display
         upload_status = [x.registry_upload_status for x in builds]
         # Any of the builds failed
         if any(x == singleStatus.FAILEDTOUPLOAD for x in upload_status):
-            status['upload'] = setStatus.FAILEDTOUPLOAD
+            status["upload"] = setStatus.FAILEDTOUPLOAD
         # All of the builds uploaded
         elif all(x == singleStatus.UPLOADED for x in upload_status):
-            status['upload'] = setStatus.UPLOADED
+            status["upload"] = setStatus.UPLOADED
         # All of the builds are yet to attempt an upload
         elif all(x == singleStatus.UNSCHEDULED for x in upload_status):
-            status['upload'] = setStatus.UNSCHEDULED
+            status["upload"] = setStatus.UNSCHEDULED
         # Any of the builds have uploaded. Set after 'all of the builds'
         # have uploaded.
         elif any(x == singleStatus.UPLOADED for x in upload_status):
-            status['upload'] = setStatus.PARTIAL
+            status["upload"] = setStatus.PARTIAL
         # And if it's none of the above, we're waiting
         else:
-            status['upload'] = setStatus.PENDING
+            status["upload"] = setStatus.PENDING
 
         # Get the longest date and whether any of them are estimated
         # for the summary of the builds
         dates = [x.date for x in self.builds if x.date]
-        status['date'] = max(dates) if dates else None
-        status['date_estimated'] = any(x.estimate for x in self.builds)
+        status["date"] = max(dates) if dates else None
+        status["date_estimated"] = any(x.estimate for x in self.builds)
 
         return status
 
@@ -356,12 +370,15 @@ class OCIRecipeRequestBuildsJob(OCIRecipeJobDerived):
         requester = self.requester
         if requester is None:
             log.info(
-                "Skipping %r because the requester has been deleted." % self)
+                "Skipping %r because the requester has been deleted." % self
+            )
             return
         try:
             self.builds = self.recipe.requestBuildsFromJob(
-                requester, build_request=self.build_request,
-                architectures=self.architectures)
+                requester,
+                build_request=self.build_request,
+                architectures=self.architectures,
+            )
             self.error_message = None
         except self.retry_error_types:
             raise
diff --git a/lib/lp/oci/model/ocirecipesubscription.py b/lib/lp/oci/model/ocirecipesubscription.py
index 4de765e..ba657cf 100644
--- a/lib/lp/oci/model/ocirecipesubscription.py
+++ b/lib/lp/oci/model/ocirecipesubscription.py
@@ -3,15 +3,10 @@
 
 """OCIRecipe subscription model."""
 
-__all__ = [
-    'OCIRecipeSubscription'
-]
+__all__ = ["OCIRecipeSubscription"]
 
 import pytz
-from storm.properties import (
-    DateTime,
-    Int,
-    )
+from storm.properties import DateTime, Int
 from storm.references import Reference
 from zope.interface import implementer
 
@@ -26,12 +21,11 @@ from lp.services.database.stormbase import StormBase
 class OCIRecipeSubscription(StormBase):
     """A relationship between a person and an OCI recipe."""
 
-    __storm_table__ = 'OCIRecipeSubscription'
+    __storm_table__ = "OCIRecipeSubscription"
 
     id = Int(primary=True)
 
-    person_id = Int(
-        "person", allow_none=False, validator=validate_person)
+    person_id = Int("person", allow_none=False, validator=validate_person)
     person = Reference(person_id, "Person.id")
 
     recipe_id = Int("recipe", allow_none=False)
@@ -40,7 +34,8 @@ class OCIRecipeSubscription(StormBase):
     date_created = DateTime(allow_none=False, default=UTC_NOW, tzinfo=pytz.UTC)
 
     subscribed_by_id = Int(
-        "subscribed_by", allow_none=False, validator=validate_person)
+        "subscribed_by", allow_none=False, validator=validate_person
+    )
     subscribed_by = Reference(subscribed_by_id, "Person.id")
 
     def __init__(self, recipe, person, subscribed_by):
@@ -53,7 +48,9 @@ class OCIRecipeSubscription(StormBase):
         """See `IOCIRecipeSubscription`."""
         if user is None:
             return False
-        return (user.inTeam(self.recipe.owner) or
-                user.inTeam(self.person) or
-                user.inTeam(self.subscribed_by) or
-                IPersonRoles(user).in_admin)
+        return (
+            user.inTeam(self.recipe.owner)
+            or user.inTeam(self.person)
+            or user.inTeam(self.subscribed_by)
+            or IPersonRoles(user).in_admin
+        )
diff --git a/lib/lp/oci/model/ociregistryclient.py b/lib/lp/oci/model/ociregistryclient.py
index 1b71e33..4316efb 100644
--- a/lib/lp/oci/model/ociregistryclient.py
+++ b/lib/lp/oci/model/ociregistryclient.py
@@ -3,27 +3,22 @@
 
 """Client for talking to an OCI registry."""
 
-__all__ = [
-    'OCIRegistryClient'
-]
+__all__ = ["OCIRegistryClient"]
 
 import base64
-from functools import partial
 import hashlib
-from http.client import IncompleteRead
-from io import BytesIO
 import json
 import logging
 import re
 import tarfile
+from functools import partial
+from http.client import IncompleteRead
+from io import BytesIO
 from urllib.parse import urlparse
 
 import boto3
 from botocore.config import Config
-from requests.exceptions import (
-    ConnectionError,
-    HTTPError,
-    )
+from requests.exceptions import ConnectionError, HTTPError
 from requests.utils import parse_dict_header
 from tenacity import (
     before_log,
@@ -31,7 +26,7 @@ from tenacity import (
     retry_if_exception_type,
     stop_after_attempt,
     wait_fixed,
-    )
+)
 from zope.interface import implementer
 
 from lp.buildmaster.enums import BuildStatus
@@ -40,21 +35,20 @@ from lp.oci.interfaces.ociregistryclient import (
     IOCIRegistryClient,
     ManifestUploadFailed,
     MultipleOCIRegistryError,
-    )
+)
 from lp.services.config import config
 from lp.services.features import getFeatureFlag
 from lp.services.librarian.utils import EncodableLibraryFileAlias
 from lp.services.propertycache import cachedproperty
 from lp.services.timeout import urlfetch
 
-
 log = logging.getLogger(__name__)
 
 # Helper function to call urlfetch(use_proxy=True, *args, **kwargs)
 proxy_urlfetch = partial(urlfetch, use_proxy=True)
 
 
-OCI_AWS_BEARER_TOKEN_DOMAINS_FLAG = 'oci.push.aws.bearer_token_domains'
+OCI_AWS_BEARER_TOKEN_DOMAINS_FLAG = "oci.push.aws.bearer_token_domains"
 
 
 def is_aws_bearer_token_domain(domain):
@@ -64,13 +58,12 @@ def is_aws_bearer_token_domain(domain):
     if not domains:
         # We know that public ECR default domain is bearer token. If the
         # flag is not set, force it.
-        domains = 'public.ecr.aws'
+        domains = "public.ecr.aws"
     return any(domain.endswith(i) for i in domains.split())
 
 
 @implementer(IOCIRegistryClient)
 class OCIRegistryClient:
-
     @classmethod
     def _getJSONfile(cls, reference):
         """Read JSON out of a `LibraryFileAlias`."""
@@ -99,9 +92,12 @@ class OCIRegistryClient:
         wait=wait_fixed(3),
         before=before_log(log, logging.INFO),
         reraise=True,
-        retry=(retry_if_exception_type(ConnectionError) |
-               retry_if_exception_type(IncompleteRead)),
-        stop=stop_after_attempt(5))
+        retry=(
+            retry_if_exception_type(ConnectionError)
+            | retry_if_exception_type(IncompleteRead)
+        ),
+        stop=stop_after_attempt(5),
+    )
     def _upload(cls, digest, push_rule, fileobj, length, http_client):
         """Upload a blob to the registry, using a given digest.
 
@@ -115,8 +111,8 @@ class OCIRegistryClient:
         # Check if it already exists
         try:
             head_response = http_client.requestPath(
-                "/blobs/{}".format(digest),
-                method="HEAD")
+                "/blobs/{}".format(digest), method="HEAD"
+            )
             if head_response.status_code == 200:
                 log.info("{} already found".format(digest))
                 return
@@ -126,7 +122,8 @@ class OCIRegistryClient:
                 raise http_error
 
         post_response = http_client.requestPath(
-            "/blobs/uploads/", method="POST")
+            "/blobs/uploads/", method="POST"
+        )
 
         post_location = post_response.headers["Location"]
         query_parsed = {"digest": digest}
@@ -137,19 +134,23 @@ class OCIRegistryClient:
                 params=query_parsed,
                 data=fileobj,
                 headers={"Content-Length": str(length)},
-                method="PUT")
+                method="PUT",
+            )
         except HTTPError as http_error:
             put_response = http_error.response
         if put_response.status_code != 201:
             raise cls._makeRegistryError(
                 BlobUploadFailed,
                 "Upload of {} for {} failed".format(
-                    digest, push_rule.image_name),
-                put_response)
+                    digest, push_rule.image_name
+                ),
+                put_response,
+            )
 
     @classmethod
-    def _upload_layer(cls, digest, push_rule, lfa, http_client,
-                      upload_layers_uncompressed):
+    def _upload_layer(
+        cls, digest, push_rule, lfa, http_client, upload_layers_uncompressed
+    ):
         """Upload a layer blob to the registry.
 
         Uses _upload, but opens the LFA and extracts the necessary files
@@ -162,9 +163,9 @@ class OCIRegistryClient:
         lfa.open()
         try:
             if upload_layers_uncompressed:
-                with tarfile.open(fileobj=lfa, mode='r|gz') as un_zipped:
+                with tarfile.open(fileobj=lfa, mode="r|gz") as un_zipped:
                     for tarinfo in un_zipped:
-                        if tarinfo.name != 'layer.tar':
+                        if tarinfo.name != "layer.tar":
                             continue
                         fileobj = un_zipped.extractfile(tarinfo)
                         # XXX Work around requests handling of objects that
@@ -173,25 +174,34 @@ class OCIRegistryClient:
                         fileobj.len = tarinfo.size
                         try:
                             cls._upload(
-                                digest, push_rule, fileobj, tarinfo.size,
-                                http_client)
+                                digest,
+                                push_rule,
+                                fileobj,
+                                tarinfo.size,
+                                http_client,
+                            )
                         finally:
                             fileobj.close()
                         return tarinfo.size
             else:
                 size = lfa.content.filesize
                 wrapper = EncodableLibraryFileAlias(lfa)
-                cls._upload(
-                    digest, push_rule, wrapper, size,
-                    http_client)
+                cls._upload(digest, push_rule, wrapper, size, http_client)
                 return size
         finally:
             lfa.close()
 
     @classmethod
-    def _build_registry_manifest(cls, digests, config, config_json,
-                                 config_sha, preloaded_data, layer_sizes,
-                                 upload_layers_uncompressed):
+    def _build_registry_manifest(
+        cls,
+        digests,
+        config,
+        config_json,
+        config_sha,
+        preloaded_data,
+        layer_sizes,
+        upload_layers_uncompressed,
+    ):
         """Create an image manifest for the uploading image.
 
         This involves nearly everything as digests and lengths are required.
@@ -206,14 +216,16 @@ class OCIRegistryClient:
         # Create the initial manifest data with empty layer information
         manifest = {
             "schemaVersion": 2,
-            "mediaType":
-                "application/vnd.docker.distribution.manifest.v2+json",
+            "mediaType": (
+                "application/vnd.docker.distribution.manifest.v2+json"
+            ),
             "config": {
                 "mediaType": "application/vnd.docker.container.image.v1+json",
                 "size": len(config_json),
                 "digest": "sha256:{}".format(config_sha),
             },
-            "layers": []}
+            "layers": [],
+        }
 
         # Fill in the layer information
         # For uploading the compressed tar.gz for each layer we need
@@ -224,13 +236,19 @@ class OCIRegistryClient:
                 digest = unzipped_sha
             else:
                 digest = "sha256:{}".format(digests[unzipped_sha]["digest"])
-            log.info("Adding digest: %s for layer %s to manifest file." %
-                     (digest, digests[unzipped_sha]["layer_id"]))
-            manifest["layers"].append({
-                "mediaType":
-                    "application/vnd.docker.image.rootfs.diff.tar.gzip",
-                "size": layer_sizes[digest],
-                "digest": digest})
+            log.info(
+                "Adding digest: %s for layer %s to manifest file."
+                % (digest, digests[unzipped_sha]["layer_id"])
+            )
+            manifest["layers"].append(
+                {
+                    "mediaType": (
+                        "application/vnd.docker.image.rootfs.diff.tar.gzip"
+                    ),
+                    "size": layer_sizes[digest],
+                    "digest": digest,
+                }
+            )
         log.info("LP constructed the following manifest: %s", manifest)
         return manifest
 
@@ -246,8 +264,7 @@ class OCIRegistryClient:
         data = {}
         for section in manifest:
             # Load the matching config file for this section
-            config = cls._getJSONfile(
-                build.getFileByName(section['Config']))
+            config = cls._getJSONfile(build.getFileByName(section["Config"]))
             files = {"config_file": config}
             for diff_id in config["rootfs"]["diff_ids"]:
                 # We may have already seen this diff ID.
@@ -274,10 +291,10 @@ class OCIRegistryClient:
         if recipe.is_valid_branch_format:
             ref_name = recipe.git_ref.path
             # lp:1921865, account for tags in the correct format
-            if ref_name.startswith('refs/tags/'):
-                ref_name = ref_name[len('refs/tags/'):]
-            elif ref_name.startswith('refs/heads/'):
-                ref_name = ref_name[len('refs/heads/'):]
+            if ref_name.startswith("refs/tags/"):
+                ref_name = ref_name[len("refs/tags/") :]
+            elif ref_name.startswith("refs/heads/"):
+                ref_name = ref_name[len("refs/heads/") :]
             tags.append("{}_{}".format(ref_name, "edge"))
         else:
             tags.append("edge")
@@ -291,12 +308,14 @@ class OCIRegistryClient:
         url = "/manifests/{}".format(tag)
         accept = "application/vnd.docker.distribution.manifest.list.v2+json"
         response = http_client.requestPath(
-            url, method="GET", headers={"Accept": accept})
+            url, method="GET", headers={"Accept": accept}
+        )
         return response.json()
 
     @classmethod
-    def _uploadRegistryManifest(cls, http_client, registry_manifest,
-                                push_rule, tag, build=None):
+    def _uploadRegistryManifest(
+        cls, http_client, registry_manifest, push_rule, tag, build=None
+    ):
         """Uploads the build manifest, returning its content information.
 
         The returned information can be used to create a Manifest list
@@ -314,27 +333,30 @@ class OCIRegistryClient:
             tag = "sha256:{}".format(hashlib.sha256(data).hexdigest())
         size = len(data)
         content_type = registry_manifest.get(
-            "mediaType",
-            "application/vnd.docker.distribution.manifest.v2+json")
+            "mediaType", "application/vnd.docker.distribution.manifest.v2+json"
+        )
         try:
             manifest_response = http_client.requestPath(
                 "/manifests/{}".format(tag),
                 data=data,
                 headers={"Content-Type": content_type},
-                method="PUT")
+                method="PUT",
+            )
             digest = manifest_response.headers.get("Docker-Content-Digest")
         except HTTPError as http_error:
             manifest_response = http_error.response
         if manifest_response.status_code != 201:
             if build:
                 msg = "Failed to upload manifest for {} ({}) in {}".format(
-                    build.recipe.name, push_rule.registry_url, build.id)
+                    build.recipe.name, push_rule.registry_url, build.id
+                )
             else:
-                msg = ("Failed to upload manifest of manifests for"
-                       " {} ({})").format(
-                    push_rule.recipe.name, push_rule.registry_url)
+                msg = (
+                    "Failed to upload manifest of manifests for" " {} ({})"
+                ).format(push_rule.recipe.name, push_rule.registry_url)
             raise cls._makeRegistryError(
-                ManifestUploadFailed, msg, manifest_response)
+                ManifestUploadFailed, msg, manifest_response
+            )
         return {"digest": digest, "size": size}
 
     @classmethod
@@ -360,9 +382,9 @@ class OCIRegistryClient:
         # not been requested to be uploaded to registries yet.
         try:
             lfa.open()
-            with tarfile.open(fileobj=lfa, mode='r|gz') as un_zipped:
+            with tarfile.open(fileobj=lfa, mode="r|gz") as un_zipped:
                 for tarinfo in un_zipped:
-                    if tarinfo.name == 'layer.tar':
+                    if tarinfo.name == "layer.tar":
                         return True
             return False
         finally:
@@ -370,8 +392,8 @@ class OCIRegistryClient:
 
     @classmethod
     def _upload_to_push_rule(
-            cls, push_rule, build, manifest, digests, preloaded_data,
-            tag=None):
+        cls, push_rule, build, manifest, digests, preloaded_data, tag=None
+    ):
         http_client = RegistryHTTPClient.getInstance(push_rule)
 
         for section in manifest:
@@ -385,20 +407,23 @@ class OCIRegistryClient:
             first_id = config["rootfs"]["diff_ids"][0]
             lfa = file_data[first_id]
             upload_layers_uncompressed = cls.should_upload_layers_uncompressed(
-                lfa)
+                lfa
+            )
 
             for diff_id in config["rootfs"]["diff_ids"]:
                 if upload_layers_uncompressed:
                     digest = diff_id
                 else:
                     digest = "sha256:{}".format(
-                        file_data[diff_id].content.sha256)
+                        file_data[diff_id].content.sha256
+                    )
                 layer_size = cls._upload_layer(
                     digest,
                     push_rule,
                     file_data[diff_id],
                     http_client,
-                    upload_layers_uncompressed)
+                    upload_layers_uncompressed,
+                )
                 layer_sizes[digest] = layer_size
             # The config file is required in different forms, so we can
             # calculate the sha, work these out and upload
@@ -409,18 +434,25 @@ class OCIRegistryClient:
                 push_rule,
                 BytesIO(config_json),
                 len(config_json),
-                http_client)
+                http_client,
+            )
 
             # Build the registry manifest from the image manifest
             # and associated configs
             registry_manifest = cls._build_registry_manifest(
-                digests, config, config_json, config_sha,
+                digests,
+                config,
+                config_json,
+                config_sha,
                 preloaded_data[section["Config"]],
-                layer_sizes, upload_layers_uncompressed)
+                layer_sizes,
+                upload_layers_uncompressed,
+            )
 
             # Upload the registry manifest
             manifest = cls._uploadRegistryManifest(
-                http_client, registry_manifest, push_rule, tag, build)
+                http_client, registry_manifest, push_rule, tag, build
+            )
 
             # Save the uploaded manifest location, so we can use it in case
             # this is a multi-arch image upload.
@@ -453,8 +485,13 @@ class OCIRegistryClient:
             for push_rule in build.recipe.push_rules:
                 try:
                     cls._upload_to_push_rule(
-                        push_rule, build, manifest, digests,
-                        preloaded_data, tag=None)
+                        push_rule,
+                        build,
+                        manifest,
+                        digests,
+                        preloaded_data,
+                        tag=None,
+                    )
                 except Exception as e:
                     exceptions.append(e)
             if len(exceptions) == 1:
@@ -473,7 +510,7 @@ class OCIRegistryClient:
         "ppc64el": {"os": "linux", "architecture": "ppc64le"},
         "riscv64": {"os": "linux", "architecture": "riscv64"},
         "s390x": {"os": "linux", "architecture": "s390x"},
-        }
+    }
 
     @classmethod
     def _makePlatformSpecifiers(cls, arch):
@@ -499,14 +536,16 @@ class OCIRegistryClient:
         return platforms
 
     @classmethod
-    def makeMultiArchManifest(cls, http_client, push_rule, build_request,
-                              uploaded_builds, tag):
+    def makeMultiArchManifest(
+        cls, http_client, push_rule, build_request, uploaded_builds, tag
+    ):
         """Returns the multi-arch manifest content including all uploaded
         builds of the given build_request.
         """
         try:
             current_manifest = cls._getCurrentRegistryManifest(
-                http_client, tag)
+                http_client, tag
+            )
             # Check if the current manifest is not an incompatible version.
             version = current_manifest.get("schemaVersion", 1)
             if version < 2 or "manifests" not in current_manifest:
@@ -519,23 +558,29 @@ class OCIRegistryClient:
                 current_manifest = None
                 msg_tpl = (
                     "No multi-arch manifest on registry %s (image name: %s). "
-                    "Uploading a new one.")
-                log.info(msg_tpl % (
-                    push_rule.registry_url, push_rule.image_name))
+                    "Uploading a new one."
+                )
+                log.info(
+                    msg_tpl % (push_rule.registry_url, push_rule.image_name)
+                )
             else:
                 raise
         if current_manifest is None:
             current_manifest = {
                 "schemaVersion": 2,
-                "mediaType": ("application/"
-                              "vnd.docker.distribution.manifest.list.v2+json"),
-                "manifests": []}
+                "mediaType": (
+                    "application/"
+                    "vnd.docker.distribution.manifest.list.v2+json"
+                ),
+                "manifests": [],
+            }
         manifests = current_manifest["manifests"]
         for build in uploaded_builds:
             build_manifest = build_request.uploaded_manifests.get(build.id)
             if not build_manifest:
                 log.info(
-                    "No build manifest found for build {}".format(build.id))
+                    "No build manifest found for build {}".format(build.id)
+                )
                 continue
             log.info("Build manifest found for build {}".format(build.id))
             digest = build_manifest["digest"]
@@ -544,14 +589,18 @@ class OCIRegistryClient:
             platforms = cls._makePlatformSpecifiers(arch)
 
             manifest = next(
-                (m for m in manifests if m["platform"] in platforms), None)
+                (m for m in manifests if m["platform"] in platforms), None
+            )
             if manifest is None:
                 log.info(
                     "Appending multi-arch manifest for build {} "
-                    "with arch {}".format(build.id, arch))
+                    "with arch {}".format(build.id, arch)
+                )
                 manifest = {
-                    "mediaType": ("application/"
-                                  "vnd.docker.distribution.manifest.v2+json"),
+                    "mediaType": (
+                        "application/"
+                        "vnd.docker.distribution.manifest.v2+json"
+                    ),
                     "size": size,
                     "digest": digest,
                     "platform": platforms[0],
@@ -560,7 +609,8 @@ class OCIRegistryClient:
             else:
                 log.info(
                     "Updating multi-arch manifest for build {} "
-                    "with arch {}".format(build.id, arch))
+                    "with arch {}".format(build.id, arch)
+                )
                 manifest["digest"] = digest
                 manifest["size"] = size
                 manifest["platform"] = platforms[0]
@@ -577,10 +627,11 @@ class OCIRegistryClient:
         if build.status == BuildStatus.SUPERSEDED:
             return
         if build.hasMoreRecentBuild():
-            force_transition = (build.status == BuildStatus.FULLYBUILT)
+            force_transition = build.status == BuildStatus.FULLYBUILT
             build.updateStatus(
                 BuildStatus.SUPERSEDED,
-                force_invalid_transition=force_transition)
+                force_invalid_transition=force_transition,
+            )
 
     @classmethod
     def uploadManifestList(cls, build_request, uploaded_builds):
@@ -591,8 +642,11 @@ class OCIRegistryClient:
         # manifest files were not superseded by newer builds.
         for build in uploaded_builds:
             cls.updateSupersededBuilds(build)
-        uploaded_builds = [build for build in uploaded_builds
-                           if build.status != BuildStatus.SUPERSEDED]
+        uploaded_builds = [
+            build
+            for build in uploaded_builds
+            if build.status != BuildStatus.SUPERSEDED
+        ]
         if not uploaded_builds:
             return
         for push_rule in build_request.recipe.push_rules:
@@ -600,15 +654,24 @@ class OCIRegistryClient:
                 try:
                     http_client = RegistryHTTPClient.getInstance(push_rule)
                     multi_manifest_content = cls.makeMultiArchManifest(
-                        http_client, push_rule, build_request, uploaded_builds,
-                        tag)
+                        http_client,
+                        push_rule,
+                        build_request,
+                        uploaded_builds,
+                        tag,
+                    )
                     cls._uploadRegistryManifest(
-                        http_client, multi_manifest_content, push_rule, tag,
-                        build=None)
+                        http_client,
+                        multi_manifest_content,
+                        push_rule,
+                        tag,
+                        build=None,
+                    )
                 except Exception:
                     log.exception(
                         "Exception in uploading manifest for OCI build "
-                        "request {} with tag {}".format(build_request.id, tag))
+                        "request {} with tag {}".format(build_request.id, tag)
+                    )
                     raise
 
 
@@ -626,8 +689,8 @@ class RegistryHTTPClient:
     def credentials(self):
         """Returns a tuple of (username, password)."""
         auth = self.push_rule.registry_credentials.getCredentials()
-        if auth.get('username'):
-            return auth['username'], auth.get('password')
+        if auth.get("username"):
+            return auth["username"], auth.get("password")
         return None, None
 
     @property
@@ -664,10 +727,12 @@ class RegistryHTTPClient:
             # If we got back an "UNAUTHORIZED" error with "Www-Authenticate"
             # header, we should check what type of authorization we should use.
             header_key = "Www-Authenticate"
-            if (e.response.status_code == 401
-                    and header_key in e.response.headers):
-                auth_type = e.response.headers[header_key].split(' ', 1)[0]
-                if auth_type == 'Bearer':
+            if (
+                e.response.status_code == 401
+                and header_key in e.response.headers
+            ):
+                auth_type = e.response.headers[header_key].split(" ", 1)[0]
+                if auth_type == "Bearer":
                     # Note that, although we have the realm where to
                     # authenticate, we do not retrieve the authentication
                     # token here. Different operations might need different
@@ -676,11 +741,13 @@ class RegistryHTTPClient:
                     # are actually doing the operations and we will get info
                     # about what scope we will need.
                     return BearerTokenRegistryClient(push_rule)
-                elif auth_type == 'Basic':
+                elif auth_type == "Basic":
                     return RegistryHTTPClient(push_rule)
             raise OCIRegistryAuthenticationError(
-                "Unknown authentication type for %s registry" %
-                push_rule.registry_url, e)
+                "Unknown authentication type for %s registry"
+                % push_rule.registry_url,
+                e,
+            )
 
 
 class BearerTokenRegistryClient(RegistryHTTPClient):
@@ -702,8 +769,8 @@ class BearerTokenRegistryClient(RegistryHTTPClient):
         This method parses the appropriate header from the request and returns
         the token type and the key-value pairs that should be used as query
         parameters of the token GET request."""
-        instructions = request.headers['Www-Authenticate']
-        token_type, values = instructions.split(' ', 1)
+        instructions = request.headers["Www-Authenticate"]
+        token_type, values = instructions.split(" ", 1)
         dict_values = parse_dict_header(values)
         return token_type, dict_values
 
@@ -716,17 +783,20 @@ class BearerTokenRegistryClient(RegistryHTTPClient):
         except KeyError:
             raise OCIRegistryAuthenticationError(
                 "Auth instructions didn't include realm to get the token: %s"
-                % values)
+                % values
+            )
         # We should use the basic auth version for this request.
         response = super().request(
-            url, params=values, method="GET", auth=self.credentials)
+            url, params=values, method="GET", auth=self.credentials
+        )
         response.raise_for_status()
         response_data = response.json()
         try:
             self.auth_token = response_data["token"]
         except KeyError:
             raise OCIRegistryAuthenticationError(
-                "Could not get token from response data: %s" % response_data)
+                "Could not get token from response data: %s" % response_data
+            )
 
     def request(self, url, auth_retry=True, *args, **request_kwargs):
         """Does a request, handling authentication cycle in case of 401
@@ -743,8 +813,12 @@ class BearerTokenRegistryClient(RegistryHTTPClient):
             if auth_retry and e.response.status_code == 401:
                 self.authenticate(e.response)
                 return self.request(
-                    url, auth_retry=False, headers=headers,
-                    *args, **request_kwargs)
+                    url,
+                    auth_retry=False,
+                    headers=headers,
+                    *args,
+                    **request_kwargs,
+                )
             raise
 
 
@@ -756,23 +830,28 @@ class AWSAuthenticatorMixin:
 
     def _getClientParameters(self):
         if config.launchpad.http_proxy:
-            boto_config = Config(proxies={
-                'http': config.launchpad.http_proxy,
-                'https': config.launchpad.http_proxy})
+            boto_config = Config(
+                proxies={
+                    "http": config.launchpad.http_proxy,
+                    "https": config.launchpad.http_proxy,
+                }
+            )
         else:
             boto_config = Config()
         auth = self.push_rule.registry_credentials.getCredentials()
-        username, password = auth['username'], auth.get('password')
+        username, password = auth["username"], auth.get("password")
         region = self._getRegion()
         log.info("Trying to authenticate with AWS in region %s" % region)
         return dict(
             aws_access_key_id=username,
-            aws_secret_access_key=password, region_name=region,
-            config=boto_config)
+            aws_secret_access_key=password,
+            region_name=region,
+            config=boto_config,
+        )
 
     def _getBotoClient(self):
         params = self._getClientParameters()
-        client_type = 'ecr-public' if self.is_public_ecr else 'ecr'
+        client_type = "ecr-public" if self.is_public_ecr else "ecr"
         return boto3.client(client_type, **params)
 
     @property
@@ -792,7 +871,7 @@ class AWSAuthenticatorMixin:
         # Try to guess from the domain. The format should be something like
         # 'xxx.dkr.ecr.sa-east-1.amazonaws.com'. 'sa-east-1' is the region.
         domain = urlparse(self.push_rule.registry_url).netloc
-        if re.match(r'.+\.dkr\.ecr\..+\.amazonaws\.com', domain):
+        if re.match(r".+\.dkr\.ecr\..+\.amazonaws\.com", domain):
             return domain.split(".")[-3]
         raise OCIRegistryAuthenticationError("Unknown AWS region.")
 
@@ -809,25 +888,31 @@ class AWSAuthenticatorMixin:
             # both situations.
             if isinstance(auth_data, list):
                 auth_data = auth_data[0]
-            authorization_token = auth_data['authorizationToken']
-            username, password = base64.b64decode(
-                authorization_token).decode().split(':')
+            authorization_token = auth_data["authorizationToken"]
+            username, password = (
+                base64.b64decode(authorization_token).decode().split(":")
+            )
             return username, password
         except Exception as e:
-            log.error("Error trying to get authorization token for ECR "
-                      "registry: %s(%s)" % (e.__class__, e))
+            log.error(
+                "Error trying to get authorization token for ECR "
+                "registry: %s(%s)" % (e.__class__, e)
+            )
             raise OCIRegistryAuthenticationError(
-                "It was not possible to get AWS credentials for %s: %s" %
-                (self.push_rule.registry_url, e))
+                "It was not possible to get AWS credentials for %s: %s"
+                % (self.push_rule.registry_url, e)
+            )
 
 
 class AWSRegistryHTTPClient(AWSAuthenticatorMixin, RegistryHTTPClient):
     """AWS registry client with authentication flow based on basic auth."""
+
     pass
 
 
 class AWSRegistryBearerTokenClient(
-        AWSAuthenticatorMixin, BearerTokenRegistryClient):
-    """AWS registry client with authentication flow based on bearer token flow.
-    """
+    AWSAuthenticatorMixin, BearerTokenRegistryClient
+):
+    """AWS registry client with authentication flow based on bearer tokens."""
+
     pass
diff --git a/lib/lp/oci/model/ociregistrycredentials.py b/lib/lp/oci/model/ociregistrycredentials.py
index 28be770..2a6684e 100644
--- a/lib/lp/oci/model/ociregistrycredentials.py
+++ b/lib/lp/oci/model/ociregistrycredentials.py
@@ -4,20 +4,15 @@
 """Registry credentials for use by an `OCIPushRule`."""
 
 __all__ = [
-    'OCIRegistryCredentials',
-    'OCIRegistryCredentialsSet',
-    ]
+    "OCIRegistryCredentials",
+    "OCIRegistryCredentialsSet",
+]
 
 import base64
 import json
 
 from storm.databases.postgres import JSON
-from storm.locals import (
-    Int,
-    Reference,
-    Storm,
-    Unicode,
-    )
+from storm.locals import Int, Reference, Storm, Unicode
 from zope.component import getUtility
 from zope.interface import implementer
 from zope.schema import ValidationError
@@ -29,24 +24,21 @@ from lp.oci.interfaces.ociregistrycredentials import (
     IOCIRegistryCredentialsSet,
     OCIRegistryCredentialsAlreadyExist,
     OCIRegistryCredentialsNotOwner,
-    )
+)
 from lp.services.config import config
-from lp.services.crypto.interfaces import (
-    CryptoError,
-    IEncryptedContainer,
-    )
+from lp.services.crypto.interfaces import CryptoError, IEncryptedContainer
 from lp.services.crypto.model import NaClEncryptedContainerBase
 from lp.services.database.interfaces import IStore
 
 
 @implementer(IEncryptedContainer)
 class OCIRegistrySecretsEncryptedContainer(NaClEncryptedContainerBase):
-
     @property
     def public_key_bytes(self):
         if config.oci.registry_secrets_public_key is not None:
             return base64.b64decode(
-                config.oci.registry_secrets_public_key.encode('UTF-8'))
+                config.oci.registry_secrets_public_key.encode("UTF-8")
+            )
         else:
             return None
 
@@ -54,7 +46,8 @@ class OCIRegistrySecretsEncryptedContainer(NaClEncryptedContainerBase):
     def private_key_bytes(self):
         if config.oci.registry_secrets_private_key is not None:
             return base64.b64decode(
-                config.oci.registry_secrets_private_key.encode('UTF-8'))
+                config.oci.registry_secrets_private_key.encode("UTF-8")
+            )
         else:
             return None
 
@@ -63,30 +56,36 @@ def url_validator(allowed_schemes):
     def wrapped(obj, attr, value):
         if not validate_url(value, allowed_schemes):
             raise ValidationError(
-                "%s is not a valid URL for '%s' attribute" % (value, attr))
+                "%s is not a valid URL for '%s' attribute" % (value, attr)
+            )
         return value
+
     return wrapped
 
 
 @implementer(IOCIRegistryCredentials)
 class OCIRegistryCredentials(Storm):
 
-    __storm_table__ = 'OCIRegistryCredentials'
+    __storm_table__ = "OCIRegistryCredentials"
 
     id = Int(primary=True)
 
-    owner_id = Int(name='owner', allow_none=False)
-    owner = Reference(owner_id, 'Person.id')
+    owner_id = Int(name="owner", allow_none=False)
+    owner = Reference(owner_id, "Person.id")
 
     url = Unicode(
-        name="url", allow_none=False, validator=url_validator(
-            IOCIRegistryCredentials['url'].allowed_schemes))
+        name="url",
+        allow_none=False,
+        validator=url_validator(
+            IOCIRegistryCredentials["url"].allowed_schemes
+        ),
+    )
 
     _credentials = JSON(name="credentials", allow_none=True)
 
     # The list of dict keys that should not be encrypted when storing
     # _credentials attribute.
-    _UNENCRYPTED_CREDENTIALS_FIELDS = ['username', 'region']
+    _UNENCRYPTED_CREDENTIALS_FIELDS = ["username", "region"]
 
     def __init__(self, owner, url, credentials):
         self.owner = owner
@@ -97,8 +96,11 @@ class OCIRegistryCredentials(Storm):
         container = getUtility(IEncryptedContainer, "oci-registry-secrets")
         try:
             data = dict(self._credentials or {})
-            decrypted_data = json.loads(container.decrypt(
-                self._credentials['credentials_encrypted']).decode("UTF-8"))
+            decrypted_data = json.loads(
+                container.decrypt(
+                    self._credentials["credentials_encrypted"]
+                ).decode("UTF-8")
+            )
             if decrypted_data:
                 data.update(decrypted_data)
             data.pop("credentials_encrypted")
@@ -119,7 +121,9 @@ class OCIRegistryCredentials(Storm):
         # Encrypt the rest of the dict.
         data = {
             "credentials_encrypted": removeSecurityProxy(
-                container.encrypt(json.dumps(copy).encode('UTF-8')))}
+                container.encrypt(json.dumps(copy).encode("UTF-8"))
+            )
+        }
         # Put back the fields that shouldn't be encrypted.
         for field in self._UNENCRYPTED_CREDENTIALS_FIELDS:
             value = unencrypted_fields[field]
@@ -129,19 +133,19 @@ class OCIRegistryCredentials(Storm):
 
     @property
     def username(self):
-        return self._credentials.get('username')
+        return self._credentials.get("username")
 
     @username.setter
     def username(self, value):
-        self._credentials['username'] = value
+        self._credentials["username"] = value
 
     @property
     def region(self):
-        return self._credentials.get('region')
+        return self._credentials.get("region")
 
     @region.setter
     def region(self, value):
-        self._credentials['region'] = value
+        self._credentials["region"] = value
 
     def destroySelf(self):
         """See `IOCIRegistryCredentials`."""
@@ -150,23 +154,24 @@ class OCIRegistryCredentials(Storm):
 
 @implementer(IOCIRegistryCredentialsSet)
 class OCIRegistryCredentialsSet:
-
     def _checkOwner(self, registrant, owner):
         if not registrant.inTeam(owner):
             if owner.is_team:
                 raise OCIRegistryCredentialsNotOwner(
-                    "%s is not a member of %s." %
-                    (registrant.display_name, owner.display_name))
+                    "%s is not a member of %s."
+                    % (registrant.display_name, owner.display_name)
+                )
             else:
                 raise OCIRegistryCredentialsNotOwner(
-                    "%s cannot create credentials owned by %s." %
-                    (registrant.display_name, owner.display_name))
+                    "%s cannot create credentials owned by %s."
+                    % (registrant.display_name, owner.display_name)
+                )
 
     def _checkForExisting(self, owner, url, credentials):
         for existing in self.findByOwner(owner):
             url_match = existing.url == url
-            username_match = existing.username == credentials.get('username')
-            region_match = existing.region == credentials.get('region')
+            username_match = existing.username == credentials.get("username")
+            region_match = existing.region == credentials.get("region")
             if url_match and username_match and region_match:
                 return existing
         return None
@@ -179,8 +184,9 @@ class OCIRegistryCredentialsSet:
             raise OCIRegistryCredentialsAlreadyExist()
         return OCIRegistryCredentials(owner, url, credentials)
 
-    def getOrCreate(self, registrant, owner, url, credentials,
-                    override_owner=False):
+    def getOrCreate(
+        self, registrant, owner, url, credentials, override_owner=False
+    ):
         """See `IOCIRegistryCredentialsSet`."""
         if not override_owner:
             self._checkOwner(registrant, owner)
@@ -188,11 +194,12 @@ class OCIRegistryCredentialsSet:
         if existing:
             return existing
         return self.new(
-            registrant, owner, url, credentials, override_owner=override_owner)
+            registrant, owner, url, credentials, override_owner=override_owner
+        )
 
     def findByOwner(self, owner):
         """See `IOCIRegistryCredentialsSet`."""
         store = IStore(OCIRegistryCredentials)
         return store.find(
-            OCIRegistryCredentials,
-            OCIRegistryCredentials.owner == owner)
+            OCIRegistryCredentials, OCIRegistryCredentials.owner == owner
+        )
diff --git a/lib/lp/oci/security.py b/lib/lp/oci/security.py
index 0546859..d940124 100644
--- a/lib/lp/oci/security.py
+++ b/lib/lp/oci/security.py
@@ -9,12 +9,9 @@ from lp.app.security import (
     AnonymousAuthorization,
     AuthorizationBase,
     DelegatedAuthorization,
-    )
+)
 from lp.oci.interfaces.ocipushrule import IOCIPushRule
-from lp.oci.interfaces.ocirecipe import (
-    IOCIRecipe,
-    IOCIRecipeBuildRequest,
-    )
+from lp.oci.interfaces.ocirecipe import IOCIRecipe, IOCIRecipeBuildRequest
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
 from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription
 from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentials
@@ -22,17 +19,18 @@ from lp.security import AdminByBuilddAdmin
 
 
 class ViewOCIRecipeBuildRequest(DelegatedAuthorization):
-    permission = 'launchpad.View'
+    permission = "launchpad.View"
     usedfor = IOCIRecipeBuildRequest
 
     def __init__(self, obj):
-        super().__init__(obj, obj.recipe, 'launchpad.View')
+        super().__init__(obj, obj.recipe, "launchpad.View")
 
 
 class ViewOCIRecipe(AnonymousAuthorization):
     """Anyone can view public `IOCIRecipe`, but only subscribers can view
     private ones.
     """
+
     usedfor = IOCIRecipe
 
     def checkUnauthenticated(self):
@@ -43,13 +41,13 @@ class ViewOCIRecipe(AnonymousAuthorization):
 
 
 class EditOCIRecipe(AuthorizationBase):
-    permission = 'launchpad.Edit'
+    permission = "launchpad.Edit"
     usedfor = IOCIRecipe
 
     def checkAuthenticated(self, user):
         return (
-            user.isOwner(self.obj) or
-            user.in_commercial_admin or user.in_admin)
+            user.isOwner(self.obj) or user.in_commercial_admin or user.in_admin
+        )
 
 
 class AdminOCIRecipe(AuthorizationBase):
@@ -59,19 +57,20 @@ class AdminOCIRecipe(AuthorizationBase):
     settings, so they can only be changed by "PPA"/commercial admins, or by
     "PPA" self admins on OCI recipes that they can already edit.
     """
-    permission = 'launchpad.Admin'
+
+    permission = "launchpad.Admin"
     usedfor = IOCIRecipe
 
     def checkAuthenticated(self, user):
         if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
             return True
-        return (
-            user.in_ppa_self_admins
-            and EditOCIRecipe(self.obj).checkAuthenticated(user))
+        return user.in_ppa_self_admins and EditOCIRecipe(
+            self.obj
+        ).checkAuthenticated(user)
 
 
 class OCIRecipeSubscriptionEdit(AuthorizationBase):
-    permission = 'launchpad.Edit'
+    permission = "launchpad.Edit"
     usedfor = IOCIRecipeSubscription
 
     def checkAuthenticated(self, user):
@@ -84,14 +83,16 @@ class OCIRecipeSubscriptionEdit(AuthorizationBase):
         the OCI recipe owner is a team, then members of the team can edit
         the subscription.
         """
-        return (user.inTeam(self.obj.recipe.owner) or
-                user.inTeam(self.obj.person) or
-                user.inTeam(self.obj.subscribed_by) or
-                user.in_admin)
+        return (
+            user.inTeam(self.obj.recipe.owner)
+            or user.inTeam(self.obj.person)
+            or user.inTeam(self.obj.subscribed_by)
+            or user.in_admin
+        )
 
 
 class OCIRecipeSubscriptionView(AuthorizationBase):
-    permission = 'launchpad.View'
+    permission = "launchpad.View"
     usedfor = IOCIRecipeSubscription
 
     def checkUnauthenticated(self):
@@ -102,7 +103,7 @@ class OCIRecipeSubscriptionView(AuthorizationBase):
 
 
 class ViewOCIRecipeBuild(DelegatedAuthorization):
-    permission = 'launchpad.View'
+    permission = "launchpad.View"
     usedfor = IOCIRecipeBuild
 
     def iter_objects(self):
@@ -110,7 +111,7 @@ class ViewOCIRecipeBuild(DelegatedAuthorization):
 
 
 class EditOCIRecipeBuild(AdminByBuilddAdmin):
-    permission = 'launchpad.Edit'
+    permission = "launchpad.Edit"
     usedfor = IOCIRecipeBuild
 
     def checkAuthenticated(self, user):
@@ -131,27 +132,28 @@ class AdminOCIRecipeBuild(AdminByBuilddAdmin):
 
 
 class ViewOCIRegistryCredentials(AuthorizationBase):
-    permission = 'launchpad.View'
+    permission = "launchpad.View"
     usedfor = IOCIRegistryCredentials
 
     def checkAuthenticated(self, user):
         # This must be kept in sync with user_can_edit_credentials_for_owner
         # in lp.oci.interfaces.ociregistrycredentials.
-        return (
-            user.isOwner(self.obj) or
-            user.in_admin)
+        return user.isOwner(self.obj) or user.in_admin
 
 
 class ViewOCIPushRule(AnonymousAuthorization):
     """Anyone can view an `IOCIPushRule`."""
+
     usedfor = IOCIPushRule
 
 
 class OCIPushRuleEdit(AuthorizationBase):
-    permission = 'launchpad.Edit'
+    permission = "launchpad.Edit"
     usedfor = IOCIPushRule
 
     def checkAuthenticated(self, user):
         return (
-            user.isOwner(self.obj.recipe) or
-            user.in_commercial_admin or user.in_admin)
+            user.isOwner(self.obj.recipe)
+            or user.in_commercial_admin
+            or user.in_admin
+        )
diff --git a/lib/lp/oci/subscribers/ocirecipebuild.py b/lib/lp/oci/subscribers/ocirecipebuild.py
index 3394826..abdc541 100644
--- a/lib/lp/oci/subscribers/ocirecipebuild.py
+++ b/lib/lp/oci/subscribers/ocirecipebuild.py
@@ -21,12 +21,22 @@ def _trigger_oci_recipe_build_webhook(build, action):
         payload = {
             "recipe_build": canonical_url(build, force_local_path=True),
             "action": action,
-            }
-        payload.update(compose_webhook_payload(
-            IOCIRecipeBuild, build,
-            ["recipe", "build_request", "status", "registry_upload_status"]))
+        }
+        payload.update(
+            compose_webhook_payload(
+                IOCIRecipeBuild,
+                build,
+                [
+                    "recipe",
+                    "build_request",
+                    "status",
+                    "registry_upload_status",
+                ],
+            )
+        )
         getUtility(IWebhookSet).trigger(
-            build.recipe, "oci-recipe:build:0.1", payload)
+            build.recipe, "oci-recipe:build:0.1", payload
+        )
 
 
 def oci_recipe_build_created(build, event):
@@ -42,11 +52,14 @@ def oci_recipe_build_modified(build, event):
         if status_changed or registry_changed:
             _trigger_oci_recipe_build_webhook(build, "status-changed")
         if status_changed:
-            if (build.recipe.can_upload_to_registry and
-                    build.status == BuildStatus.FULLYBUILT):
+            if (
+                build.recipe.can_upload_to_registry
+                and build.status == BuildStatus.FULLYBUILT
+            ):
                 log.info("Scheduling upload of %r to registries." % build)
                 getUtility(IOCIRegistryUploadJobSource).create(build)
             else:
                 log.info(
-                    "%r is not configured for upload to registries." %
-                    build.recipe)
+                    "%r is not configured for upload to registries."
+                    % build.recipe
+                )
diff --git a/lib/lp/oci/tests/helpers.py b/lib/lp/oci/tests/helpers.py
index 336b078..242f6cb 100644
--- a/lib/lp/oci/tests/helpers.py
+++ b/lib/lp/oci/tests/helpers.py
@@ -8,36 +8,39 @@ __all__ = []
 import base64
 
 from nacl.public import PrivateKey
-from testtools.matchers import (
-    AfterPreprocessing,
-    MatchesAll,
-    )
+from testtools.matchers import AfterPreprocessing, MatchesAll
 from zope.security.proxy import removeSecurityProxy
 
 from lp.oci.interfaces.ocirecipe import (
     OCI_RECIPE_ALLOW_CREATE,
     OCI_RECIPE_PRIVATE_FEATURE_FLAG,
-    )
+)
 from lp.services.features.testing import FeatureFixture
 
 
 class OCIConfigHelperMixin:
-
     def setConfig(self, feature_flags=None):
         self.private_key = PrivateKey.generate()
         self.pushConfig(
             "oci",
             registry_secrets_public_key=base64.b64encode(
-                bytes(self.private_key.public_key)).decode("UTF-8"))
+                bytes(self.private_key.public_key)
+            ).decode("UTF-8"),
+        )
         self.pushConfig(
             "oci",
             registry_secrets_private_key=base64.b64encode(
-                bytes(self.private_key)).decode("UTF-8"))
+                bytes(self.private_key)
+            ).decode("UTF-8"),
+        )
         # Default feature flags for our tests
         feature_flags = feature_flags or {}
-        feature_flags.update({
-            OCI_RECIPE_ALLOW_CREATE: 'on',
-            OCI_RECIPE_PRIVATE_FEATURE_FLAG: 'on'})
+        feature_flags.update(
+            {
+                OCI_RECIPE_ALLOW_CREATE: "on",
+                OCI_RECIPE_PRIVATE_FEATURE_FLAG: "on",
+            }
+        )
         self.useFixture(FeatureFixture(feature_flags))
 
 
@@ -53,4 +56,6 @@ class MatchesOCIRegistryCredentials(MatchesAll):
             main_matcher,
             AfterPreprocessing(
                 lambda matchee: removeSecurityProxy(matchee).getCredentials(),
-                credentials_matcher))
+                credentials_matcher,
+            ),
+        )
diff --git a/lib/lp/oci/tests/test_ocipushrule.py b/lib/lp/oci/tests/test_ocipushrule.py
index 043c6b9..aecdc7b 100644
--- a/lib/lp/oci/tests/test_ocipushrule.py
+++ b/lib/lp/oci/tests/test_ocipushrule.py
@@ -12,13 +12,10 @@ from lp.oci.interfaces.ocipushrule import (
     IOCIPushRule,
     IOCIPushRuleSet,
     OCIPushRuleAlreadyExists,
-    )
+)
 from lp.oci.model.ociregistrycredentials import OCIRegistryCredentials
 from lp.oci.tests.helpers import OCIConfigHelperMixin
-from lp.testing import (
-    person_logged_in,
-    TestCaseWithFactory,
-    )
+from lp.testing import TestCaseWithFactory, person_logged_in
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -37,25 +34,26 @@ class TestOCIPushRule(OCIConfigHelperMixin, TestCaseWithFactory):
     def test_change_attribute(self):
         push_rule = self.factory.makeOCIPushRule()
         with person_logged_in(push_rule.recipe.owner):
-            push_rule.setNewImageName('new image name')
+            push_rule.setNewImageName("new image name")
 
         found_rule = push_rule.recipe.push_rules[0]
-        self.assertEqual(found_rule.image_name, 'new image name')
+        self.assertEqual(found_rule.image_name, "new image name")
 
     def test_change_image_name_existing(self):
         first = self.factory.makeOCIPushRule(image_name="first")
         second = self.factory.makeOCIPushRule(
             image_name="second",
-            registry_credentials=first.registry_credentials)
+            registry_credentials=first.registry_credentials,
+        )
         self.assertRaises(
-            OCIPushRuleAlreadyExists,
-            second.setNewImageName,
-            first.image_name)
+            OCIPushRuleAlreadyExists, second.setNewImageName, first.image_name
+        )
 
     def test_username_retrieval(self):
         credentials = self.factory.makeOCIRegistryCredentials()
         push_rule = self.factory.makeOCIPushRule(
-            registry_credentials=credentials)
+            registry_credentials=credentials
+        )
         self.assertEqual(credentials.username, push_rule.username)
 
     def test_valid_registry_url(self):
@@ -65,7 +63,11 @@ class TestOCIPushRule(OCIConfigHelperMixin, TestCaseWithFactory):
         self.assertRaisesRegex(
             ValidationError,
             "asdf://foo.com is not a valid URL for 'url' attribute",
-            OCIRegistryCredentials, owner, url, credentials)
+            OCIRegistryCredentials,
+            owner,
+            url,
+            credentials,
+        )
         # Avoid trying to flush the incomplete object on cleanUp.
         Store.of(owner).rollback()
 
@@ -89,14 +91,17 @@ class TestOCIPushRuleSet(OCIConfigHelperMixin, TestCaseWithFactory):
         push_rule = getUtility(IOCIPushRuleSet).new(
             recipe=recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
+            image_name=image_name,
+        )
 
         self.assertThat(
             push_rule,
             MatchesStructure.byEquality(
                 recipe=recipe,
                 registry_credentials=registry_credentials,
-                image_name=image_name))
+                image_name=image_name,
+            ),
+        )
 
     def test_new_with_existing(self):
         recipe = self.factory.makeOCIRecipe()
@@ -105,9 +110,13 @@ class TestOCIPushRuleSet(OCIConfigHelperMixin, TestCaseWithFactory):
         getUtility(IOCIPushRuleSet).new(
             recipe=recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
+            image_name=image_name,
+        )
 
         self.assertRaises(
             OCIPushRuleAlreadyExists,
             getUtility(IOCIPushRuleSet).new,
-            recipe, registry_credentials, image_name)
+            recipe,
+            registry_credentials,
+            image_name,
+        )
diff --git a/lib/lp/oci/tests/test_ocirecipe.py b/lib/lp/oci/tests/test_ocirecipe.py
index a697657..0262c88 100644
--- a/lib/lp/oci/tests/test_ocirecipe.py
+++ b/lib/lp/oci/tests/test_ocirecipe.py
@@ -3,9 +3,10 @@
 
 """Tests for OCI image building recipe functionality."""
 
-from datetime import datetime
 import json
+from datetime import datetime
 
+import transaction
 from fixtures import FakeLogger
 from storm.exceptions import LostObjectError
 from storm.store import Store
@@ -18,14 +19,10 @@ from testtools.matchers import (
     MatchesDict,
     MatchesSetwise,
     MatchesStructure,
-    )
-import transaction
+)
 from zope.component import getUtility
 from zope.schema import ValidationError
-from zope.security.interfaces import (
-    ForbiddenAttribute,
-    Unauthorized,
-    )
+from zope.security.interfaces import ForbiddenAttribute, Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.enums import InformationType
@@ -35,53 +32,47 @@ from lp.code.tests.helpers import GitHostingFixture
 from lp.oci.interfaces.ocipushrule import (
     IOCIPushRuleSet,
     OCIPushRuleAlreadyExists,
-    )
+)
 from lp.oci.interfaces.ocirecipe import (
+    OCI_RECIPE_ALLOW_CREATE,
+    OCI_RECIPE_BUILD_DISTRIBUTION,
+    OCI_RECIPE_WEBHOOKS_FEATURE_FLAG,
     CannotModifyOCIRecipeProcessor,
     DuplicateOCIRecipeName,
     IOCIRecipe,
     IOCIRecipeSet,
     NoSourceForOCIRecipe,
     NoSuchOCIRecipe,
-    OCI_RECIPE_ALLOW_CREATE,
-    OCI_RECIPE_BUILD_DISTRIBUTION,
-    OCI_RECIPE_WEBHOOKS_FEATURE_FLAG,
     OCIRecipeBuildAlreadyPending,
     OCIRecipeBuildRequestStatus,
     OCIRecipeNotOwner,
     OCIRecipePrivacyMismatch,
     UsingDistributionCredentials,
-    )
+)
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuildSet
 from lp.oci.interfaces.ocirecipejob import IOCIRecipeRequestBuildsJobSource
 from lp.oci.interfaces.ociregistrycredentials import (
     OCIRegistryCredentialsNotOwner,
-    )
+)
 from lp.oci.tests.helpers import (
     MatchesOCIRegistryCredentials,
     OCIConfigHelperMixin,
-    )
+)
 from lp.registry.enums import (
     BranchSharingPolicy,
     PersonVisibility,
     TeamMembershipPolicy,
-    )
+)
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifactSource,
     IAccessPolicyArtifactSource,
     IAccessPolicySource,
-    )
+)
 from lp.registry.interfaces.ociproject import OCIProjectRecipeInvalid
 from lp.registry.interfaces.series import SeriesStatus
-from lp.registry.model.accesspolicy import (
-    AccessArtifact,
-    AccessArtifactGrant,
-    )
+from lp.registry.model.accesspolicy import AccessArtifact, AccessArtifactGrant
 from lp.services.config import config
-from lp.services.database.constants import (
-    ONE_DAY_AGO,
-    UTC_NOW,
-    )
+from lp.services.database.constants import ONE_DAY_AGO, UTC_NOW
 from lp.services.database.interfaces import IStore
 from lp.services.database.sqlbase import flush_database_caches
 from lp.services.features.testing import FeatureFixture
@@ -91,19 +82,16 @@ from lp.services.webapp.publisher import canonical_url
 from lp.services.webapp.snapshot import notify_modified
 from lp.services.webhooks.testing import LogsScheduledWebhooks
 from lp.testing import (
+    StormStatementRecorder,
+    TestCaseWithFactory,
     admin_logged_in,
     api_url,
     login_admin,
     login_person,
     person_logged_in,
-    StormStatementRecorder,
-    TestCaseWithFactory,
-    )
+)
 from lp.testing.dbuser import dbuser
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadFunctionalLayer,
-    )
+from lp.testing.layers import DatabaseFunctionalLayer, LaunchpadFunctionalLayer
 from lp.testing.pages import webservice_for_person
 
 
@@ -113,7 +101,7 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
 
     def setUp(self):
         super().setUp()
-        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
+        self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_implements_interface(self):
         target = self.factory.makeOCIRecipe()
@@ -125,7 +113,7 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         project = self.factory.makeProduct()
         oci_project = self.factory.makeOCIProject(pillar=project)
         recipe = self.factory.makeOCIRecipe(oci_project=oci_project)
-        self.assertEqual('ubuntu', recipe.distribution.name)
+        self.assertEqual("ubuntu", recipe.distribution.name)
 
     def test_feature_flag_distribution_on_project_pillar(self):
         # With the OCI_RECIPE_BUILD_DISTRIBUTION feature flag set, we should
@@ -147,9 +135,11 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         with FeatureFixture({OCI_RECIPE_BUILD_DISTRIBUTION: "banana-distro"}):
             expected_msg = (
                 "'banana-distro' is not a valid value for feature flag '%s'"
-                % OCI_RECIPE_BUILD_DISTRIBUTION)
+                % OCI_RECIPE_BUILD_DISTRIBUTION
+            )
             self.assertRaisesWithContent(
-                ValueError, expected_msg, getattr, recipe, 'distribution')
+                ValueError, expected_msg, getattr, recipe, "distribution"
+            )
 
     def test_distribution_for_distro_based_oci_project(self):
         # For distribution-based OCI projects, we should use OCIProject's
@@ -171,81 +161,98 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         with notify_modified(removeSecurityProxy(recipe), ["name"]):
             pass
         self.assertSqlAttributeEqualsDate(
-            recipe, "date_last_modified", UTC_NOW)
+            recipe, "date_last_modified", UTC_NOW
+        )
 
     def test_checkRequestBuild(self):
         ocirecipe = removeSecurityProxy(self.factory.makeOCIRecipe())
         unrelated_person = self.factory.makePerson()
         self.assertRaises(
-            OCIRecipeNotOwner,
-            ocirecipe._checkRequestBuild,
-            unrelated_person)
+            OCIRecipeNotOwner, ocirecipe._checkRequestBuild, unrelated_person
+        )
 
-    def getDistroArchSeries(self, distroseries, proc_name="386",
-                            arch_tag="i386"):
+    def getDistroArchSeries(
+        self, distroseries, proc_name="386", arch_tag="i386"
+    ):
         processor = getUtility(IProcessorSet).getByName(proc_name)
 
         das = self.factory.makeDistroArchSeries(
-            distroseries=distroseries, architecturetag=arch_tag,
-            processor=processor)
+            distroseries=distroseries,
+            architecturetag=arch_tag,
+            processor=processor,
+        )
         fake_chroot = self.factory.makeLibraryFileAlias(
-            filename="fake_chroot.tar.gz", db_only=True)
+            filename="fake_chroot.tar.gz", db_only=True
+        )
         das.addOrUpdateChroot(fake_chroot)
         return das
 
     def test_hasPendingBuilds(self):
         ocirecipe = removeSecurityProxy(
-            self.factory.makeOCIRecipe(require_virtualized=False))
+            self.factory.makeOCIRecipe(require_virtualized=False)
+        )
         distro = ocirecipe.oci_project.distribution
         series = self.factory.makeDistroSeries(
-            distribution=distro, status=SeriesStatus.CURRENT)
+            distribution=distro, status=SeriesStatus.CURRENT
+        )
 
         arch_series_386 = self.getDistroArchSeries(series, "386", "386")
         arch_series_hppa = self.getDistroArchSeries(series, "hppa", "hppa")
 
         # Successful build (i386)
         self.factory.makeOCIRecipeBuild(
-            recipe=ocirecipe, status=BuildStatus.FULLYBUILT,
-            distro_arch_series=arch_series_386)
+            recipe=ocirecipe,
+            status=BuildStatus.FULLYBUILT,
+            distro_arch_series=arch_series_386,
+        )
         # Failed build (i386)
         self.factory.makeOCIRecipeBuild(
-            recipe=ocirecipe, status=BuildStatus.FAILEDTOBUILD,
-            distro_arch_series=arch_series_386)
+            recipe=ocirecipe,
+            status=BuildStatus.FAILEDTOBUILD,
+            distro_arch_series=arch_series_386,
+        )
         # Building build (i386)
         self.factory.makeOCIRecipeBuild(
-            recipe=ocirecipe, status=BuildStatus.BUILDING,
-            distro_arch_series=arch_series_386)
+            recipe=ocirecipe,
+            status=BuildStatus.BUILDING,
+            distro_arch_series=arch_series_386,
+        )
         # Building build (hppa)
         self.factory.makeOCIRecipeBuild(
-            recipe=ocirecipe, status=BuildStatus.BUILDING,
-            distro_arch_series=arch_series_hppa)
+            recipe=ocirecipe,
+            status=BuildStatus.BUILDING,
+            distro_arch_series=arch_series_hppa,
+        )
 
+        self.assertFalse(ocirecipe._hasPendingBuilds([arch_series_386]))
+        self.assertFalse(ocirecipe._hasPendingBuilds([arch_series_hppa]))
         self.assertFalse(
-            ocirecipe._hasPendingBuilds([arch_series_386]))
-        self.assertFalse(
-            ocirecipe._hasPendingBuilds([arch_series_hppa]))
-        self.assertFalse(
-            ocirecipe._hasPendingBuilds([arch_series_386, arch_series_hppa]))
+            ocirecipe._hasPendingBuilds([arch_series_386, arch_series_hppa])
+        )
 
         # The only pending build, for i386.
         self.factory.makeOCIRecipeBuild(
-            recipe=ocirecipe, status=BuildStatus.NEEDSBUILD,
-            distro_arch_series=arch_series_386)
+            recipe=ocirecipe,
+            status=BuildStatus.NEEDSBUILD,
+            distro_arch_series=arch_series_386,
+        )
 
-        self.assertTrue(
-            ocirecipe._hasPendingBuilds([arch_series_386]))
-        self.assertFalse(
-            ocirecipe._hasPendingBuilds([arch_series_hppa]))
+        self.assertTrue(ocirecipe._hasPendingBuilds([arch_series_386]))
+        self.assertFalse(ocirecipe._hasPendingBuilds([arch_series_hppa]))
         self.assertFalse(
-            ocirecipe._hasPendingBuilds([arch_series_386, arch_series_hppa]))
+            ocirecipe._hasPendingBuilds([arch_series_386, arch_series_hppa])
+        )
 
         # Add a pending for hppa
         self.factory.makeOCIRecipeBuild(
-            recipe=ocirecipe, status=BuildStatus.NEEDSBUILD,
-            distro_arch_series=arch_series_hppa)
+            recipe=ocirecipe,
+            status=BuildStatus.NEEDSBUILD,
+            distro_arch_series=arch_series_hppa,
+        )
 
         self.assertTrue(
-            ocirecipe._hasPendingBuilds([arch_series_386, arch_series_hppa]))
+            ocirecipe._hasPendingBuilds([arch_series_386, arch_series_hppa])
+        )
 
     def test_requestBuild(self):
         ocirecipe = self.factory.makeOCIRecipe()
@@ -261,148 +268,214 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         self.assertRaises(
             OCIRecipeBuildAlreadyPending,
             ocirecipe.requestBuild,
-            ocirecipe.owner, oci_arch)
+            ocirecipe.owner,
+            oci_arch,
+        )
 
     def test_requestBuild_triggers_webhooks(self):
         # Requesting a build triggers webhooks.
         logger = self.useFixture(FakeLogger())
-        with FeatureFixture({OCI_RECIPE_WEBHOOKS_FEATURE_FLAG: "on",
-                             OCI_RECIPE_ALLOW_CREATE: 'on'}):
+        with FeatureFixture(
+            {
+                OCI_RECIPE_WEBHOOKS_FEATURE_FLAG: "on",
+                OCI_RECIPE_ALLOW_CREATE: "on",
+            }
+        ):
             recipe = self.factory.makeOCIRecipe()
             oci_arch = self.factory.makeOCIRecipeArch(recipe=recipe)
             hook = self.factory.makeWebhook(
-                target=recipe, event_types=["oci-recipe:build:0.1"])
+                target=recipe, event_types=["oci-recipe:build:0.1"]
+            )
             build = recipe.requestBuild(recipe.owner, oci_arch)
 
         expected_payload = {
             "recipe_build": Equals(
-                canonical_url(build, force_local_path=True)),
+                canonical_url(build, force_local_path=True)
+            ),
             "action": Equals("created"),
             "recipe": Equals(canonical_url(recipe, force_local_path=True)),
             "build_request": Is(None),
             "status": Equals("Needs building"),
-            'registry_upload_status': Equals('Unscheduled'),
-            }
+            "registry_upload_status": Equals("Unscheduled"),
+        }
         with person_logged_in(recipe.owner):
             delivery = hook.deliveries.one()
             self.assertThat(
-                delivery, MatchesStructure(
+                delivery,
+                MatchesStructure(
                     event_type=Equals("oci-recipe:build:0.1"),
-                    payload=MatchesDict(expected_payload)))
+                    payload=MatchesDict(expected_payload),
+                ),
+            )
             with dbuser(config.IWebhookDeliveryJobSource.dbuser):
                 self.assertEqual(
-                    "<WebhookDeliveryJob for webhook %d on %r>" % (
-                        hook.id, hook.target),
-                    repr(delivery))
+                    "<WebhookDeliveryJob for webhook %d on %r>"
+                    % (hook.id, hook.target),
+                    repr(delivery),
+                )
                 self.assertThat(
                     logger.output,
-                    LogsScheduledWebhooks([
-                        (hook, "oci-recipe:build:0.1",
-                         MatchesDict(expected_payload))]))
+                    LogsScheduledWebhooks(
+                        [
+                            (
+                                hook,
+                                "oci-recipe:build:0.1",
+                                MatchesDict(expected_payload),
+                            )
+                        ]
+                    ),
+                )
 
     def test_requestBuildsFromJob_creates_builds(self):
-        ocirecipe = removeSecurityProxy(self.factory.makeOCIRecipe(
-            require_virtualized=False))
+        ocirecipe = removeSecurityProxy(
+            self.factory.makeOCIRecipe(require_virtualized=False)
+        )
         owner = ocirecipe.owner
         distro = ocirecipe.oci_project.distribution
         series = self.factory.makeDistroSeries(
-            distribution=distro, status=SeriesStatus.CURRENT)
+            distribution=distro, status=SeriesStatus.CURRENT
+        )
         arch_series_386 = self.getDistroArchSeries(series, "386", "386")
         arch_series_hppa = self.getDistroArchSeries(series, "hppa", "hppa")
         job = getUtility(IOCIRecipeRequestBuildsJobSource).create(
-            ocirecipe, owner)
+            ocirecipe, owner
+        )
 
         with person_logged_in(job.requester):
             builds = ocirecipe.requestBuildsFromJob(
-                job.requester, build_request=job.build_request)
-            self.assertThat(builds, MatchesSetwise(
-                MatchesStructure(
-                    recipe=Equals(ocirecipe),
-                    processor=Equals(arch_series_386.processor)),
-                MatchesStructure(
-                    recipe=Equals(ocirecipe),
-                    processor=Equals(arch_series_hppa.processor))
-            ))
+                job.requester, build_request=job.build_request
+            )
+            self.assertThat(
+                builds,
+                MatchesSetwise(
+                    MatchesStructure(
+                        recipe=Equals(ocirecipe),
+                        processor=Equals(arch_series_386.processor),
+                    ),
+                    MatchesStructure(
+                        recipe=Equals(ocirecipe),
+                        processor=Equals(arch_series_hppa.processor),
+                    ),
+                ),
+            )
 
     def test_requestBuildsFromJob_unauthorized_user(self):
         ocirecipe = removeSecurityProxy(self.factory.makeOCIRecipe())
         self.factory.makeDistroSeries(
             distribution=ocirecipe.oci_project.distribution,
-            status=SeriesStatus.CURRENT)
+            status=SeriesStatus.CURRENT,
+        )
         another_user = self.factory.makePerson()
         job = getUtility(IOCIRecipeRequestBuildsJobSource).create(
-            ocirecipe, another_user)
+            ocirecipe, another_user
+        )
         with person_logged_in(job.requester):
             self.assertRaises(
                 OCIRecipeNotOwner,
                 ocirecipe.requestBuildsFromJob,
-                job.requester, build_request=job.build_request)
+                job.requester,
+                build_request=job.build_request,
+            )
 
     def test_requestBuildsFromJob_with_pending_jobs(self):
         ocirecipe = removeSecurityProxy(self.factory.makeOCIRecipe())
         distro = ocirecipe.oci_project.distribution
         series = self.factory.makeDistroSeries(
-            distribution=distro, status=SeriesStatus.CURRENT)
+            distribution=distro, status=SeriesStatus.CURRENT
+        )
         arch_series_386 = self.getDistroArchSeries(series, "386", "386")
         self.factory.makeOCIRecipeBuild(
-            recipe=ocirecipe, status=BuildStatus.NEEDSBUILD,
-            distro_arch_series=arch_series_386)
+            recipe=ocirecipe,
+            status=BuildStatus.NEEDSBUILD,
+            distro_arch_series=arch_series_386,
+        )
         job = getUtility(IOCIRecipeRequestBuildsJobSource).create(
-            ocirecipe, ocirecipe.owner)
+            ocirecipe, ocirecipe.owner
+        )
 
         with person_logged_in(job.requester):
             self.assertRaises(
                 OCIRecipeBuildAlreadyPending,
                 ocirecipe.requestBuildsFromJob,
-                job.requester, build_request=job.build_request)
+                job.requester,
+                build_request=job.build_request,
+            )
 
     def test_requestBuildsFromJob_triggers_webhooks(self):
         # requestBuildsFromJob triggers webhooks, and the payload includes a
         # link to the build request.
-        self.useFixture(FeatureFixture({
-            OCI_RECIPE_WEBHOOKS_FEATURE_FLAG: "on",
-            OCI_RECIPE_ALLOW_CREATE: "on",
-            }))
-        recipe = removeSecurityProxy(self.factory.makeOCIRecipe(
-            require_virtualized=False))
+        self.useFixture(
+            FeatureFixture(
+                {
+                    OCI_RECIPE_WEBHOOKS_FEATURE_FLAG: "on",
+                    OCI_RECIPE_ALLOW_CREATE: "on",
+                }
+            )
+        )
+        recipe = removeSecurityProxy(
+            self.factory.makeOCIRecipe(require_virtualized=False)
+        )
         distro = recipe.oci_project.distribution
         series = self.factory.makeDistroSeries(
-            distribution=distro, status=SeriesStatus.CURRENT)
+            distribution=distro, status=SeriesStatus.CURRENT
+        )
         self.getDistroArchSeries(series, "386", "386")
         self.getDistroArchSeries(series, "hppa", "hppa")
         job = getUtility(IOCIRecipeRequestBuildsJobSource).create(
-            recipe, recipe.owner)
+            recipe, recipe.owner
+        )
 
         logger = self.useFixture(FakeLogger())
         hook = self.factory.makeWebhook(
-            target=job.recipe, event_types=["oci-recipe:build:0.1"])
+            target=job.recipe, event_types=["oci-recipe:build:0.1"]
+        )
         with person_logged_in(job.requester):
             builds = recipe.requestBuildsFromJob(
-                job.requester, build_request=job.build_request)
+                job.requester, build_request=job.build_request
+            )
             self.assertEqual(2, len(builds))
             payload_matchers = [
-                MatchesDict({
-                    "recipe_build": Equals(canonical_url(
-                        build, force_local_path=True)),
-                    "action": Equals("created"),
-                    "recipe": Equals(canonical_url(
-                        job.recipe, force_local_path=True)),
-                    "build_request": Equals(canonical_url(
-                        job.build_request, force_local_path=True)),
-                    "status": Equals("Needs building"),
-                    "registry_upload_status": Equals("Unscheduled"),
-                    })
-                for build in builds]
-            self.assertThat(hook.deliveries, MatchesSetwise(*(
-                MatchesStructure(
-                    event_type=Equals("oci-recipe:build:0.1"),
-                    payload=payload_matcher)
-                for payload_matcher in payload_matchers)))
+                MatchesDict(
+                    {
+                        "recipe_build": Equals(
+                            canonical_url(build, force_local_path=True)
+                        ),
+                        "action": Equals("created"),
+                        "recipe": Equals(
+                            canonical_url(job.recipe, force_local_path=True)
+                        ),
+                        "build_request": Equals(
+                            canonical_url(
+                                job.build_request, force_local_path=True
+                            )
+                        ),
+                        "status": Equals("Needs building"),
+                        "registry_upload_status": Equals("Unscheduled"),
+                    }
+                )
+                for build in builds
+            ]
+            self.assertThat(
+                hook.deliveries,
+                MatchesSetwise(
+                    *(
+                        MatchesStructure(
+                            event_type=Equals("oci-recipe:build:0.1"),
+                            payload=payload_matcher,
+                        )
+                        for payload_matcher in payload_matchers
+                    )
+                ),
+            )
             self.assertThat(
                 logger.output,
-                LogsScheduledWebhooks([
-                    (hook, "oci-recipe:build:0.1", payload_matcher)
-                    for payload_matcher in payload_matchers]))
+                LogsScheduledWebhooks(
+                    [
+                        (hook, "oci-recipe:build:0.1", payload_matcher)
+                        for payload_matcher in payload_matchers
+                    ]
+                ),
+            )
 
     def test_destroySelf(self):
         self.setConfig()
@@ -411,12 +484,15 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         build_request = oci_recipe.requestBuilds(oci_recipe.owner, ["386"])
         build_ids = [
             self.factory.makeOCIRecipeBuild(
-                recipe=oci_recipe, build_request=build_request).id
-            for _ in range(3)]
+                recipe=oci_recipe, build_request=build_request
+            ).id
+            for _ in range(3)
+        ]
         # Create associated push rules:
         push_rule_ids = [
             self.factory.makeOCIPushRule(recipe=oci_recipe).id
-            for i in range(3)]
+            for i in range(3)
+        ]
 
         with person_logged_in(oci_recipe.owner):
             oci_recipe.destroySelf()
@@ -426,12 +502,17 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
             self.assertIsNone(getUtility(IOCIRecipeBuildSet).getByID(build_id))
         for push_rule_id in push_rule_ids:
             self.assertIsNone(
-                getUtility(IOCIPushRuleSet).getByID(push_rule_id))
+                getUtility(IOCIPushRuleSet).getByID(push_rule_id)
+            )
 
     def test_related_webhooks_deleted(self):
         owner = self.factory.makePerson()
-        with FeatureFixture({OCI_RECIPE_WEBHOOKS_FEATURE_FLAG: "on",
-                             OCI_RECIPE_ALLOW_CREATE: 'on'}):
+        with FeatureFixture(
+            {
+                OCI_RECIPE_WEBHOOKS_FEATURE_FLAG: "on",
+                OCI_RECIPE_ALLOW_CREATE: "on",
+            }
+        ):
             recipe = self.factory.makeOCIRecipe(registrant=owner, owner=owner)
             webhook = self.factory.makeWebhook(target=recipe)
         with person_logged_in(recipe.owner):
@@ -443,8 +524,10 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
     def test_getBuilds(self):
         # Test the various getBuilds methods.
         oci_recipe = self.factory.makeOCIRecipe()
-        builds = [self.factory.makeOCIRecipeBuild(recipe=oci_recipe)
-                  for x in range(3)]
+        builds = [
+            self.factory.makeOCIRecipeBuild(recipe=oci_recipe)
+            for x in range(3)
+        ]
         # We want the latest builds first.
         builds.reverse()
 
@@ -469,7 +552,8 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         instacancelled.updateStatus(BuildStatus.CANCELLED)
         self.assertEqual([fullybuilt, instacancelled], list(oci_recipe.builds))
         self.assertEqual(
-            [fullybuilt, instacancelled], list(oci_recipe.completed_builds))
+            [fullybuilt, instacancelled], list(oci_recipe.completed_builds)
+        )
         self.assertEqual([], list(oci_recipe.pending_builds))
 
     def test_push_rules(self):
@@ -490,18 +574,30 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         url = self.factory.getUniqueURL()
         image_name = self.factory.getUniqueUnicode()
         credentials = {
-            "username": "test-username", "password": "test-password"}
+            "username": "test-username",
+            "password": "test-password",
+        }
 
         with person_logged_in(recipe.registrant):
             push_rule = recipe.newPushRule(
-                recipe.registrant, url, image_name, credentials,
-                credentials_owner=recipe.registrant)
-            self.assertThat(push_rule, MatchesStructure(
-                image_name=Equals(image_name),
-                registry_credentials=MatchesOCIRegistryCredentials(
-                    MatchesStructure.byEquality(
-                        owner=recipe.registrant, url=url),
-                    Equals(credentials))))
+                recipe.registrant,
+                url,
+                image_name,
+                credentials,
+                credentials_owner=recipe.registrant,
+            )
+            self.assertThat(
+                push_rule,
+                MatchesStructure(
+                    image_name=Equals(image_name),
+                    registry_credentials=MatchesOCIRegistryCredentials(
+                        MatchesStructure.byEquality(
+                            owner=recipe.registrant, url=url
+                        ),
+                        Equals(credentials),
+                    ),
+                ),
+            )
             self.assertEqual(push_rule, recipe.push_rules[0])
 
     def test_newPushRule_default_owner(self):
@@ -512,31 +608,48 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         url = self.factory.getUniqueURL()
         image_name = self.factory.getUniqueUnicode()
         credentials = {
-            "username": "test-username", "password": "test-password"}
+            "username": "test-username",
+            "password": "test-password",
+        }
 
         with person_logged_in(recipe.registrant):
             push_rule = recipe.newPushRule(
-                recipe.registrant, url, image_name, credentials)
-            self.assertThat(push_rule, MatchesStructure(
-                image_name=Equals(image_name),
-                registry_credentials=MatchesOCIRegistryCredentials(
-                    MatchesStructure.byEquality(owner=recipe.owner, url=url),
-                    Equals(credentials))))
+                recipe.registrant, url, image_name, credentials
+            )
+            self.assertThat(
+                push_rule,
+                MatchesStructure(
+                    image_name=Equals(image_name),
+                    registry_credentials=MatchesOCIRegistryCredentials(
+                        MatchesStructure.byEquality(
+                            owner=recipe.owner, url=url
+                        ),
+                        Equals(credentials),
+                    ),
+                ),
+            )
             self.assertEqual(push_rule, recipe.push_rules[0])
 
     def test_newPushRule_invalid_url(self):
         self.setConfig()
         recipe = self.factory.makeOCIRecipe()
-        url = 'asdf://foo.com'
+        url = "asdf://foo.com"
         image_name = self.factory.getUniqueUnicode()
         credentials = {
-            "username": "test-username", "password": "test-password"}
+            "username": "test-username",
+            "password": "test-password",
+        }
 
         with person_logged_in(recipe.owner):
             self.assertRaises(
-                ValidationError, recipe.newPushRule,
-                recipe.owner, url, image_name, credentials,
-                credentials_owner=recipe.owner)
+                ValidationError,
+                recipe.newPushRule,
+                recipe.owner,
+                url,
+                image_name,
+                credentials,
+                credentials_owner=recipe.owner,
+            )
             # Avoid trying to flush the incomplete object on cleanUp.
             Store.of(recipe).rollback()
 
@@ -546,17 +659,27 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         url = self.factory.getUniqueURL()
         image_name = self.factory.getUniqueUnicode()
         credentials = {
-            "username": "test-username", "password": "test-password"}
+            "username": "test-username",
+            "password": "test-password",
+        }
 
         with person_logged_in(recipe.owner):
             recipe.newPushRule(
-                recipe.owner, url, image_name, credentials,
-                credentials_owner=recipe.owner)
+                recipe.owner,
+                url,
+                image_name,
+                credentials,
+                credentials_owner=recipe.owner,
+            )
             self.assertRaises(
                 OCIPushRuleAlreadyExists,
                 recipe.newPushRule,
-                recipe.owner, url, image_name, credentials,
-                credentials_owner=recipe.owner)
+                recipe.owner,
+                url,
+                image_name,
+                credentials,
+                credentials_owner=recipe.owner,
+            )
 
     def test_newPushRule_not_owner(self):
         # If the registrant is not the owner or a member of the owner team,
@@ -566,25 +689,41 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         url = self.factory.getUniqueURL()
         image_name = self.factory.getUniqueUnicode()
         credentials = {
-            "username": "test-username", "password": "test-password"}
+            "username": "test-username",
+            "password": "test-password",
+        }
         other_person = self.factory.makePerson()
         other_team = self.factory.makeTeam(owner=other_person)
 
         with person_logged_in(recipe.registrant):
             expected_message = "%s cannot create credentials owned by %s." % (
-                recipe.registrant.display_name, other_person.display_name)
+                recipe.registrant.display_name,
+                other_person.display_name,
+            )
             with ExpectedException(
-                    OCIRegistryCredentialsNotOwner, expected_message):
+                OCIRegistryCredentialsNotOwner, expected_message
+            ):
                 recipe.newPushRule(
-                    recipe.registrant, url, image_name, credentials,
-                    credentials_owner=other_person)
+                    recipe.registrant,
+                    url,
+                    image_name,
+                    credentials,
+                    credentials_owner=other_person,
+                )
             expected_message = "%s is not a member of %s." % (
-                recipe.registrant.display_name, other_team.display_name)
+                recipe.registrant.display_name,
+                other_team.display_name,
+            )
             with ExpectedException(
-                    OCIRegistryCredentialsNotOwner, expected_message):
+                OCIRegistryCredentialsNotOwner, expected_message
+            ):
                 recipe.newPushRule(
-                    recipe.registrant, url, image_name, credentials,
-                    credentials_owner=other_team)
+                    recipe.registrant,
+                    url,
+                    image_name,
+                    credentials,
+                    credentials_owner=other_team,
+                )
 
     def test_newPushRule_distribution_credentials(self):
         # If the OCIRecipe is in a Distribution with credentials set
@@ -598,41 +737,54 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
             distribution.oci_registry_credentials = credentials
             project.setOfficialRecipeStatus(recipe, True)
 
-        url = 'asdf://foo.com'
+        url = "asdf://foo.com"
         image_name = self.factory.getUniqueUnicode()
         credentials = {
-            "username": "test-username", "password": "test-password"}
+            "username": "test-username",
+            "password": "test-password",
+        }
 
         with person_logged_in(recipe.owner):
             with ExpectedException(UsingDistributionCredentials):
                 recipe.newPushRule(
-                    recipe.registrant, url, image_name, credentials,
-                    credentials_owner=recipe.registrant)
+                    recipe.registrant,
+                    url,
+                    image_name,
+                    credentials,
+                    credentials_owner=recipe.registrant,
+                )
 
     def test_set_official_directly_is_forbidden(self):
         recipe = self.factory.makeOCIRecipe()
         self.assertRaises(
-            ForbiddenAttribute, setattr, recipe, 'official', True)
+            ForbiddenAttribute, setattr, recipe, "official", True
+        )
 
     def test_set_recipe_as_official_for_oci_project(self):
         distro = self.factory.makeDistribution()
         owner = distro.owner
         login_person(owner)
         oci_project1 = self.factory.makeOCIProject(
-            registrant=owner, pillar=distro)
+            registrant=owner, pillar=distro
+        )
         oci_project2 = self.factory.makeOCIProject(
-            registrant=owner, pillar=distro)
+            registrant=owner, pillar=distro
+        )
 
         oci_proj1_recipes = [
             self.factory.makeOCIRecipe(
-                oci_project=oci_project1, registrant=owner, owner=owner)
-            for _ in range(3)]
+                oci_project=oci_project1, registrant=owner, owner=owner
+            )
+            for _ in range(3)
+        ]
 
         # Recipes for project 2
         oci_proj2_recipes = [
             self.factory.makeOCIRecipe(
-                oci_project=oci_project2, registrant=owner, owner=owner)
-            for _ in range(2)]
+                oci_project=oci_project2, registrant=owner, owner=owner
+            )
+            for _ in range(2)
+        ]
 
         self.assertTrue(oci_project1.getOfficialRecipes().is_empty())
         self.assertTrue(oci_project2.getOfficialRecipes().is_empty())
@@ -650,7 +802,8 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
 
         self.assertTrue(oci_project2.getOfficialRecipes().is_empty())
         self.assertEqual(
-            oci_proj1_recipes[0], oci_project1.getOfficialRecipes()[0])
+            oci_proj1_recipes[0], oci_project1.getOfficialRecipes()[0]
+        )
         self.assertTrue(oci_proj1_recipes[0].official)
         for recipe in oci_proj1_recipes[1:] + oci_proj2_recipes:
             self.assertFalse(recipe.official)
@@ -669,28 +822,36 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         owner = distro.owner
         login_person(owner)
         oci_project = self.factory.makeOCIProject(
-            registrant=owner, pillar=distro)
+            registrant=owner, pillar=distro
+        )
         another_oci_project = self.factory.makeOCIProject(
-            registrant=owner, pillar=distro)
+            registrant=owner, pillar=distro
+        )
 
         recipe = self.factory.makeOCIRecipe(
-            oci_project=oci_project, registrant=owner)
+            oci_project=oci_project, registrant=owner
+        )
 
         self.assertRaises(
             OCIProjectRecipeInvalid,
-            another_oci_project.setOfficialRecipeStatus, recipe, True)
+            another_oci_project.setOfficialRecipeStatus,
+            recipe,
+            True,
+        )
 
     def test_permission_check_on_setOfficialRecipe(self):
         distro = self.factory.makeDistribution()
         owner = distro.owner
         login_person(owner)
         oci_project = self.factory.makeOCIProject(
-            registrant=owner, pillar=distro)
+            registrant=owner, pillar=distro
+        )
 
         another_user = self.factory.makePerson()
         with person_logged_in(another_user):
             self.assertRaises(
-                Unauthorized, getattr, oci_project, 'setOfficialRecipeStatus')
+                Unauthorized, getattr, oci_project, "setOfficialRecipeStatus"
+            )
 
     def test_oci_project_get_recipe_by_name_and_owner(self):
         owner = self.factory.makePerson()
@@ -698,16 +859,22 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         oci_project = self.factory.makeOCIProject(registrant=owner)
 
         recipe = self.factory.makeOCIRecipe(
-            oci_project=oci_project, registrant=owner, owner=owner,
-            name="foo-recipe")
+            oci_project=oci_project,
+            registrant=owner,
+            owner=owner,
+            name="foo-recipe",
+        )
 
         self.assertEqual(
             recipe,
-            oci_project.getRecipeByNameAndOwner(recipe.name, owner.name))
+            oci_project.getRecipeByNameAndOwner(recipe.name, owner.name),
+        )
         self.assertIsNone(
-            oci_project.getRecipeByNameAndOwner(recipe.name, "someone"))
+            oci_project.getRecipeByNameAndOwner(recipe.name, "someone")
+        )
         self.assertIsNone(
-            oci_project.getRecipeByNameAndOwner("some-recipe", owner.name))
+            oci_project.getRecipeByNameAndOwner("some-recipe", owner.name)
+        )
 
     def test_search_recipe_from_oci_project(self):
         owner = self.factory.makePerson()
@@ -716,13 +883,17 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         another_oci_project = self.factory.makeOCIProject(registrant=owner)
 
         recipe1 = self.factory.makeOCIRecipe(
-            name="a something", oci_project=oci_project, registrant=owner)
+            name="a something", oci_project=oci_project, registrant=owner
+        )
         recipe2 = self.factory.makeOCIRecipe(
-            name="banana", oci_project=oci_project, registrant=owner)
+            name="banana", oci_project=oci_project, registrant=owner
+        )
         # Recipe from another project.
         self.factory.makeOCIRecipe(
-            name="something too", oci_project=another_oci_project,
-            registrant=owner)
+            name="something too",
+            oci_project=another_oci_project,
+            registrant=owner,
+        )
 
         self.assertEqual([recipe1], list(oci_project.searchRecipes("somet")))
         self.assertEqual([recipe2], list(oci_project.searchRecipes("bana")))
@@ -740,25 +911,38 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
 
         distro = self.factory.makeDistribution(oci_project_admin=team)
         oci_project = self.factory.makeOCIProject(
-            registrant=team, pillar=distro)
+            registrant=team, pillar=distro
+        )
         recipe1 = self.factory.makeOCIRecipe(
-            name="same-name", oci_project=oci_project,
-            registrant=owner1, owner=owner1)
+            name="same-name",
+            oci_project=oci_project,
+            registrant=owner1,
+            owner=owner1,
+        )
         recipe2 = self.factory.makeOCIRecipe(
-            name="same-name", oci_project=oci_project,
-            registrant=owner2, owner=owner2)
+            name="same-name",
+            oci_project=oci_project,
+            registrant=owner2,
+            owner=owner2,
+        )
         recipe3 = self.factory.makeOCIRecipe(
-            name="a-first", oci_project=oci_project,
-            registrant=owner1, owner=owner1)
+            name="a-first",
+            oci_project=oci_project,
+            registrant=owner1,
+            owner=owner1,
+        )
         # This one should be filtered out.
         self.factory.makeOCIRecipe(
-            name="xxx", oci_project=oci_project,
-            registrant=owner3, owner=owner3)
+            name="xxx",
+            oci_project=oci_project,
+            registrant=owner3,
+            owner=owner3,
+        )
 
         # It should be sorted by owner's name first, then recipe name.
         self.assertEqual(
-            [recipe3, recipe1, recipe2],
-            list(oci_project.searchRecipes("a")))
+            [recipe3, recipe1, recipe2], list(oci_project.searchRecipes("a"))
+        )
 
     def test_build_args_dict(self):
         args = {"MY_VERSION": "1.0.3", "ANOTHER_VERSION": "2.9.88"}
@@ -776,8 +960,10 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         ]
         for invalid_build_args in invalid_build_args_set:
             self.assertRaises(
-                AssertionError, self.factory.makeOCIRecipe,
-                build_args=invalid_build_args)
+                AssertionError,
+                self.factory.makeOCIRecipe,
+                build_args=invalid_build_args,
+            )
 
     def test_build_args_flatten_dict(self):
         # Makes sure we only store one level of key=pair, flattening to
@@ -791,11 +977,14 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         # Force fetch it from database
         store = IStore(recipe)
         store.invalidate(recipe)
-        self.assertEqual({
-            "VAR1": "{'something': [1, 2, 3]}",
-            "VAR2": "123",
-            "VAR3": "A string",
-        }, recipe.build_args)
+        self.assertEqual(
+            {
+                "VAR1": "{'something': [1, 2, 3]}",
+                "VAR2": "123",
+                "VAR3": "A string",
+            },
+            recipe.build_args,
+        )
 
     def test_use_distribution_credentials_set(self):
         self.setConfig()
@@ -834,38 +1023,59 @@ class TestOCIRecipe(OCIConfigHelperMixin, TestCaseWithFactory):
         login_admin()
         private_team = self.factory.makeTeam(
             visibility=PersonVisibility.PRIVATE,
-            membership_policy=TeamMembershipPolicy.MODERATED)
+            membership_policy=TeamMembershipPolicy.MODERATED,
+        )
         owner = self.factory.makePerson(member_of=[private_team])
         pillar = self.factory.makeProduct(
-            owner=private_team, registrant=owner,
+            owner=private_team,
+            registrant=owner,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         oci_project = self.factory.makeOCIProject(
-            registrant=owner, pillar=pillar)
+            registrant=owner, pillar=pillar
+        )
 
         [private_git_ref] = self.factory.makeGitRefs(
-            target=pillar, owner=owner,
+            target=pillar,
+            owner=owner,
             information_type=InformationType.PROPRIETARY,
-            paths=['refs/heads/v1.0-20.04'])
+            paths=["refs/heads/v1.0-20.04"],
+        )
 
         private_recipe = self.factory.makeOCIRecipe(
-            owner=private_team, registrant=owner,
-            oci_project=oci_project, git_ref=private_git_ref,
-            information_type=InformationType.PROPRIETARY)
+            owner=private_team,
+            registrant=owner,
+            oci_project=oci_project,
+            git_ref=private_git_ref,
+            information_type=InformationType.PROPRIETARY,
+        )
         public_recipe = self.factory.makeOCIRecipe()
 
         # Should not be able to make the recipe PUBLIC if it's linked to
         self.assertRaises(
-            OCIRecipePrivacyMismatch, setattr,
-            private_recipe, 'information_type', InformationType.PUBLIC)
+            OCIRecipePrivacyMismatch,
+            setattr,
+            private_recipe,
+            "information_type",
+            InformationType.PUBLIC,
+        )
         # We should not be able to link public recipe to a private repo.
         self.assertRaises(
-            OCIRecipePrivacyMismatch, setattr,
-            public_recipe, 'git_ref', private_git_ref)
+            OCIRecipePrivacyMismatch,
+            setattr,
+            public_recipe,
+            "git_ref",
+            private_git_ref,
+        )
         # We should not be able to link public recipe to a private owner.
         self.assertRaises(
-            OCIRecipePrivacyMismatch, setattr,
-            public_recipe, 'owner', private_team)
+            OCIRecipePrivacyMismatch,
+            setattr,
+            public_recipe,
+            "owner",
+            private_team,
+        )
 
 
 class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
@@ -878,30 +1088,43 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
     def test_change_oci_project_pillar_reconciles_access(self):
         person = self.factory.makePerson()
         initial_project = self.factory.makeProduct(
-            name='initial-project',
-            owner=person, registrant=person)
+            name="initial-project", owner=person, registrant=person
+        )
         final_project = self.factory.makeProduct(
-            name='final-project',
-            owner=person, registrant=person)
+            name="final-project", owner=person, registrant=person
+        )
         oci_project = self.factory.makeOCIProject(
-            ociprojectname='the-oci-project', pillar=initial_project,
-            registrant=person)
+            ociprojectname="the-oci-project",
+            pillar=initial_project,
+            registrant=person,
+        )
         recipes = []
         for i in range(10):
-            recipes.append(self.factory.makeOCIRecipe(
-                registrant=person, owner=person,
-                oci_project=oci_project,
-                information_type=InformationType.USERDATA))
+            recipes.append(
+                self.factory.makeOCIRecipe(
+                    registrant=person,
+                    owner=person,
+                    oci_project=oci_project,
+                    information_type=InformationType.USERDATA,
+                )
+            )
 
         access_artifacts = getUtility(IAccessArtifactSource).find(recipes)
-        initial_access_policy = getUtility(IAccessPolicySource).find(
-            [(initial_project, InformationType.USERDATA)]).one()
+        initial_access_policy = (
+            getUtility(IAccessPolicySource)
+            .find([(initial_project, InformationType.USERDATA)])
+            .one()
+        )
         apasource = getUtility(IAccessPolicyArtifactSource)
         policy_artifacts = apasource.find(
-            [(recipe_artifact, initial_access_policy)
-             for recipe_artifact in access_artifacts])
+            [
+                (recipe_artifact, initial_access_policy)
+                for recipe_artifact in access_artifacts
+            ]
+        )
         self.assertEqual(
-            {i.policy.pillar for i in policy_artifacts}, {initial_project})
+            {i.policy.pillar for i in policy_artifacts}, {initial_project}
+        )
 
         # Changing OCI project's pillar should move the policy artifacts of
         # all OCI recipes associated to the new pillar.
@@ -909,13 +1132,20 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
         with admin_logged_in():
             oci_project.pillar = final_project
 
-        final_access_policy = getUtility(IAccessPolicySource).find(
-            [(final_project, InformationType.USERDATA)]).one()
+        final_access_policy = (
+            getUtility(IAccessPolicySource)
+            .find([(final_project, InformationType.USERDATA)])
+            .one()
+        )
         policy_artifacts = apasource.find(
-            [(recipe_artifact, final_access_policy)
-             for recipe_artifact in access_artifacts])
+            [
+                (recipe_artifact, final_access_policy)
+                for recipe_artifact in access_artifacts
+            ]
+        )
         self.assertEqual(
-            {i.policy.pillar for i in policy_artifacts}, {final_project})
+            {i.policy.pillar for i in policy_artifacts}, {final_project}
+        )
 
     def getGrants(self, ocirecipe, person=None):
         conditions = [AccessArtifact.ocirecipe == ocirecipe]
@@ -924,13 +1154,16 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
         return IStore(AccessArtifactGrant).find(
             AccessArtifactGrant,
             AccessArtifactGrant.abstract_artifact_id == AccessArtifact.id,
-            *conditions)
+            *conditions,
+        )
 
     def test_reconcile_set_public(self):
         owner = self.factory.makePerson()
         recipe = self.factory.makeOCIRecipe(
-            registrant=owner, owner=owner,
-            information_type=InformationType.USERDATA)
+            registrant=owner,
+            owner=owner,
+            information_type=InformationType.USERDATA,
+        )
         another_user = self.factory.makePerson()
         with admin_logged_in():
             recipe.subscribe(another_user, recipe.owner)
@@ -941,7 +1174,9 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
                     person=Equals(another_user),
                     recipe=Equals(recipe),
                     subscribed_by=Equals(recipe.owner),
-                    date_created=IsInstance(datetime)))
+                    date_created=IsInstance(datetime),
+                ),
+            )
 
             recipe.information_type = InformationType.PUBLIC
             self.assertEqual(0, self.getGrants(recipe, another_user).count())
@@ -951,7 +1186,9 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
                     person=Equals(another_user),
                     recipe=Equals(recipe),
                     subscribed_by=Equals(recipe.owner),
-                    date_created=IsInstance(datetime)))
+                    date_created=IsInstance(datetime),
+                ),
+            )
 
     def test_owner_is_subscribed_automatically(self):
         recipe = self.factory.makeOCIRecipe()
@@ -960,19 +1197,25 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
         self.assertTrue(recipe.visibleByUser(owner))
         self.assertIn(owner, recipe.subscribers)
         with person_logged_in(owner):
-            self.assertThat(recipe.getSubscription(owner), MatchesStructure(
-                person=Equals(owner),
-                subscribed_by=Equals(registrant),
-                date_created=IsInstance(datetime)))
+            self.assertThat(
+                recipe.getSubscription(owner),
+                MatchesStructure(
+                    person=Equals(owner),
+                    subscribed_by=Equals(registrant),
+                    date_created=IsInstance(datetime),
+                ),
+            )
 
     def test_owner_can_grant_access(self):
         owner = self.factory.makePerson()
         recipe = self.factory.makeOCIRecipe(
-            registrant=owner, owner=owner,
-            information_type=InformationType.USERDATA)
+            registrant=owner,
+            owner=owner,
+            information_type=InformationType.USERDATA,
+        )
         other_person = self.factory.makePerson()
         with person_logged_in(other_person):
-            self.assertRaises(Unauthorized, getattr, recipe, 'subscribe')
+            self.assertRaises(Unauthorized, getattr, recipe, "subscribe")
         with person_logged_in(owner):
             recipe.subscribe(other_person, owner)
             self.assertIn(other_person, recipe.subscribers)
@@ -981,18 +1224,23 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
         owner = self.factory.makePerson()
         person = self.factory.makePerson()
         recipe = self.factory.makeOCIRecipe(
-            registrant=owner, owner=owner,
-            information_type=InformationType.USERDATA)
+            registrant=owner,
+            owner=owner,
+            information_type=InformationType.USERDATA,
+        )
         with person_logged_in(owner):
             self.assertFalse(recipe.visibleByUser(person))
 
     def test_private_is_visible_by_team_member(self):
         person = self.factory.makePerson()
         team = self.factory.makeTeam(
-            members=[person], membership_policy=TeamMembershipPolicy.MODERATED)
+            members=[person], membership_policy=TeamMembershipPolicy.MODERATED
+        )
         recipe = self.factory.makeOCIRecipe(
-            owner=team, registrant=person,
-            information_type=InformationType.USERDATA)
+            owner=team,
+            registrant=person,
+            information_type=InformationType.USERDATA,
+        )
         with person_logged_in(team):
             self.assertTrue(recipe.visibleByUser(person))
 
@@ -1000,8 +1248,10 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
         person = self.factory.makePerson()
         owner = self.factory.makePerson()
         recipe = self.factory.makeOCIRecipe(
-            registrant=owner, owner=owner,
-            information_type=InformationType.USERDATA)
+            registrant=owner,
+            owner=owner,
+            information_type=InformationType.USERDATA,
+        )
 
         with person_logged_in(owner):
             self.assertFalse(recipe.visibleByUser(person))
@@ -1012,7 +1262,9 @@ class TestOCIRecipeAccessControl(TestCaseWithFactory, OCIConfigHelperMixin):
                     person=Equals(person),
                     recipe=Equals(recipe),
                     subscribed_by=Equals(recipe.owner),
-