← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~twom/launchpad:detatch-oci-recipe-from-source into launchpad:master

 

Tom Wardill has proposed merging ~twom/launchpad:detatch-oci-recipe-from-source into launchpad:master.

Commit message:
Allow an OCIRecipe to be detatched from the source git repository

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~twom/launchpad/+git/launchpad/+merge/380039

Implement the detachFromGitRepository method from snaps into the OCI recipe model.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~twom/launchpad:detatch-oci-recipe-from-source into launchpad:master.
diff --git a/lib/lp/code/model/gitrepository.py b/lib/lp/code/model/gitrepository.py
index 6260c16..5ac0288 100644
--- a/lib/lp/code/model/gitrepository.py
+++ b/lib/lp/code/model/gitrepository.py
@@ -143,6 +143,7 @@ from lp.code.model.gitrule import (
     GitRuleGrant,
     )
 from lp.code.model.gitsubscription import GitSubscription
+from lp.oci.interfaces.ocirecipe import IOCIRecipeSet
 from lp.registry.enums import PersonVisibility
 from lp.registry.errors import CannotChangeInformationType
 from lp.registry.interfaces.accesspolicy import (
@@ -1525,6 +1526,10 @@ class GitRepository(StormBase, WebhookTargetMixin, GitIdentityMixin):
             alteration_operations.append(DeletionCallable(
                 None, msg("Some snap packages build from this repository."),
                 getUtility(ISnapSet).detachFromGitRepository, self))
+        if not getUtility(IOCIRecipeSet).findByGitRepository(self).is_empty():
+            alteration_operations.append(DeletionCallable(
+                None, msg("Some OCI recipes build from this repository."),
+                getUtility(IOCIRecipeSet).detachFromGitRepository, self))
 
         return (alteration_operations, deletion_operations)
 
diff --git a/lib/lp/oci/interfaces/ocirecipe.py b/lib/lp/oci/interfaces/ocirecipe.py
index e665c1e..751326f 100644
--- a/lib/lp/oci/interfaces/ocirecipe.py
+++ b/lib/lp/oci/interfaces/ocirecipe.py
@@ -182,7 +182,7 @@ class IOCIRecipeEditableAttributes(IHasOwner):
     git_repository = ReferenceChoice(
         title=_("Git repository"),
         schema=IGitRepository, vocabulary="GitRepository",
-        required=True, readonly=False,
+        required=False, readonly=False,
         description=_(
             "A Git repository with a branch containing a Dockerfile "
             "at the location defined by the build_file attribute."))
@@ -252,4 +252,19 @@ class IOCIRecipeSet(Interface):
         """Return all OCI recipes with the given `oci_project`."""
 
     def preloadDataForOCIRecipes(recipes, user):
-        """Load the data reloated to a list of OCI Recipes."""
+        """Load the data related to a list of OCI Recipes."""
+
+    def findByGitRepository(repository, paths=None):
+        """Return all OCI recipes for the given Git repository.
+
+        :param repository: An `IGitRepository`.
+        :param paths: If not None, only return OCI recipes for one of
+            these Git reference paths.
+        """
+
+    def detachFromGitRepository(repository):
+        """Detach all OCI recipes from the given Git repository.
+
+        After this, any OCI recipes that previously used this repository
+        will have no source and so cannot dispatch new builds.
+        """
diff --git a/lib/lp/oci/model/ocirecipe.py b/lib/lp/oci/model/ocirecipe.py
index 2bf8068..79a3b0d 100644
--- a/lib/lp/oci/model/ocirecipe.py
+++ b/lib/lp/oci/model/ocirecipe.py
@@ -97,9 +97,9 @@ class OCIRecipe(Storm):
 
     official = Bool(name="official", default=False)
 
-    git_repository_id = Int(name="git_repository", allow_none=False)
+    git_repository_id = Int(name="git_repository", allow_none=True)
     git_repository = Reference(git_repository_id, "GitRepository.id")
-    git_path = Unicode(name="git_path", allow_none=False)
+    git_path = Unicode(name="git_path", allow_none=True)
     build_file = Unicode(name="build_file", allow_none=False)
 
     require_virtualized = Bool(name="require_virtualized", default=True,
@@ -152,8 +152,8 @@ class OCIRecipe(Storm):
     def git_ref(self, value):
         """See `IOCIRecipe`."""
         if value is not None:
-            self.git_path = value.path
             self.git_repository = value.repository
+            self.git_path = value.path
         else:
             self.git_repository = None
             self.git_path = None
@@ -306,14 +306,26 @@ class OCIRecipeSet:
         return oci_recipe
 
     def findByOwner(self, owner):
-        """See `IOCIRecipe`."""
+        """See `IOCIRecipeSet`."""
         return IStore(OCIRecipe).find(OCIRecipe, OCIRecipe.owner == owner)
 
     def findByOCIProject(self, oci_project):
-        """See `IOCIRecipe`."""
+        """See `IOCIRecipeSet`."""
         return IStore(OCIRecipe).find(
             OCIRecipe, OCIRecipe.oci_project == oci_project)
 
+    def findByGitRepository(self, repository, paths=None):
+        """See `IOCIRecipeSet`."""
+        clauses = [OCIRecipe.git_repository == repository]
+        if paths is not None:
+            clauses.append(OCIRecipe.git_path.is_in(paths))
+        return IStore(OCIRecipe).find(OCIRecipe, *clauses)
+
+    def detachFromGitRepository(self, repository):
+        """See `IOCIRecipeSet`."""
+        self.findByGitRepository(repository).set(
+            git_repository_id=None, git_path=None, date_last_modified=UTC_NOW)
+
     def preloadDataForOCIRecipes(self, recipes, user=None):
         """See `IOCIRecipeSet`."""
         recipes = [removeSecurityProxy(recipe) for recipe in recipes]
diff --git a/lib/lp/oci/tests/test_ocirecipe.py b/lib/lp/oci/tests/test_ocirecipe.py
index 5b614f0..2bcc053 100644
--- a/lib/lp/oci/tests/test_ocirecipe.py
+++ b/lib/lp/oci/tests/test_ocirecipe.py
@@ -227,3 +227,30 @@ class TestOCIRecipeSet(TestCaseWithFactory):
             owner=owner,
             oci_project=oci_project,
             name="missing")
+
+    def test_detachFromGitRepository(self):
+        repositories = [self.factory.makeGitRepository() for i in range(2)]
+        oci_recipes = []
+        paths = []
+        refs = []
+        for repository in repositories:
+            for i in range(2):
+                [ref] = self.factory.makeGitRefs(repository=repository)
+                paths.append(ref.path)
+                refs.append(ref)
+                oci_recipes.append(self.factory.makeOCIRecipe(
+                    git_ref=ref, date_created=ONE_DAY_AGO))
+        getUtility(IOCIRecipeSet).detachFromGitRepository(repositories[0])
+        self.assertEqual(
+            [None, None, repositories[1], repositories[1]],
+            [oci_recipe.git_repository for oci_recipe in oci_recipes])
+        self.assertEqual(
+            [None, None, paths[2], paths[3]],
+            [oci_recipe.git_path for oci_recipe in oci_recipes])
+        self.assertEqual(
+            [None, None, refs[2], refs[3]],
+            [oci_recipe.git_ref for oci_recipe in oci_recipes])
+        for oci_recipe in oci_recipes[:2]:
+            self.assertSqlAttributeEqualsDate(
+                oci_recipe, "date_last_modified", UTC_NOW)
+

Follow ups