← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~abentley/launchpad/retain-spr-builds into lp:launchpad

 

Aaron Bentley has proposed merging lp:~abentley/launchpad/retain-spr-builds into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #645620 Deleting recipe leaves SourcePackageReleases with no traceability
  https://bugs.launchpad.net/bugs/645620


= Summary =
Fix bug #645620: Deleting recipe leaves SourcePackageReleases with no
traceability

== Proposed fix ==
Relax the not-null constraint for recipe on sourcepackagerecipebuild.  NULL
sourcepackagerecipebuilds on recipe deletion instead of deleting them.

== Pre-implementation notes ==
Discussed with thumper

== Implementation details ==
None

== Tests ==
bin/test -t test_destroySelf_retains_build -t test_destroySelf_preserves_release

== Demo and Q/A ==
Cannot be demoed because sourcepackagerecipebuilds do not have a valid URL if
their recipe is deleted.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/code/interfaces/sourcepackagerecipebuild.py
  lib/canonical/launchpad/security.py
  database/schema/patch-2208-90-0.sql
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/code/model/sourcepackagerecipebuild.py
  lib/lp/code/model/sourcepackagerecipe.py

./lib/canonical/launchpad/security.py
     721: E302 expected 2 blank lines, found 1
    1256: E302 expected 2 blank lines, found 1
    1466: E302 expected 2 blank lines, found 1
-- 
https://code.launchpad.net/~abentley/launchpad/retain-spr-builds/+merge/40669
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/retain-spr-builds into lp:launchpad.
=== added file 'database/schema/patch-2208-90-0.sql'
--- database/schema/patch-2208-90-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-90-0.sql	2010-11-11 21:04:15 +0000
@@ -0,0 +1,7 @@
+-- Copyright 2010 Canonical Ltd.  This software is licensed under the
+-- GNU Affero General Public License version 3 (see the file LICENSE).
+SET client_min_messages=ERROR;
+
+ALTER TABLE SourcePackageRecipeBuild ALTER COLUMN recipe DROP NOT NULL;
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 90, 0);

=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py	2010-10-22 15:58:40 +0000
+++ lib/canonical/launchpad/security.py	2010-11-11 21:04:15 +0000
@@ -2260,7 +2260,8 @@
     usedfor = ISourcePackageRecipeBuild
 
     def iter_objects(self):
-        yield self.obj.recipe
+        if self.obj.recipe is not None:
+            yield self.obj.recipe
         yield self.obj.archive
 
 

=== modified file 'lib/lp/code/interfaces/sourcepackagerecipebuild.py'
--- lib/lp/code/interfaces/sourcepackagerecipebuild.py	2010-08-27 02:55:05 +0000
+++ lib/lp/code/interfaces/sourcepackagerecipebuild.py	2010-11-11 21:04:15 +0000
@@ -58,8 +58,7 @@
         title=_("The person who wants this to be done."))
 
     recipe = Object(
-        schema=ISourcePackageRecipe, required=True,
-        title=_("The recipe being built."))
+        schema=ISourcePackageRecipe, title=_("The recipe being built."))
 
     manifest = Object(
         schema=ISourcePackageRecipeData, title=_(

=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
--- lib/lp/code/model/sourcepackagerecipe.py	2010-11-04 06:36:29 +0000
+++ lib/lp/code/model/sourcepackagerecipe.py	2010-11-11 21:04:15 +0000
@@ -28,7 +28,6 @@
     implements,
     )
 
-from canonical.config import config
 from canonical.database.datetimecol import UtcDateTimeCol
 from canonical.launchpad.interfaces.lpstorm import (
     IMasterStore,
@@ -199,12 +198,12 @@
         self.distroseries.clear()
         self._recipe_data.instructions.find().remove()
 
-        def destroyBuilds(pending):
+        def clearBuilds(pending):
             builds = self.getBuilds(pending=pending)
             for build in builds:
-                build.destroySelf()
-        destroyBuilds(pending=True)
-        destroyBuilds(pending=False)
+                build.recipe = None
+        clearBuilds(pending=True)
+        clearBuilds(pending=False)
         store.remove(self._recipe_data)
         store.remove(self)
 

=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py	2010-10-22 20:50:50 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py	2010-11-11 21:04:15 +0000
@@ -109,7 +109,7 @@
 
     is_virtualized = True
 
-    recipe_id = Int(name='recipe', allow_none=False)
+    recipe_id = Int(name='recipe')
     recipe = Reference(recipe_id, 'SourcePackageRecipe.id')
 
     manifest = Reference(

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py	2010-11-08 01:08:15 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py	2010-11-11 21:04:15 +0000
@@ -51,7 +51,10 @@
     NonPPABuildRequest,
     SourcePackageRecipe,
     )
-from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuildJob
+from lp.code.model.sourcepackagerecipebuild import (
+    SourcePackageRecipeBuild,
+    SourcePackageRecipeBuildJob,
+    )
 from lp.code.tests.helpers import recipe_parser_newest_version
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.services.job.interfaces.job import (
@@ -484,7 +487,7 @@
         # Show no database constraints were violated
         Store.of(recipe).flush()
 
-    def test_destroySelf_clears_release(self):
+    def test_destroySelf_preserves_release(self):
         # Destroying a sourcepackagerecipe removes references to its builds
         # from their releases.
         recipe = self.factory.makeSourcePackageRecipe()
@@ -494,7 +497,28 @@
         self.assertEqual(build, release.source_package_recipe_build)
         with person_logged_in(recipe.owner):
             recipe.destroySelf()
-        self.assertIs(None, release.source_package_recipe_build)
+        self.assertIsNot(None, release.source_package_recipe_build)
+
+    def test_destroySelf_retains_build(self):
+        # Destroying a sourcepackagerecipe removes references to its builds
+        # from their releases.
+        recipe = self.factory.makeSourcePackageRecipe()
+        build = self.factory.makeSourcePackageRecipeBuild(recipe=recipe)
+        store = Store.of(build)
+        store.flush()
+        build_id = build.id
+        build = store.find(
+            SourcePackageRecipeBuild,
+            SourcePackageRecipeBuild.id == build_id).one()
+        self.assertIsNot(None, build)
+        self.assertEqual(recipe, build.recipe)
+        with person_logged_in(recipe.owner):
+            recipe.destroySelf()
+        build = store.find(
+            SourcePackageRecipeBuild,
+            SourcePackageRecipeBuild.id == build_id).one()
+        self.assertIsNot(None, build)
+        self.assertIs(None, build.recipe)
         transaction.commit()
 
     def test_findStaleDailyBuilds(self):