← Back to team overview

sts-sponsors team mailing list archive

[Merge] ~alexsander-souza/maas/+git/maas-release-tools:point_release_checks into ~maas-committers/maas/+git/maas-release-tools:main

 

Alexsander de Souza has proposed merging ~alexsander-souza/maas/+git/maas-release-tools:point_release_checks into ~maas-committers/maas/+git/maas-release-tools:main.

Commit message:
Add check for point releases 

Requested reviews:
  MAAS Committers (maas-committers)

For more details, see:
https://code.launchpad.net/~alexsander-souza/maas/+git/maas-release-tools/+merge/437533
-- 
Your team MAAS Committers is requested to review the proposed merge of ~alexsander-souza/maas/+git/maas-release-tools:point_release_checks into ~maas-committers/maas/+git/maas-release-tools:main.
diff --git a/maas_release_tools/scripts/release_status.py b/maas_release_tools/scripts/release_status.py
index 58c9b5f..fdf4a13 100644
--- a/maas_release_tools/scripts/release_status.py
+++ b/maas_release_tools/scripts/release_status.py
@@ -15,6 +15,7 @@ will go smoother.
 from abc import ABC, abstractmethod
 from argparse import ArgumentParser, FileType
 import base64
+from datetime import date, timedelta
 from functools import lru_cache
 import glob
 import json
@@ -45,16 +46,21 @@ from ..maasci import (
 from ..version import get_branch_setup_version, ReleaseVersion
 
 MAAS_SNAP_ID = "shY22YTZ3RhJJDOj0MfmShTNZTEb1Jiq"
+MAAS_TEST_DB_SNAP_ID = "idVMgkvkiKaOC8BjKlJRPUH50fvibpp8"
 BUILD_ARCHS = ("amd64", "arm64", "ppc64el", "s390x")
 
 
 def get_macaroon_refresh_help():
     """Get help on refreshing the macaroon that can be copied&pasted."""
+    exp_date = date.today() + timedelta(days=7)
     return (
         "  Please refresh it:\n"
         "    rm -f release.macaroon\n"
-        "    snapcraft export-login --snaps maas,maas-test-db \\\n"
-        "        --acls package_release,package_access release.macaroon"
+        "    snapcraft export-login \\\n"
+        "        --snaps maas,maas-test-db \\\n"
+        "        --acls package_release,package_access \\\n"
+        f"        --expires {exp_date.isoformat()} \\\n"
+        "        release.macaroon"
     )
 
 
@@ -352,7 +358,9 @@ class SnapTrack(ReleaseStep):
 
 
 class PPACopyMixin:
-    def check_packages_copied(self, source_ppa, target_ppa):
+    def check_packages_copied(
+        self, source_ppa, target_ppa, selected: Optional[list[str]] = None
+    ):
         target_packages = list(
             (package.source_package_name, package.source_package_version)
             for package in target_ppa.getPublishedSources(
@@ -360,9 +368,14 @@ class PPACopyMixin:
             )
         )
         missing_packages = set()
-        for package in source_ppa.getPublishedSources(
-            status="Published", distro_series=self.current_series
-        ):
+        published_packages = [
+            package
+            for package in source_ppa.getPublishedSources(
+                status="Published", distro_series=self.current_series
+            )
+            if selected is None or package.source_package_name in selected
+        ]
+        for package in published_packages:
             name, version = (
                 package.source_package_name,
                 package.source_package_version,
@@ -375,7 +388,6 @@ class PPACopyMixin:
                 f"{name} {version} has not been copied"
                 for name, version in sorted(missing_packages)
             )
-            # error_message += f"\nGo to {source_ppa.web_link}/+copy-packages"
             return False, error_message
         else:
             return True, None
@@ -427,22 +439,25 @@ class MAASPPA(ReleaseStep):
             )
         return True, None
 
+    def how_to_create_ppa(self, ppa_owner: str, ppa_name: str) -> list[str]:
+        return [
+            f"Go to https://launchpad.net/~{ppa_owner}/+activate-ppa";,
+            f"Create '{ppa_name}' PPA",
+            "Click `Change Details`",
+            f"Enable the following processors: {', '.join(sorted(set(BUILD_ARCHS)))}",
+        ]
+
     def fix(self, doit: bool = False) -> Tuple[bool, list[str]]:
-        steps = []
         if self.ppa is None:
-            steps.append(
-                f"Go to https://launchpad.net/~{self.ppa_owner.name}/+activate-ppa";
+            return False, self.how_to_create_ppa(
+                self.ppa_owner.name, self.ppa_name
             )
-            steps.append(f"Create '{self.ppa_name}' PPA")
         else:
-            steps.append(
-                f"Go to https://launchpad.net/~{self.ppa_owner.name}/+archive/ubuntu/{self.ppa_name}/";
-            )
-        steps.append("Click `Change Details`")
-        steps.append(
-            f"Enable the following processors: {', '.join(sorted(set(BUILD_ARCHS)))}"
-        )
-        return False, steps
+            return False, [
+                f"Go to {self.ppa.web_link}",
+                "Click `Change Details`",
+                f"Enable the following processors: {', '.join(sorted(set(BUILD_ARCHS)))}",
+            ]
 
 
 class MAASPackagePublished(MAASPPA):
@@ -517,21 +532,24 @@ class MAASPackagePublished(MAASPPA):
         if self.ppa is None:
             return super().fix(doit)
         return False, [
-            f"Check https://launchpad.net/~/+archive/ubuntu/{self.ppa_name}/+packages";,
+            f"Check {self.ppa.web_link}/+packages",
         ]
 
 
 class PackagesCopiedFromDeps(MAASPPA, PPACopyMixin):
     def __init__(self, preparer):
-        super().__init__(preparer, "release-preparation")
+        super().__init__(preparer, "candidate")
         self.source_ppa = None
         self.target_ppa = None
+        self.selected = None
 
     @property
     def title(self):
         return f"Packages copied from ppa:{MAAS_USER}/latest-deps"
 
     def check(self):
+        if not self.preparer.version.new_series:
+            self.selected = ["maas"]
         try:
             self.source_ppa = self.preparer.launchpad.maas.getPPAByName(
                 name="latest-deps"
@@ -546,12 +564,39 @@ class PackagesCopiedFromDeps(MAASPPA, PPACopyMixin):
                 f"ppa:{self.ppa_path} couldn't be found.",
             )
         else:
-            return self.check_packages_copied(self.source_ppa, self.target_ppa)
+            return self.check_packages_copied(
+                self.source_ppa, self.target_ppa, self.selected
+            )
 
-    # def fix(self, doit: bool = False) -> Tuple[bool, list[str]]:
+    def fix(self, doit: bool = False) -> Tuple[bool, list[str]]:
+        if self.source_ppa is None:
+            return False, self.how_to_create_ppa(MAAS_USER, "latest-deps")
+        if self.target_ppa is None:
+            return False, self.how_to_create_ppa(self.ppa_owner, self.ppa_name)
+        procedure = [
+            f"Go to {self.source_ppa.web_link}/+copy-packages",
+            f"Under `Packages`, in the right middle of the screen, select the `{get_ubuntu_series()}` OS series and click `Filter`",
+            f"select {self.selected or 'all'} packages",
+            "select `copy existing binaries`",
+            f"Select `{self.target_ppa.name}` as Destination PPA",
+            "Click `Copy packages`",
+        ]
+        return False, procedure
 
 
 class PackagesCopiedToReleasePPA(MAASPPA, PPACopyMixin):
+    def __init__(self, preparer, ppa_type):
+        super().__init__(preparer, ppa_type)
+        self.source_ppa = None
+        self.target_ppa = None
+        self.source = None
+        self.selected = None
+        match ppa_type:
+            case "stable":
+                self.source = MAASPPA(preparer, "candidate")
+            case "candidate":
+                self.source = MAASPPA(preparer, "release-preparation")
+
     @property
     def title(self):
         return f"Packages copied to ppa:{self.ppa_path}"
@@ -563,27 +608,45 @@ class PackagesCopiedToReleasePPA(MAASPPA, PPACopyMixin):
         )
 
     def check(self):
