launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #27800
[Merge] ~cjwatson/launchpad:not-automatic-proposed into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:not-automatic-proposed into launchpad:master.
Commit message:
Add and honour DistroSeries.proposed_not_automatic
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1016776 in Launchpad itself: "Users are offered updates to packages in -proposed"
https://bugs.launchpad.net/launchpad/+bug/1016776
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/412537
If set, this flag causes the -proposed pocket of the series to have `NotAutomatic: yes` and `ButAutomaticUpgrades: yes` written to its `Release` file, causing apt to not install versions from that suite without explicit user consent, but to apply upgrades if the user has already installed a version higher than that available from automatic suites.
See "man apt_preferences" and https://wiki.debian.org/DebianRepository/Format for detailed rules. They're quite confusing, but this is the closest available approximation of "only upgrade a package from -proposed if you already installed an earlier version from -proposed".
This is analogous to, and largely copied from, the existing `DistroSeries.backports_not_automatic` flag.
This and https://code.launchpad.net/~cjwatson/launchpad-buildd/+git/launchpad-buildd/+merge/412535 can be landed in either order, but we must be careful not to actually enable the new flag anywhere on production before the launchpad-buildd change has been deployed to production.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:not-automatic-proposed into launchpad:master.
diff --git a/lib/lp/archivepublisher/publishing.py b/lib/lp/archivepublisher/publishing.py
index f18ca9f..a5a0cc4 100644
--- a/lib/lp/archivepublisher/publishing.py
+++ b/lib/lp/archivepublisher/publishing.py
@@ -1235,8 +1235,10 @@ class Publisher:
release_file["Components"] = " ".join(
reorder_components(all_components))
release_file["Description"] = drsummary
- if (pocket == PackagePublishingPocket.BACKPORTS and
- distroseries.backports_not_automatic):
+ if ((pocket == PackagePublishingPocket.BACKPORTS and
+ distroseries.backports_not_automatic) or
+ (pocket == PackagePublishingPocket.PROPOSED and
+ distroseries.proposed_not_automatic)):
release_file["NotAutomatic"] = "yes"
release_file["ButAutomaticUpgrades"] = "yes"
diff --git a/lib/lp/archivepublisher/tests/test_publisher.py b/lib/lp/archivepublisher/tests/test_publisher.py
index 6ff8cb2..c0891dd 100644
--- a/lib/lp/archivepublisher/tests/test_publisher.py
+++ b/lib/lp/archivepublisher/tests/test_publisher.py
@@ -116,6 +116,7 @@ from lp.testing.matchers import FileContainsBytes
RELEASE = PackagePublishingPocket.RELEASE
+PROPOSED = PackagePublishingPocket.PROPOSED
BACKPORTS = PackagePublishingPocket.BACKPORTS
@@ -2064,6 +2065,45 @@ class TestPublisher(TestPublisherBase):
self.assertIn("NotAutomatic: yes", get_release(BACKPORTS))
self.assertIn("ButAutomaticUpgrades: yes", get_release(BACKPORTS))
+ def testReleaseFileForNotAutomaticProposed(self):
+ # Test Release file writing for series with NotAutomatic -proposed.
+ publisher = Publisher(
+ self.logger, self.config, self.disk_pool,
+ self.ubuntutest.main_archive)
+ self.getPubSource(filecontent=b'Hello world', pocket=RELEASE)
+ self.getPubSource(filecontent=b'Hello world', pocket=PROPOSED)
+
+ # Make everything other than breezy-autotest OBSOLETE so that they
+ # aren't republished.
+ for series in self.ubuntutest.series:
+ if series.name != "breezy-autotest":
+ series.status = SeriesStatus.OBSOLETE
+
+ publisher.A_publish(True)
+ publisher.C_writeIndexes(False)
+
+ def get_release(pocket):
+ release_path = os.path.join(
+ publisher._config.distsroot,
+ 'breezy-autotest%s' % pocketsuffix[pocket], 'Release')
+ with open(release_path) as release_file:
+ return release_file.read().splitlines()
+
+ # When proposed_not_automatic is unset, no Release files have
+ # NotAutomatic: yes.
+ self.assertEqual(False, self.breezy_autotest.proposed_not_automatic)
+ publisher.D_writeReleaseFiles(False)
+ self.assertNotIn("NotAutomatic: yes", get_release(RELEASE))
+ self.assertNotIn("NotAutomatic: yes", get_release(PROPOSED))
+
+ # But with the flag set, -proposed Release files gain
+ # NotAutomatic: yes and ButAutomaticUpgrades: yes.
+ self.breezy_autotest.proposed_not_automatic = True
+ publisher.D_writeReleaseFiles(False)
+ self.assertNotIn("NotAutomatic: yes", get_release(RELEASE))
+ self.assertIn("NotAutomatic: yes", get_release(PROPOSED))
+ self.assertIn("ButAutomaticUpgrades: yes", get_release(PROPOSED))
+
def testReleaseFileForI18n(self):
"""Test Release file writing for translated package descriptions."""
publisher = Publisher(
diff --git a/lib/lp/registry/configure.zcml b/lib/lp/registry/configure.zcml
index 9d07930..0ccf396 100644
--- a/lib/lp/registry/configure.zcml
+++ b/lib/lp/registry/configure.zcml
@@ -1,4 +1,4 @@
-<!-- Copyright 2009-2020 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).
-->
@@ -326,6 +326,7 @@
description
driver
backports_not_automatic
+ proposed_not_automatic
include_long_descriptions
index_compressors
publish_by_hash
diff --git a/lib/lp/registry/interfaces/distroseries.py b/lib/lp/registry/interfaces/distroseries.py
index 848cf6d..da0e19e 100644
--- a/lib/lp/registry/interfaces/distroseries.py
+++ b/lib/lp/registry/interfaces/distroseries.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2020 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).
"""Interfaces including and related to IDistroSeries."""
@@ -373,6 +373,15 @@ class IDistroSeriesPublic(
automatically upgrade within backports, but not into it.
"""))
+ proposed_not_automatic = Bool(
+ title=_("Don't upgrade to proposed updates automatically"),
+ required=True,
+ description=_("""
+ Set NotAutomatic: yes and ButAutomaticUpgrades: yes in Release
+ files generated for the proposed pocket. This tells apt to
+ automatically upgrade within proposed, but not into it.
+ """))
+
include_long_descriptions = exported(
Bool(
title=_(
diff --git a/lib/lp/registry/model/distroseries.py b/lib/lp/registry/model/distroseries.py
index 2ad609c..167aa8e 100644
--- a/lib/lp/registry/model/distroseries.py
+++ b/lib/lp/registry/model/distroseries.py
@@ -279,6 +279,7 @@ class DistroSeries(SQLBase, BugTargetBase, HasSpecificationsMixin,
if "publishing_options" not in kwargs:
kwargs["publishing_options"] = {
"backports_not_automatic": False,
+ "proposed_not_automatic": False,
"include_long_descriptions": True,
"index_compressors": [
compressor.title
@@ -828,6 +829,15 @@ class DistroSeries(SQLBase, BugTargetBase, HasSpecificationsMixin,
self.publishing_options["backports_not_automatic"] = value
@property
+ def proposed_not_automatic(self):
+ return self.publishing_options.get("proposed_not_automatic", False)
+
+ @proposed_not_automatic.setter
+ def proposed_not_automatic(self, value):
+ assert isinstance(value, bool)
+ self.publishing_options["proposed_not_automatic"] = value
+
+ @property
def include_long_descriptions(self):
return self.publishing_options.get("include_long_descriptions", True)
diff --git a/lib/lp/registry/tests/test_distroseries.py b/lib/lp/registry/tests/test_distroseries.py
index 1543eca..d67e499 100644
--- a/lib/lp/registry/tests/test_distroseries.py
+++ b/lib/lp/registry/tests/test_distroseries.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2016 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).
"""Tests for distroseries."""
@@ -358,6 +358,16 @@ class TestDistroSeries(TestCaseWithFactory):
self.assertTrue(
naked_distroseries.publishing_options["backports_not_automatic"])
+ def test_proposed_not_automatic(self):
+ distroseries = self.factory.makeDistroSeries()
+ self.assertFalse(distroseries.proposed_not_automatic)
+ with admin_logged_in():
+ distroseries.proposed_not_automatic = True
+ self.assertTrue(distroseries.proposed_not_automatic)
+ naked_distroseries = removeSecurityProxy(distroseries)
+ self.assertTrue(
+ naked_distroseries.publishing_options["proposed_not_automatic"])
+
def test_include_long_descriptions(self):
distroseries = self.factory.makeDistroSeries()
self.assertTrue(distroseries.include_long_descriptions)
diff --git a/lib/lp/soyuz/scripts/initialize_distroseries.py b/lib/lp/soyuz/scripts/initialize_distroseries.py
index 1ca534d..5a773bf 100644
--- a/lib/lp/soyuz/scripts/initialize_distroseries.py
+++ b/lib/lp/soyuz/scripts/initialize_distroseries.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2020 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).
"""Initialize a distroseries from its parent distroseries."""
@@ -383,6 +383,9 @@ class InitializeDistroSeries:
self.distroseries.backports_not_automatic = any(
parent.backports_not_automatic
for parent in self.derivation_parents)
+ self.distroseries.proposed_not_automatic = any(
+ parent.proposed_not_automatic
+ for parent in self.derivation_parents)
self.distroseries.include_long_descriptions = any(
parent.include_long_descriptions
for parent in self.derivation_parents)
diff --git a/lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py b/lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py
index 4079947..b3edb46 100644
--- a/lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py
+++ b/lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2021 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Test the initialize_distroseries script machinery."""
@@ -93,6 +93,7 @@ class InitializationHelperTestCase(TestCaseWithFactory):
if existing_format_selection is None:
spfss_utility.add(parent, format_selection)
parent.backports_not_automatic = True
+ parent.proposed_not_automatic = True
parent.include_long_descriptions = False
parent.index_compressors = [IndexCompressionType.XZ]
parent.publish_by_hash = True
@@ -685,6 +686,7 @@ class TestInitializeDistroSeries(InitializationHelperTestCase):
SourcePackageFormat.FORMAT_1_0))
# Other configuration bits are copied too.
self.assertTrue(child.backports_not_automatic)
+ self.assertTrue(child.proposed_not_automatic)
self.assertFalse(child.include_long_descriptions)
self.assertEqual([IndexCompressionType.XZ], child.index_compressors)
self.assertTrue(child.publish_by_hash)