← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:export-is-stale into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:export-is-stale into launchpad:master.

Commit message:
Export read-only is_stale attribute on recipes

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

This is useful for debugging.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:export-is-stale into launchpad:master.
diff --git a/lib/lp/code/interfaces/sourcepackagerecipe.py b/lib/lp/code/interfaces/sourcepackagerecipe.py
index b499ccd..3d9d3ab 100644
--- a/lib/lp/code/interfaces/sourcepackagerecipe.py
+++ b/lib/lp/code/interfaces/sourcepackagerecipe.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2021 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Interface of the `SourcePackageRecipe` content type."""
@@ -277,7 +277,8 @@ class ISourcePackageRecipeEditableAttributes(IHasOwner):
     date_last_modified = exported(
         Datetime(required=True, readonly=True))
 
-    is_stale = Bool(title=_('Recipe is stale.'))
+    is_stale = exported(
+        Bool(title=_('Recipe is stale.'), required=True, readonly=True))
 
 
 class ISourcePackageRecipeEdit(Interface):
diff --git a/lib/lp/code/model/gitrepository.py b/lib/lp/code/model/gitrepository.py
index 2ba9491..1b254ce 100644
--- a/lib/lp/code/model/gitrepository.py
+++ b/lib/lp/code/model/gitrepository.py
@@ -1267,12 +1267,20 @@ class GitRepository(StormBase, WebhookTargetMixin, GitIdentityMixin):
     def markSnapsStale(self, paths):
         """See `IGitRepository`."""
         snap_set = getUtility(ISnapSet)
+<<<<<<< lib/lp/code/model/gitrepository.py
         snaps = snap_set.findByGitRepository(
             self,
             paths=paths,
             check_permissions=False)
         for snap in snaps:
             snap.is_stale = True
+=======
+        for snap in snap_set.findByGitRepository(self, paths=paths):
+            # ISnapSet.findByGitRepository returns security-proxied Snap
+            # objects on which the is_stale attribute is read-only.  Bypass
+            # this.
+            removeSecurityProxy(snap).is_stale = True
+>>>>>>> lib/lp/code/model/gitrepository.py
 
     def _markProposalMerged(self, proposal, merged_revision_id, logger=None):
         if logger is not None:
diff --git a/lib/lp/code/model/tests/test_sourcepackagerecipe.py b/lib/lp/code/model/tests/test_sourcepackagerecipe.py
index 514521f..b14b8ab 100644
--- a/lib/lp/code/model/tests/test_sourcepackagerecipe.py
+++ b/lib/lp/code/model/tests/test_sourcepackagerecipe.py
@@ -14,6 +14,7 @@ from datetime import (
 import textwrap
 
 from breezy.plugins.builder.recipe import ForbiddenInstructionError
+from lazr.restfulclient.errors import BadRequest
 from pytz import UTC
 from storm.locals import Store
 from testtools.matchers import Equals
@@ -1232,6 +1233,13 @@ class TestWebserviceMixin:
         recipe.setRecipeText(recipe_text=recipe_text2)
         self.assertEqual(recipe_text2, recipe.recipe_text)
 
+    def test_is_stale(self):
+        """is_stale is exported and is read-only."""
+        recipe = self.makeRecipe()[0]
+        self.assertTrue(recipe.is_stale)
+        recipe.is_stale = False
+        self.assertRaises(BadRequest, recipe.lp_save)
+
     def test_getRecipe(self):
         """Person.getRecipe returns the named recipe."""
         recipe, user = self.makeRecipe()[:-1]
diff --git a/lib/lp/snappy/interfaces/snap.py b/lib/lp/snappy/interfaces/snap.py
index f2ce156..16fd213 100644
--- a/lib/lp/snappy/interfaces/snap.py
+++ b/lib/lp/snappy/interfaces/snap.py
@@ -827,9 +827,9 @@ class ISnapEditableAttributes(IHasOwner):
             "this snap package.  Currently only 'core', 'core18', "
             "'core20' and 'snapcraft' keys are supported.")))
 
-    is_stale = Bool(
+    is_stale = exported(Bool(
         title=_("Snap package is stale and is due to be rebuilt."),
-        required=True, readonly=False)
+        required=True, readonly=True))
 
     store_upload = exported(Bool(
         title=_("Automatically upload to store"),
diff --git a/lib/lp/snappy/tests/test_snap.py b/lib/lp/snappy/tests/test_snap.py
index be338d7..bb6c659 100644
--- a/lib/lp/snappy/tests/test_snap.py
+++ b/lib/lp/snappy/tests/test_snap.py
@@ -3039,6 +3039,15 @@ class TestSnapWebservice(TestCaseWithFactory):
             "git_path": Equals("HEAD"),
             }))
 
+    def test_is_stale(self):
+        # is_stale is exported and is read-only.
+        snap = self.makeSnap()
+        self.assertTrue(snap["is_stale"])
+        response = self.webservice.patch(
+            snap["self_link"], "application/json",
+            json.dumps({"is_stale": False}))
+        self.assertEqual(400, response.status)
+
     def test_getByName(self):
         # lp.snaps.getByName returns a matching Snap.
         snap = self.makeSnap()