-        sources = {
-            "stable": MAASPPA(self.preparer, "candidate"),
-            "candidate": MAASPPA(self.preparer, "release-preparation"),
-        }
-        source = sources[self.ppa_type]
+        if not self.preparer.version.new_series:
+            self.selected = ["maas"]
         try:
-            source_ppa = source.ppa_owner.getPPAByName(name=source.ppa_name)
+            self.source_ppa = self.source.ppa_owner.getPPAByName(
+                name=self.source.ppa_name
+            )
         except NotFound:
             return (
                 False,
                 f"ppa:{self.ppa_path} couldn't be found.",
             )
         try:
-            target_ppa = self.ppa_owner.getPPAByName(name=self.ppa_name)
+            self.target_ppa = self.ppa_owner.getPPAByName(name=self.ppa_name)
         except NotFound:
             return (
                 False,
                 f"ppa:{self.ppa_path} couldn't be found.",
             )
         else:
-            return self.check_packages_copied(source_ppa, target_ppa)
+            return self.check_packages_copied(
+                self.source_ppa, self.target_ppa, self.selected
+            )
+
+    def fix(self, doit: bool = False) -> Tuple[bool, list[str]]:
+        if self.source_ppa is None:
+            return False, self.how_to_create_ppa(
+                self.source.ppa_owner, self.source.name
+            )
+        if self.target_ppa is None:
+            return False, self.how_to_create_ppa(self.ppa_owner, self.ppa_name)
+        procedure = [
+            f"Go to {self.source_ppa.web_link}/+copy-packages",
+            f"Under `Packages`, in the right middle of the screen, select the `{get_ubuntu_series()}` OS series and click `Filter`",
+            f"select {self.selected or 'all'} packages",
+            "select `copy existing binaries`",
+            f"Select `{self.target_ppa.name}` as Destination PPA",
+            "Click `Copy packages`",
+        ]
+        return False, procedure
 
 
 def macaroon_auth(macaroons):
@@ -629,12 +692,22 @@ class PackageBuilt(ReleaseStep):
 
 
 class SnapsUploaded(ReleaseStep):
-
-    snap_name = "maas"
+    def __init__(
+        self,
+        preparer: ReleasePreparer,
+        snap_name: str = "maas",
+        snap_id: str = MAAS_SNAP_ID,
+        cwd: Optional[str] = None,
+    ):
+        super().__init__(preparer, cwd)
+        self.snap_name = snap_name
+        self.snap_id = snap_id
 
     @property
     def title(self):
-        return "Snaps have been built and uploaded to the store."
+        return (
+            f"Snap `{self.snap_name}` has been built and uploaded to the store"
+        )
 
     @lru_cache(maxsize=1)
     def _get_revisisions(self):
