← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:ci-build-upload-job-filetype into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:ci-build-upload-job-filetype into launchpad:master with ~cjwatson/launchpad:scan-conda as a prerequisite.

Commit message:
Extend filetype detection in CIBuildUploadJob

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

`BinaryPackageRelease.addFile` attempts to detect the `BinaryPackageFileType` based on the file name, but this isn't likely to be reliably possible for all new package types.  Determine this in `CIBuildUploadJob` and pass it down.

The intent of the distinction between `BinaryPackageFormat` and `BinaryPackageFileType` is unclear to me, but their values don't completely coincide so merging them would be non-trivial.  For now, we can at least maintain a one-to-one mapping for the values relevant to `CIBuildUploadJob`.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:ci-build-upload-job-filetype into launchpad:master.
diff --git a/lib/lp/soyuz/enums.py b/lib/lp/soyuz/enums.py
index 5bc948a..93f4a70 100644
--- a/lib/lp/soyuz/enums.py
+++ b/lib/lp/soyuz/enums.py
@@ -221,6 +221,19 @@ class BinaryPackageFileType(DBEnumeratedType):
         U{https://peps.python.org/pep-0427/}.
         """)
 
+    CONDA_V1 = DBItem(7, """
+        Conda Package v1
+
+        Version 1 of the Conda package format, with the ".tar.bz2" extension.
+        """)
+
+    CONDA_V2 = DBItem(8, """
+        Conda Package v2
+
+        Version 2 of the Conda package format, with the ".conda" extension;
+        introduced in Conda 4.7.
+        """)
+
 
 class BinaryPackageFormat(DBEnumeratedType):
     """Binary Package Format
diff --git a/lib/lp/soyuz/interfaces/binarypackagerelease.py b/lib/lp/soyuz/interfaces/binarypackagerelease.py
index a4c9e4b..2b243cb 100644
--- a/lib/lp/soyuz/interfaces/binarypackagerelease.py
+++ b/lib/lp/soyuz/interfaces/binarypackagerelease.py
@@ -96,9 +96,12 @@ class IBinaryPackageRelease(Interface):
     sourcepackageversion = Attribute(
         "The version of the source package from where this binary was built.")
 
-    def addFile(file):
+    def addFile(file, filetype=None):
         """Create a BinaryPackageFile record referencing this build
         and attach the provided library file alias (file).
+
+        If filetype is None, then the file type is automatically detected
+        based on the file name, if possible.
         """
 
     def override(component=None, section=None, priority=None):
diff --git a/lib/lp/soyuz/model/archivejob.py b/lib/lp/soyuz/model/archivejob.py
index a183ae8..cc9b75b 100644
--- a/lib/lp/soyuz/model/archivejob.py
+++ b/lib/lp/soyuz/model/archivejob.py
@@ -42,6 +42,7 @@ from lp.services.job.runner import BaseRunnableJob
 from lp.services.librarian.utils import copy_and_close
 from lp.soyuz.enums import (
     ArchiveJobType,
+    BinaryPackageFileType,
     BinaryPackageFormat,
     PackageUploadStatus,
     )
@@ -201,6 +202,18 @@ class CIBuildUploadJob(ArchiveJobDerived):
 
     config = config.ICIBuildUploadJobSource
 
+    # XXX cjwatson 2022-06-10: There doesn't seem to be a very clear
+    # conceptual distinction between BinaryPackageFormat and
+    # BinaryPackageFileType, but we end up having to add entries to both for
+    # each new package type because they're used in different database
+    # columns.  Try to minimize the hassle involved in this by maintaining a
+    # mapping here of all the formats we're interested in.
+    filetype_by_format = {
+        BinaryPackageFormat.WHL: BinaryPackageFileType.WHL,
+        BinaryPackageFormat.CONDA_V1: BinaryPackageFileType.CONDA_V1,
+        BinaryPackageFormat.CONDA_V2: BinaryPackageFileType.CONDA_V2,
+        }
+
     @classmethod
     def create(cls, ci_build, requester, target_archive, target_distroseries,
                target_pocket, target_channel=None):
@@ -332,8 +345,10 @@ class CIBuildUploadJob(ArchiveJobDerived):
                 metadata["binarypackagename"] = (
                     getUtility(IBinaryPackageNameSet).ensure(metadata["name"]))
                 del metadata["name"]
+                filetype = self.filetype_by_format[
+                    metadata["binpackageformat"]]
                 bpr = self.ci_build.createBinaryPackageRelease(**metadata)
-                bpr.addFile(artifact.library_file)
+                bpr.addFile(artifact.library_file, filetype=filetype)
                 # The publishBinaries interface was designed for .debs,
                 # which need extra per-binary "override" information
                 # (component, etc.).  None of this is relevant here.
diff --git a/lib/lp/soyuz/model/binarypackagerelease.py b/lib/lp/soyuz/model/binarypackagerelease.py
index 7134895..15192c2 100644
--- a/lib/lp/soyuz/model/binarypackagerelease.py
+++ b/lib/lp/soyuz/model/binarypackagerelease.py
@@ -146,26 +146,26 @@ class BinaryPackageRelease(SQLBase):
         return list(
             Store.of(self).find(BinaryPackageFile, binarypackagerelease=self))
 
-    def addFile(self, file):
+    def addFile(self, file, filetype=None):
         """See `IBinaryPackageRelease`."""
-        determined_filetype = None
-        if file.filename.endswith(".deb"):
-            determined_filetype = BinaryPackageFileType.DEB
-        elif file.filename.endswith(".rpm"):
-            determined_filetype = BinaryPackageFileType.RPM
-        elif file.filename.endswith(".udeb"):
-            determined_filetype = BinaryPackageFileType.UDEB
-        elif file.filename.endswith(".ddeb"):
-            determined_filetype = BinaryPackageFileType.DDEB
-        elif file.filename.endswith(".whl"):
-            determined_filetype = BinaryPackageFileType.WHL
-        else:
-            raise AssertionError(
-                'Unsupported file type: %s' % file.filename)
+        if filetype is None:
+            if file.filename.endswith(".deb"):
+                filetype = BinaryPackageFileType.DEB
+            elif file.filename.endswith(".rpm"):
+                filetype = BinaryPackageFileType.RPM
+            elif file.filename.endswith(".udeb"):
+                filetype = BinaryPackageFileType.UDEB
+            elif file.filename.endswith(".ddeb"):
+                filetype = BinaryPackageFileType.DDEB
+            elif file.filename.endswith(".whl"):
+                filetype = BinaryPackageFileType.WHL
+            else:
+                raise AssertionError(
+                    'Unsupported file type: %s' % file.filename)
 
         del get_property_cache(self).files
         return BinaryPackageFile(binarypackagerelease=self,
-                                 filetype=determined_filetype,
+                                 filetype=filetype,
                                  libraryfile=file)
 
     def override(self, component=None, section=None, priority=None):
diff --git a/lib/lp/soyuz/tests/test_archivejob.py b/lib/lp/soyuz/tests/test_archivejob.py
index b96c8b5..d599c48 100644
--- a/lib/lp/soyuz/tests/test_archivejob.py
+++ b/lib/lp/soyuz/tests/test_archivejob.py
@@ -404,6 +404,98 @@ class TestCIBuildUploadJob(TestCaseWithFactory):
                 binarypackageformat=Equals(BinaryPackageFormat.WHL),
                 distroarchseries=Equals(dases[0]))))
 
+    def test_run_conda(self):
+        archive = self.factory.makeArchive()
+        distroseries = self.factory.makeDistroSeries(
+            distribution=archive.distribution)
+        dases = [
+            self.factory.makeDistroArchSeries(distroseries=distroseries)
+            for _ in range(2)]
+        build = self.factory.makeCIBuild(distro_arch_series=dases[0])
+        report = build.getOrCreateRevisionStatusReport("build:0")
+        report.setLog(b"log data")
+        path = "conda-arch/dist/linux-64/conda-arch-0.1-0.tar.bz2"
+        with open(datadir(path), mode="rb") as f:
+            report.attach(name=os.path.basename(path), data=f.read())
+        artifact = IStore(RevisionStatusArtifact).find(
+            RevisionStatusArtifact,
+            report=report,
+            artifact_type=RevisionStatusArtifactType.BINARY).one()
+        job = CIBuildUploadJob.create(
+            build, build.git_repository.owner, archive, distroseries,
+            PackagePublishingPocket.RELEASE, target_channel="edge")
+        transaction.commit()
+
+        with dbuser(job.config.dbuser):
+            JobRunner([job]).runAll()
+
+        self.assertThat(archive.getAllPublishedBinaries(), MatchesSetwise(
+            MatchesStructure(
+                binarypackagename=MatchesStructure.byEquality(
+                    name="conda-arch"),
+                binarypackagerelease=MatchesStructure(
+                    ci_build=Equals(build),
+                    binarypackagename=MatchesStructure.byEquality(
+                        name="conda-arch"),
+                    version=Equals("0.1"),
+                    summary=Equals("Example summary"),
+                    description=Equals("Example description"),
+                    binpackageformat=Equals(BinaryPackageFormat.CONDA_V1),
+                    architecturespecific=Is(True),
+                    homepage=Equals("http://example.com/";),
+                    files=MatchesSetwise(
+                        MatchesStructure.byEquality(
+                            libraryfile=artifact.library_file,
+                            filetype=BinaryPackageFileType.CONDA_V1))),
+                binarypackageformat=Equals(BinaryPackageFormat.CONDA_V1),
+                distroarchseries=Equals(dases[0]))))
+
+    def test_run_conda_v2(self):
+        archive = self.factory.makeArchive()
+        distroseries = self.factory.makeDistroSeries(
+            distribution=archive.distribution)
+        dases = [
+            self.factory.makeDistroArchSeries(distroseries=distroseries)
+            for _ in range(2)]
+        build = self.factory.makeCIBuild(distro_arch_series=dases[0])
+        report = build.getOrCreateRevisionStatusReport("build:0")
+        report.setLog(b"log data")
+        path = "conda-v2-arch/dist/linux-64/conda-v2-arch-0.1-0.conda"
+        with open(datadir(path), mode="rb") as f:
+            report.attach(name=os.path.basename(path), data=f.read())
+        artifact = IStore(RevisionStatusArtifact).find(
+            RevisionStatusArtifact,
+            report=report,
+            artifact_type=RevisionStatusArtifactType.BINARY).one()
+        job = CIBuildUploadJob.create(
+            build, build.git_repository.owner, archive, distroseries,
+            PackagePublishingPocket.RELEASE, target_channel="edge")
+        transaction.commit()
+
+        with dbuser(job.config.dbuser):
+            JobRunner([job]).runAll()
+
+        self.assertThat(archive.getAllPublishedBinaries(), MatchesSetwise(
+            MatchesStructure(
+                binarypackagename=MatchesStructure.byEquality(
+                    name="conda-v2-arch"),
+                binarypackagerelease=MatchesStructure(
+                    ci_build=Equals(build),
+                    binarypackagename=MatchesStructure.byEquality(
+                        name="conda-v2-arch"),
+                    version=Equals("0.1"),
+                    summary=Equals("Example summary"),
+                    description=Equals("Example description"),
+                    binpackageformat=Equals(BinaryPackageFormat.CONDA_V2),
+                    architecturespecific=Is(True),
+                    homepage=Equals("http://example.com/";),
+                    files=MatchesSetwise(
+                        MatchesStructure.byEquality(
+                            libraryfile=artifact.library_file,
+                            filetype=BinaryPackageFileType.CONDA_V2))),
+                binarypackageformat=Equals(BinaryPackageFormat.CONDA_V2),
+                distroarchseries=Equals(dases[0]))))
+
 
 class TestViaCelery(TestCaseWithFactory):