@@ -645,7 +718,7 @@ class SnapsUploaded(ReleaseStep):
         #     check if the snap has been uploaded, if the snap is
         #     already released to a channel.
         res = requests.get(
-            f"https://dashboard.snapcraft.io/dev/api/snaps/{MAAS_SNAP_ID}/";
+            f"https://dashboard.snapcraft.io/dev/api/snaps/{self.snap_id}/";
             + "history",
             headers={
                 "Authorization": self.preparer.snapstore_auth,
@@ -657,14 +730,16 @@ class SnapsUploaded(ReleaseStep):
             return None, auth_error
         revision_map = {arch: [] for arch in BUILD_ARCHS}
         for revision in res.json():
-            version = revision["version"]
-            if not version.startswith(self.preparer.version.deb_version):
-                continue
-            if not version.endswith(self.preparer.git_short_rev):
-                continue
-            revision_map[revision["arch"]].append(revision)
+            if self._filter_revision(revision):
+                revision_map[revision["arch"]].append(revision)
         return revision_map, None
 
+    def _filter_revision(self, revision) -> bool:
+        version = revision["version"]
+        return version.startswith(
+            self.preparer.version.deb_version
+        ) and version.endswith(self.preparer.git_short_rev)
+
     def check(self):
         revision_map, error_message = self._get_revisisions()
         if revision_map is None:
@@ -688,7 +763,7 @@ class SnapsUploaded(ReleaseStep):
         return True, "\n".join(revision_info)
 
     def fix(self, doit=False):
-        builder = f"maas-{self.preparer.version.major}"
+        builder = f"{self.snap_name}-{self.preparer.version.major}"
         steps = []
         if self.preparer.launchpad.snap_builder_exist(builder):
             steps.append(
@@ -698,7 +773,7 @@ class SnapsUploaded(ReleaseStep):
             steps.extend(
                 [
                     f" go to https://code.launchpad.net/~{MAAS_USER}/maas/+git/maas/+ref/{self.preparer.version.major}/+new-snap";,
-                    f"  * snap recipe name: maas-{self.preparer.version.major}",
+                    f"  * snap recipe name: {self.snap_name}-{self.preparer.version.major}",
                     "  * owner: maas",
                     f"  * processors: {', '.join(BUILD_ARCHS)}",
                     "  * Automatically build when branch changes",
@@ -711,17 +786,21 @@ class SnapsUploaded(ReleaseStep):
 
 
 class SnapsInChannel(SnapsUploaded):
-
-    snap_name = "maas"
-
-    def __init__(self, preparer, channel):
-        super().__init__(preparer)
+    def __init__(
+        self,
+        preparer: ReleasePreparer,
+        channel: str,
+        snap_name: str = "maas",
+        snap_id: str = MAAS_SNAP_ID,
+        cwd: Optional[str] = None,
+    ):
+        super().__init__(preparer, snap_name, snap_id, cwd)
         self.channel = channel
         self.missing_archs = []
 
     @property
     def title(self):
-        return f"Snaps have been released to {self.channel}"
+        return f"Snap `{self.snap_name}` has been released to {self.channel}"
 
     def check(self):
         revision_map, error_message = self._get_revisisions()
@@ -756,12 +835,31 @@ class SnapsInChannel(SnapsUploaded):
                     revision["revision"] for revision in revision_map[arch]
                 )
                 steps.append(
-                    f"snapcraft release maas {latest_revision} {self.channel}"
+                    f"snapcraft release {self.snap_name} {latest_revision} {self.channel}"
                 )
 
         return False, steps
 
 
+class SnapTestDBInChannel(SnapsInChannel):
+    def __init__(
+        self,
+        preparer: ReleasePreparer,
+        channel: str,
+        cwd: Optional[str] = None,
+    ):
+        super().__init__(
+            preparer,
+            channel=channel,
+            snap_name="maas-test-db",
+            snap_id=MAAS_TEST_DB_SNAP_ID,
+            cwd=cwd,
+        )
+
+    def _filter_revision(self, revision) -> bool:
+        return True
+
+
 class ReleaseTagged(ReleaseStep):
     @property
     def title(self):
@@ -929,7 +1027,7 @@ class MilestoneReleased(ReleaseStep):
         if not self._ms_found:
             return False, ["Pre-requesites check has failed, check above"]
         return False, [
-            f"run {os.path.dirname(sys.argv[0])}/release-manage maas release-milestone {self.preparer.version.version}'"
+            f"run {os.path.dirname(sys.argv[0])}/release-manage maas release-milestone {self.preparer.version.version}"
         ]
 
 
@@ -1079,6 +1177,10 @@ def main():
             SnapsInChannel(preparer, snap_channel)
             for snap_channel in release_version.snap_channels
         ],
+        *[
+            SnapTestDBInChannel(preparer, snap_channel)
+            for snap_channel in release_version.snap_channels
+        ],
         PackagesCopiedToReleasePPA(preparer, "stable"),
         ReleaseTagged(preparer),
         MilestoneExist(preparer),
diff --git a/maas_release_tools/version.py b/maas_release_tools/version.py
index cd5fbca..e238557 100644
--- a/maas_release_tools/version.py
+++ b/maas_release_tools/version.py
@@ -30,6 +30,7 @@ class ReleaseVersion:
         self.snap_channels = self._snap_channels()
         self.deb_version = self.version.replace("-", "~")
         self.final_version = self._final()
+        self.new_series = self.final_version.rsplit(".", 1)[1] == "0"
 
     def _python_version(self) -> Version:
         string_version = (

Follow ups