launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32761
[Merge] ~ines-almeida/launchpad:revert-add-external-package into launchpad:master
Ines Almeida has proposed merging ~ines-almeida/launchpad:revert-add-external-package into launchpad:master.
Commit message:
Revert "Add ExternalPackage model"
This reverts commit b5e5b4819d2f475e07537dd1cad6b9b4d9b35259, reversing
changes made to 4a935a27f849d9e76af17c154eb3e276e860afb7.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ines-almeida/launchpad/+git/launchpad/+merge/489098
Reverting this merge proposal: https://code.launchpad.net/~enriqueesanchz/launchpad/+git/launchpad/+merge/488673
There isn't anything very wrong with it, but we found an edge case in qastaging that might require attention, so might as well not block deployments for it.
This is the result of `git revert -m 1 b5e5b4819d2f475e07537dd1cad6b9b4d9b35259`
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ines-almeida/launchpad:revert-add-external-package into launchpad:master.
diff --git a/lib/lp/app/browser/tales.py b/lib/lp/app/browser/tales.py
index 69c98cc..1714f52 100644
--- a/lib/lp/app/browser/tales.py
+++ b/lib/lp/app/browser/tales.py
@@ -52,7 +52,6 @@ from lp.registry.interfaces.distribution import IDistribution
from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
-from lp.registry.interfaces.externalpackage import IExternalPackage
from lp.registry.interfaces.person import IPerson
from lp.registry.interfaces.product import IProduct
from lp.registry.interfaces.projectgroup import IProjectGroup
@@ -734,10 +733,6 @@ class ObjectImageDisplayAPI:
sprite_string = "distribution"
elif IDistributionSourcePackage.providedBy(context):
sprite_string = "package-source"
- elif IExternalPackage.providedBy(context):
- # enriqueesanchz 2025-07-15 TODO: create a new sprite for
- # ExternalPackages
- sprite_string = "package-source"
elif ISprint.providedBy(context):
sprite_string = "meeting"
elif IBug.providedBy(context):
diff --git a/lib/lp/app/widgets/launchpadtarget.py b/lib/lp/app/widgets/launchpadtarget.py
index ff3dae1..d296589 100644
--- a/lib/lp/app/widgets/launchpadtarget.py
+++ b/lib/lp/app/widgets/launchpadtarget.py
@@ -28,7 +28,6 @@ from lp.registry.interfaces.distribution import IDistribution
from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
-from lp.registry.interfaces.externalpackage import IExternalPackage
from lp.registry.interfaces.product import IProduct
from lp.services.features import getFeatureFlag
from lp.services.webapp.interfaces import (
@@ -219,10 +218,6 @@ class LaunchpadTargetWidget(BrowserWidget, InputWidget):
self.default_option = "package"
self.distribution_widget.setRenderedValue(value.distribution)
self.package_widget.setRenderedValue(value.sourcepackagename)
- elif IExternalPackage.providedBy(value):
- self.default_option = "package"
- self.distribution_widget.setRenderedValue(value.distribution)
- self.package_widget.setRenderedValue(value.sourcepackagename)
else:
raise AssertionError("Not a valid value: %r" % value)
diff --git a/lib/lp/app/widgets/tests/test_launchpadtarget.py b/lib/lp/app/widgets/tests/test_launchpadtarget.py
index d9cd400..40350b8 100644
--- a/lib/lp/app/widgets/tests/test_launchpadtarget.py
+++ b/lib/lp/app/widgets/tests/test_launchpadtarget.py
@@ -61,9 +61,6 @@ class LaunchpadTargetWidgetTestCase(TestCaseWithFactory):
self.package = self.factory.makeDSPCache(
distroseries=distroseries, sourcepackagename="snarf"
)
- self.externalpackage = self.factory.makeExternalPackage(
- distribution=self.distribution, sourcepackagename="foo"
- )
self.project = self.factory.makeProduct("pting")
field = Reference(__name__="target", schema=Interface, title="target")
field = field.bind(Thing())
@@ -316,21 +313,6 @@ class LaunchpadTargetWidgetTestCase(TestCaseWithFactory):
self.widget.package_widget._getCurrentValue(),
)
- def test_setRenderedValue_externalpackage(self):
- # Passing an external package will set the widget's render state to
- # 'externalpackage'.
- self.widget.setUpSubWidgets()
- self.widget.setRenderedValue(self.externalpackage)
- self.assertEqual("package", self.widget.default_option)
- self.assertEqual(
- self.distribution,
- self.widget.distribution_widget._getCurrentValue(),
- )
- self.assertEqual(
- self.externalpackage.sourcepackagename,
- self.widget.package_widget._getCurrentValue(),
- )
-
def test_call(self):
# The __call__ method setups the widgets and the options.
markup = self.widget()
diff --git a/lib/lp/bugs/browser/bugtask.py b/lib/lp/bugs/browser/bugtask.py
index 9ec5f1a..8fcb135 100644
--- a/lib/lp/bugs/browser/bugtask.py
+++ b/lib/lp/bugs/browser/bugtask.py
@@ -126,7 +126,6 @@ from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
from lp.registry.interfaces.distroseries import IDistroSeries, IDistroSeriesSet
-from lp.registry.interfaces.externalpackage import IExternalPackage
from lp.registry.interfaces.ociproject import IOCIProject
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.product import IProduct
@@ -320,29 +319,12 @@ class BugTargetTraversalMixin:
# anonymous user is presented with a login screen at the correct URL,
# rather than making it look as though this task was "not found",
# because it was filtered out by privacy-aware code.
-
- externalpackage_bugtask = None
-
for bugtask in bug.bugtasks:
if bugtask.target == context:
# Security proxy this object on the way out.
return getUtility(IBugTaskSet).get(bugtask.id)
- if (
- externalpackage_bugtask is None
- and IExternalPackage.providedBy(bugtask.target)
- ):
- # enriqueesanchz 2025-07-15 TODO: set +external urls for
- # ExternalPackages. Currently we select the first
- # ExternalPackage that appears if we don't have other match
- externalpackage_bugtask = getUtility(IBugTaskSet).get(
- bugtask.id
- )
-
# If we've come this far, there's no task for the requested context.
- if externalpackage_bugtask:
- return externalpackage_bugtask
-
# If we are attempting to navigate past the non-existent bugtask,
# we raise NotFound error. eg +delete or +edit etc.
# Otherwise we are simply navigating to a non-existent task and so we
@@ -1838,16 +1820,6 @@ def bugtask_sort_key(bugtask):
None,
None,
)
- elif IExternalPackage.providedBy(bugtask.target):
- key = (
- bugtask.target.sourcepackagename.name,
- bugtask.target.distribution.displayname,
- bugtask.target.packagetype,
- bugtask.target.channel,
- None,
- None,
- None,
- )
elif ISourcePackage.providedBy(bugtask.target):
key = (
bugtask.target.sourcepackagename.name,
diff --git a/lib/lp/bugs/browser/configure.zcml b/lib/lp/bugs/browser/configure.zcml
index 3b1c7d8..beeef02 100644
--- a/lib/lp/bugs/browser/configure.zcml
+++ b/lib/lp/bugs/browser/configure.zcml
@@ -1180,10 +1180,6 @@
layer="lp.bugs.publisher.BugsLayer"
name="+bugs"/>
<browser:defaultView
- for="lp.registry.interfaces.externalpackage.IExternalPackage"
- layer="lp.bugs.publisher.BugsLayer"
- name="+bugs"/>
- <browser:defaultView
for="lp.registry.interfaces.person.IPerson"
layer="lp.bugs.publisher.BugsLayer"
name="+bugs"/>
diff --git a/lib/lp/bugs/browser/structuralsubscription.py b/lib/lp/bugs/browser/structuralsubscription.py
index c4188ba..ec278f7 100644
--- a/lib/lp/bugs/browser/structuralsubscription.py
+++ b/lib/lp/bugs/browser/structuralsubscription.py
@@ -35,7 +35,6 @@ from lp.registry.interfaces.distribution import IDistribution
from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
-from lp.registry.interfaces.externalpackage import IExternalPackage
from lp.registry.interfaces.milestone import IProjectGroupMilestone
from lp.registry.interfaces.person import IPerson, IPersonSet
from lp.services.propertycache import cachedproperty
@@ -285,11 +284,9 @@ class StructuralSubscriptionView(LaunchpadFormView):
def userIsDriver(self):
"""Has the current user driver permissions?"""
# We only want to look at this if the target is a
- # distribution source package or external package, in order to
- # maintain compatibility with the obsolete bug contacts feature.
- if IDistributionSourcePackage.providedBy(
- self.context
- ) or IExternalPackage.providedBy(self.context):
+ # distribution source package, in order to maintain
+ # compatibility with the obsolete bug contacts feature.
+ if IDistributionSourcePackage.providedBy(self.context):
return check_permission(
"launchpad.Driver", self.context.distribution
)
diff --git a/lib/lp/bugs/interfaces/bugtask.py b/lib/lp/bugs/interfaces/bugtask.py
index 9d8a36c..e22d8d0 100644
--- a/lib/lp/bugs/interfaces/bugtask.py
+++ b/lib/lp/bugs/interfaces/bugtask.py
@@ -478,17 +478,6 @@ class IBugTask(IHasBug, IBugTaskDelete):
title=_("Package"), required=False, vocabulary="SourcePackageName"
)
sourcepackagename_id = Attribute("The sourcepackagename ID")
-
- packagetype = Int(
- title=_("Package type"),
- default=None,
- readonly=True,
- )
-
- channel = Attribute("The package channel")
-
- metadata = Attribute("Bugtask metadata")
-
distribution = Choice(
title=_("Distribution"), required=False, vocabulary="Distribution"
)
diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py
index 1f78384..d6116ce 100644
--- a/lib/lp/bugs/model/bugtask.py
+++ b/lib/lp/bugs/model/bugtask.py
@@ -23,7 +23,6 @@ from itertools import chain, repeat
from operator import attrgetter, itemgetter
from lazr.lifecycle.event import ObjectDeletedEvent
-from storm.databases.postgres import JSON
from storm.expr import (
SQL,
And,
@@ -79,10 +78,6 @@ from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.registry.interfaces.externalpackage import (
- ExternalPackageType,
- IExternalPackage,
-)
from lp.registry.interfaces.milestone import IMilestoneSet
from lp.registry.interfaces.milestonetag import IProjectGroupMilestoneTag
from lp.registry.interfaces.ociproject import IOCIProject
@@ -175,8 +170,6 @@ def bug_target_from_key(
distroseries,
sourcepackagename,
ociproject,
- packagetype,
- channel,
):
"""Returns the IBugTarget defined by the given DB column values."""
if ociproject:
@@ -189,11 +182,7 @@ def bug_target_from_key(
elif productseries:
return productseries
elif distribution:
- if sourcepackagename and packagetype:
- return distribution.getExternalPackage(
- sourcepackagename, packagetype, removeSecurityProxy(channel)
- )
- elif sourcepackagename:
+ if sourcepackagename:
return distribution.getSourcePackage(sourcepackagename)
else:
return distribution
@@ -215,8 +204,6 @@ def bug_target_to_key(target):
distroseries=None,
sourcepackagename=None,
ociproject=None,
- packagetype=None,
- channel=None,
)
if IProduct.providedBy(target):
values["product"] = target
@@ -232,11 +219,6 @@ def bug_target_to_key(target):
elif ISourcePackage.providedBy(target):
values["distroseries"] = target.distroseries
values["sourcepackagename"] = target.sourcepackagename
- elif IExternalPackage.providedBy(target):
- values["distribution"] = target.distribution
- values["sourcepackagename"] = target.sourcepackagename
- values["packagetype"] = target.packagetype
- values["channel"] = removeSecurityProxy(target).channel
elif IOCIProject.providedBy(target):
# De-normalize the ociproject, including also the ociproject's
# pillar (distribution or product).
@@ -389,12 +371,6 @@ def validate_target(
)
except NotFoundError as e:
raise IllegalTarget(e.args[0])
- elif IExternalPackage.providedBy(target):
- # enriqueensanchz 2025-07-15 TODO: we are creating a bugtask for an
- # ExternalPackage that is published in the Store/SOSS, we need to use
- # their API to check that it exists as we dont have any data in
- # Launchpad database
- pass
legal_types = target.pillar.getAllowedBugInformationTypes()
new_pillar = target.pillar not in bug.affected_pillars
@@ -446,9 +422,7 @@ def validate_new_target(bug, target, check_source_package=True):
"affected package in which the bug has not yet "
"been reported." % target.displayname
)
- elif IDistributionSourcePackage.providedBy(
- target
- ) or IExternalPackage.providedBy(target):
+ elif IDistributionSourcePackage.providedBy(target):
# Ensure that there isn't already a generic task open on the
# distribution for this bug, because if there were, that task
# should be reassigned to the sourcepackage, rather than a new
@@ -519,17 +493,6 @@ class BugTask(StormBase):
sourcepackagename_id = Int(name="sourcepackagename", allow_none=True)
sourcepackagename = Reference(sourcepackagename_id, "SourcePackageName.id")
- packagetype = DBEnum(
- name="packagetype",
- allow_none=True,
- enum=ExternalPackageType,
- default=None,
- )
-
- channel = JSON(name="channel", allow_none=True, default=None)
-
- metadata = JSON(name="metadata", allow_none=True, default=None)
-
distribution_id = Int(name="distribution", allow_none=True)
distribution = Reference(distribution_id, "Distribution.id")
@@ -697,8 +660,6 @@ class BugTask(StormBase):
self.distroseries,
self.sourcepackagename,
self.ociproject,
- self.packagetype,
- self.channel,
)
@property
@@ -1922,8 +1883,6 @@ class BugTaskSet:
key["distribution"],
key["distroseries"],
key["sourcepackagename"],
- key["packagetype"],
- key["channel"],
key["ociproject"],
status,
importance,
@@ -1941,8 +1900,6 @@ class BugTaskSet:
BugTask.distribution,
BugTask.distroseries,
BugTask.sourcepackagename,
- BugTask.packagetype,
- BugTask.channel,
BugTask.ociproject,
BugTask._status,
BugTask.importance,
diff --git a/lib/lp/bugs/model/structuralsubscription.py b/lib/lp/bugs/model/structuralsubscription.py
index 9b76421..37e8be2 100644
--- a/lib/lp/bugs/model/structuralsubscription.py
+++ b/lib/lp/bugs/model/structuralsubscription.py
@@ -58,7 +58,6 @@ from lp.registry.interfaces.distributionsourcepackage import (
IDistributionSourcePackage,
)
from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.registry.interfaces.externalpackage import IExternalPackage
from lp.registry.interfaces.milestone import IMilestone
from lp.registry.interfaces.ociproject import IOCIProject
from lp.registry.interfaces.person import (
@@ -608,13 +607,7 @@ def get_structural_subscriptions_for_bug(bug, person=None):
# This is here because of a circular import.
from lp.registry.model.person import Person
- bugtasks = []
- # enriqueesanchz 2025-07-15 TODO: support bug subscriptions for
- # ExternalPackages
- for bugtask in bug.bugtasks:
- if not IExternalPackage.providedBy(bugtask.target):
- bugtasks.append(bugtask)
-
+ bugtasks = bug.bugtasks
if not bugtasks:
return EmptyResultSet()
conditions = []
diff --git a/lib/lp/bugs/model/tests/test_bugtask.py b/lib/lp/bugs/model/tests/test_bugtask.py
index 54a39e2..4549f0a 100644
--- a/lib/lp/bugs/model/tests/test_bugtask.py
+++ b/lib/lp/bugs/model/tests/test_bugtask.py
@@ -3174,8 +3174,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distroseries=None,
sourcepackagename=None,
ociproject=None,
- packagetype=None,
- channel=None,
),
)
@@ -3189,8 +3187,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distribution=None,
distroseries=None,
sourcepackagename=None,
- packagetype=None,
- channel=None,
ociproject=None,
),
)
@@ -3205,8 +3201,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distribution=distro,
distroseries=None,
sourcepackagename=None,
- packagetype=None,
- channel=None,
ociproject=None,
),
)
@@ -3221,8 +3215,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distribution=None,
distroseries=distroseries,
sourcepackagename=None,
- packagetype=None,
- channel=None,
ociproject=None,
),
)
@@ -3237,24 +3229,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distribution=dsp.distribution,
distroseries=None,
sourcepackagename=dsp.sourcepackagename,
- packagetype=None,
- channel=None,
- ociproject=None,
- ),
- )
-
- def test_externalpackage(self):
- externalpackage = self.factory.makeExternalPackage()
- self.assertTargetKeyWorks(
- externalpackage,
- dict(
- product=None,
- productseries=None,
- distribution=externalpackage.distribution,
- distroseries=None,
- sourcepackagename=externalpackage.sourcepackagename,
- packagetype=externalpackage.packagetype,
- channel=externalpackage.channel,
ociproject=None,
),
)
@@ -3269,8 +3243,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distribution=None,
distroseries=sp.distroseries,
sourcepackagename=sp.sourcepackagename,
- packagetype=None,
- channel=None,
ociproject=None,
),
)
@@ -3286,8 +3258,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distribution=pillar,
distroseries=None,
sourcepackagename=None,
- packagetype=None,
- channel=None,
ociproject=ociproject,
),
)
@@ -3303,8 +3273,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
distribution=None,
distroseries=None,
sourcepackagename=None,
- packagetype=None,
- channel=None,
ociproject=ociproject,
),
)
@@ -3324,8 +3292,6 @@ class TestBugTargetKeys(TestCaseWithFactory):
None,
None,
None,
- None,
- None,
)
@@ -3615,44 +3581,6 @@ class TestValidateTarget(TestCaseWithFactory, ValidateTargetMixin):
dsp,
)
- def test_externalpackage_task_is_allowed(self):
- # An ExternalPackage task can coexist with a task for its Distribution.
- d = self.factory.makeDistribution()
- task = self.factory.makeBugTask(target=d)
- externalpackage = self.factory.makeExternalPackage(distribution=d)
- validate_target(task.bug, externalpackage)
-
- def test_externalpackage_task_with_dsp_is_allowed(self):
- # An ExternalPackage task can coexist with a task for a
- # DistributionSourcePackage with the same name.
- d = self.factory.makeDistribution()
- dsp = self.factory.makeDistributionSourcePackage(distribution=d)
- task = self.factory.makeBugTask(target=dsp)
- externalpackage = self.factory.makeExternalPackage(distribution=d)
- validate_target(task.bug, externalpackage)
-
- def test_different_externalpackage_tasks_are_allowed(self):
- # An ExternalPackage task can also coexist with a task for another one.
- externalpackage = self.factory.makeExternalPackage()
- task = self.factory.makeBugTask(target=externalpackage)
- externalpackage = self.factory.makeExternalPackage(
- distribution=externalpackage.distribution
- )
- validate_target(task.bug, externalpackage)
-
- def test_same_externalpackage_task_is_forbidden(self):
- # But an ExternalPackage task cannot coexist with a task for itself.
- externalpackage = self.factory.makeExternalPackage()
- task = self.factory.makeBugTask(target=externalpackage)
- self.assertRaisesWithContent(
- IllegalTarget,
- "A fix for this bug has already been requested for %s"
- % (externalpackage.displayname),
- validate_target,
- task.bug,
- externalpackage,
- )
-
def test_illegal_information_type_disallowed(self):
# The bug's current information_type must be permitted by the
# new target.
@@ -3751,38 +3679,6 @@ class TestValidateNewTarget(TestCaseWithFactory, ValidateTargetMixin):
d,
)
- def test_externalpackage_task_with_distribution_task_forbidden(self):
- # You can't create a BugTask for the ExternalPackage if a Distribution
- # BugTask already exists, you need to re target it
- d = self.factory.makeDistribution()
- externalpackage = self.factory.makeExternalPackage(distribution=d)
- task = self.factory.makeBugTask(target=d)
- self.assertRaisesWithContent(
- IllegalTarget,
- "This bug is already open on %s with no package specified. "
- "You should fill in a package name for the existing bug."
- % d.displayname,
- validate_new_target,
- task.bug,
- externalpackage,
- )
-
- def test_distribution_task_with_externalpackage_task_forbidden(self):
- # You can't create a BugTask for the Distribution if an ExternalPackage
- # BugTask already exists, you need to re target it
- d = self.factory.makeDistribution()
- externalpackage = self.factory.makeExternalPackage(distribution=d)
- task = self.factory.makeBugTask(target=externalpackage)
- self.assertRaisesWithContent(
- IllegalTarget,
- "This bug is already on %s. Please specify an affected "
- "package in which the bug has not yet been reported."
- % d.displayname,
- validate_new_target,
- task.bug,
- d,
- )
-
class TestWebservice(TestCaseWithFactory):
"""Tests for the webservice."""
@@ -3931,11 +3827,6 @@ class TestBugTaskUserHasBugSupervisorPrivilegesContext(TestCaseWithFactory):
dsp = self.factory.makeDistributionSourcePackage()
self.assert_userHasBugSupervisorPrivilegesContext(dsp)
- def test_externalpackage(self):
- # A user does not have bug supervisor privileges in the externalpackage
- externalpackage = self.factory.makeExternalPackage()
- self.assert_userHasBugSupervisorPrivilegesContext(externalpackage)
-
def test_product(self):
product = self.factory.makeProduct()
self.assert_userHasBugSupervisorPrivilegesContext(product)
diff --git a/lib/lp/bugs/scripts/bugsummaryrebuild.py b/lib/lp/bugs/scripts/bugsummaryrebuild.py
index 12e0c76..088ce97 100644
--- a/lib/lp/bugs/scripts/bugsummaryrebuild.py
+++ b/lib/lp/bugs/scripts/bugsummaryrebuild.py
@@ -109,9 +109,7 @@ def load_target(pid, psid, did, dsid, spnid, ociproject_id):
(pid, psid, did, dsid, spnid, ociproject_id),
),
)
- # enriqueesanchz 2025-07-16 TODO: modify when BugSummary for
- # ExternalPackage implemented
- return bug_target_from_key(p, ps, d, ds, spn, ociproject, None, None)
+ return bug_target_from_key(p, ps, d, ds, spn, ociproject)
def format_target(target):
@@ -132,16 +130,7 @@ def format_target(target):
def _get_bugsummary_constraint_bits(target):
raw_key = bug_target_to_key(target)
# Map to ID columns to work around Storm bug #682989.
- constraint_bits = {}
- for k, v in raw_key.items():
- # enriqueesanchz 2025-07-16 TODO: implement BugSummary for packagetype
- # and channel
- if k != "packagetype" and k != "channel":
- key = "%s_id" % k
- value = v.id if v else None
- constraint_bits[key] = value
-
- return constraint_bits
+ return {"%s_id" % k: v.id if v else None for (k, v) in raw_key.items()}
def get_bugsummary_constraint(target, cls=RawBugSummary):
@@ -165,14 +154,10 @@ def get_bugtaskflat_constraint(target):
if IProduct.providedBy(target):
del raw_key["ociproject"]
# Map to ID columns to work around Storm bug #682989.
- constraint = []
- for k, v in raw_key.items():
- if k != "packagetype" and k != "channel":
- key = "%s_id" % k
- value = v.id if v else None
- constraint.append(getattr(BugTaskFlat, key) == value)
-
- return constraint
+ return [
+ getattr(BugTaskFlat, "%s_id" % k) == (v.id if v else None)
+ for (k, v) in raw_key.items()
+ ]
def get_bugsummary_rows(target):
diff --git a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
index e04f18e..70a82d4 100644
--- a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
+++ b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
@@ -107,10 +107,7 @@ class BugTaskTargetNameCachesTunableLoop:
(store.get(cls, id) if id is not None else None)
for cls, id in zip(target_classes, target_bits)
)
-
- # We don't need packagetype and channel to get items from
- # target_classes, as there is no ExternalPackage class there
- target = bug_target_from_key(*target_objects, None, None)
+ target = bug_target_from_key(*target_objects)
new_name = target.bugtargetdisplayname
cached_names.discard(new_name)
# If there are any outdated names cached, update them all in
diff --git a/lib/lp/bugs/tests/test_structuralsubscription.py b/lib/lp/bugs/tests/test_structuralsubscription.py
index 411a577..ce1f712 100644
--- a/lib/lp/bugs/tests/test_structuralsubscription.py
+++ b/lib/lp/bugs/tests/test_structuralsubscription.py
@@ -548,24 +548,6 @@ class TestGetStructuralSubscriptionTargets(TestCaseWithFactory):
},
)
- def test_externalpackage_target(self):
- actor = self.factory.makePerson()
- login_person(actor)
- externalpackage = self.factory.makeExternalPackage()
- product = self.factory.makeProduct()
- bug = self.factory.makeBug(target=product)
- bug.addTask(actor, externalpackage)
- product_bugtask = bug.bugtasks[0]
- result = get_structural_subscription_targets(bug.bugtasks)
- # enriqueesanchz 2025-07-15: We don't have an ExternalPackage here as
- # it is not implemented yet
- self.assertEqual(
- set(result),
- {
- (product_bugtask, product),
- },
- )
-
def test_product_with_project_group(self):
# get_structural_subscription_targets() will yield both a
# product and its parent project group if it has one.
diff --git a/lib/lp/registry/browser/configure.zcml b/lib/lp/registry/browser/configure.zcml
index f60b871..970fd02 100644
--- a/lib/lp/registry/browser/configure.zcml
+++ b/lib/lp/registry/browser/configure.zcml
@@ -554,10 +554,6 @@
for="lp.registry.interfaces.distributionsourcepackage.IDistributionSourcePackage"
urldata="lp.registry.browser.distributionsourcepackage.DistributionSourcePackageURL"
/>
- <lp:url
- for="lp.registry.interfaces.externalpackage.IExternalPackage"
- urldata="lp.registry.browser.distributionsourcepackage.DistributionSourcePackageURL"
- />
<lp:navigation
module="lp.registry.browser.distributionsourcepackage"
classes="
diff --git a/lib/lp/registry/configure.zcml b/lib/lp/registry/configure.zcml
index 22e60a9..ac416ec 100644
--- a/lib/lp/registry/configure.zcml
+++ b/lib/lp/registry/configure.zcml
@@ -608,21 +608,6 @@
provides="lp.services.webapp.interfaces.IBreadcrumb"
factory="lp.registry.browser.distributionsourcepackage.DistributionSourcePackageBreadcrumb"/>
- <!-- ExternalPackage -->
- <class
- class="lp.registry.model.externalpackage.ExternalPackage">
- <allow
- interface="lp.registry.interfaces.externalpackage.IExternalPackageView"/>
- <require
- permission="launchpad.BugSupervisor"
- set_attributes="
- bug_reported_acknowledgement
- bug_reporting_guidelines
- content_templates
- enable_bugfiling_duplicate_search
- "/>
- </class>
-
<!-- CommercialSubscription -->
<class
diff --git a/lib/lp/registry/interfaces/distribution.py b/lib/lp/registry/interfaces/distribution.py
index 336c843..ef966c7 100644
--- a/lib/lp/registry/interfaces/distribution.py
+++ b/lib/lp/registry/interfaces/distribution.py
@@ -878,11 +878,6 @@ class IDistributionView(
distribution, or None.
"""
- def getExternalPackage(name, packagetype, channel):
- """Return an ExternalPackage with the given name, packagetype and
- channel for this distribution.
- """
-
def getSourcePackageRelease(sourcepackagerelease):
"""Returns an IDistributionSourcePackageRelease
diff --git a/lib/lp/registry/interfaces/externalpackage.py b/lib/lp/registry/interfaces/externalpackage.py
deleted file mode 100644
index 2789943..0000000
--- a/lib/lp/registry/interfaces/externalpackage.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright 2009, 2025 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""External package interfaces."""
-
-__all__ = [
- "IExternalPackage",
- "ExternalPackageType",
-]
-
-from lazr.enum import DBEnumeratedType, DBItem
-from lazr.restful.declarations import exported, exported_as_webservice_entry
-from lazr.restful.fields import Reference
-from zope.interface import Attribute
-from zope.schema import TextLine
-
-from lp import _
-from lp.app.interfaces.launchpad import IHeadingContext
-from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags
-from lp.registry.interfaces.distribution import IDistribution
-from lp.registry.interfaces.role import IHasDrivers
-
-
-@exported_as_webservice_entry(as_of="beta")
-class IExternalPackageView(
- IHeadingContext,
- IBugTarget,
- IHasOfficialBugTags,
- IHasDrivers,
-):
- """`IExternalPackage` attributes that require launchpad.View."""
-
- packagetype = Attribute("The package type")
-
- channel = Attribute("The package channel")
-
- display_channel = TextLine(title=_("Display channel name"), readonly=True)
-
- distribution = exported(
- Reference(IDistribution, title=_("The distribution."))
- )
- sourcepackagename = Attribute("The source package name.")
-
- name = exported(
- TextLine(title=_("The source package name as text"), readonly=True)
- )
- display_name = exported(
- TextLine(title=_("Display name for this package."), readonly=True)
- )
- displayname = Attribute("Display name (deprecated)")
- title = exported(
- TextLine(title=_("Title for this package."), readonly=True)
- )
-
- drivers = Attribute("The drivers for the distribution.")
-
- def __eq__(other):
- """IExternalPackage comparison method.
-
- Distro sourcepackages compare equal only if their fields compare equal.
- """
-
- def __ne__(other):
- """IExternalPackage comparison method.
-
- External packages compare not equal if either of their
- fields compare not equal.
- """
-
-
-@exported_as_webservice_entry(as_of="beta")
-class IExternalPackage(
- IExternalPackageView,
-):
- """Represents an ExternalPackage in a distribution.
-
- Create IExternalPackage by invoking `IDistribution.getExternalPackage()`.
- """
-
-
-class ExternalPackageType(DBEnumeratedType):
- """ExternalPackageType
-
- The various possible types for an ExternalPackage.
- """
-
- UNKNOWN = DBItem(
- 0,
- """
- Unknown
-
- Unknown external package
- """,
- )
-
- SNAP = DBItem(
- 1,
- """
- Snap
-
- Snap external package
- """,
- )
-
- CHARM = DBItem(
- 2,
- """
- Charm
-
- Charm external package
- """,
- )
-
- ROCK = DBItem(
- 3,
- """
- Rock
-
- Rock external package
- """,
- )
-
- PYTHON = DBItem(
- 4,
- """
- Python
-
- Python external package
- """,
- )
-
- CONDA = DBItem(
- 5,
- """
- Conda
-
- Conda external package
- """,
- )
-
- CARGO = DBItem(
- 6,
- """
- Cargo
-
- Cargo external package
- """,
- )
-
- MAVEN = DBItem(
- 7,
- """
- Maven
-
- Maven external package
- """,
- )
diff --git a/lib/lp/registry/model/distribution.py b/lib/lp/registry/model/distribution.py
index 646475d..310381a 100644
--- a/lib/lp/registry/model/distribution.py
+++ b/lib/lp/registry/model/distribution.py
@@ -145,7 +145,6 @@ from lp.registry.model.distributionsourcepackage import (
)
from lp.registry.model.distroseries import DistroSeries
from lp.registry.model.distroseriesparent import DistroSeriesParent
-from lp.registry.model.externalpackage import ExternalPackage
from lp.registry.model.hasdrivers import HasDriversMixin
from lp.registry.model.karma import KarmaContextMixin
from lp.registry.model.milestone import HasMilestonesMixin, Milestone
@@ -1360,18 +1359,6 @@ class Distribution(
return None
return DistributionSourcePackage(self, sourcepackagename)
- def getExternalPackage(self, name, packagetype, channel):
- """See `IDistribution`."""
- if ISourcePackageName.providedBy(name):
- sourcepackagename = name
- else:
- sourcepackagename = getUtility(ISourcePackageNameSet).queryByName(
- name
- )
- if sourcepackagename is None:
- return None
- return ExternalPackage(self, sourcepackagename, packagetype, channel)
-
def getSourcePackageRelease(self, sourcepackagerelease):
"""See `IDistribution`."""
return DistributionSourcePackageRelease(self, sourcepackagerelease)
diff --git a/lib/lp/registry/model/externalpackage.py b/lib/lp/registry/model/externalpackage.py
deleted file mode 100644
index d97390d..0000000
--- a/lib/lp/registry/model/externalpackage.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Copyright 2009-2025 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Classes to represent external packages in a distribution."""
-
-__all__ = [
- "ExternalPackage",
-]
-
-from zope.interface import implementer
-
-from lp.bugs.model.bugtarget import BugTargetBase
-from lp.bugs.model.structuralsubscription import (
- StructuralSubscriptionTargetMixin,
-)
-from lp.registry.interfaces.distribution import IDistribution
-from lp.registry.interfaces.externalpackage import (
- ExternalPackageType,
- IExternalPackage,
-)
-from lp.registry.interfaces.sourcepackagename import ISourcePackageName
-from lp.registry.model.hasdrivers import HasDriversMixin
-from lp.services.channels import channel_list_to_string, channel_string_to_list
-from lp.services.propertycache import cachedproperty
-
-
-@implementer(IExternalPackage)
-class ExternalPackage(
- BugTargetBase,
- HasDriversMixin,
- StructuralSubscriptionTargetMixin,
-):
- """This is a "Magic External Package". It is not a Storm model, but instead
- it represents a package with a particular name, type and channel in a
- particular distribution.
- """
-
- def __init__(
- self,
- distribution: IDistribution,
- sourcepackagename: ISourcePackageName,
- packagetype: ExternalPackageType,
- channel: (str, tuple, list),
- ) -> "ExternalPackage":
- self.distribution = distribution
- self.sourcepackagename = sourcepackagename
- self.packagetype = packagetype
- self.channel = self.validate_channel(channel)
-
- def __repr__(self) -> str:
- return f"<{self.__class__.__name__} '{self.display_name}'>"
-
- def validate_channel(self, channel: (str, tuple, list)) -> tuple:
- if channel is None:
- return None
-
- if not isinstance(channel, (str, tuple, list)):
- raise ValueError("Channel must be a str, tuple or list")
-
- return channel_string_to_list(channel)
-
- @property
- def name(self) -> str:
- """See `IExternalPackage`."""
- return self.sourcepackagename.name
-
- @property
- def display_channel(self) -> str:
- """See `IExternalPackage`."""
- if not self.channel:
- return None
-
- return channel_list_to_string(*self.channel)
-
- @cachedproperty
- def display_name(self) -> str:
- """See `IExternalPackage`."""
- if self.channel:
- return "%s - %s @%s in %s" % (
- self.sourcepackagename.name,
- self.packagetype,
- self.display_channel,
- self.distribution.display_name,
- )
-
- return "%s - %s in %s" % (
- self.sourcepackagename.name,
- self.packagetype,
- self.distribution.display_name,
- )
-
- # There are different places of launchpad codebase where they use different
- # display names
- @property
- def displayname(self) -> str:
- """See `IExternalPackage`."""
- return self.display_name
-
- @property
- def bugtargetdisplayname(self) -> str:
- """See `IExternalPackage`."""
- return self.display_name
-
- @property
- def bugtargetname(self) -> str:
- """See `IExternalPackage`."""
- return self.display_name
-
- @property
- def title(self) -> str:
- """See `IExternalPackage`."""
- return self.display_name
-
- def __eq__(self, other: "ExternalPackage") -> str:
- """See `IExternalPackage`."""
- return (
- (IExternalPackage.providedBy(other))
- and (self.distribution.id == other.distribution.id)
- and (self.sourcepackagename.id == other.sourcepackagename.id)
- and (self.packagetype == other.packagetype)
- and (self.channel == other.channel)
- )
-
- def __hash__(self) -> int:
- """Return the combined attributes hash."""
- return hash(
- (
- self.distribution,
- self.sourcepackagename,
- self.packagetype,
- self.display_channel,
- )
- )
-
- @property
- def drivers(self) -> list:
- """See `IHasDrivers`."""
- return self.distribution.drivers
-
- @property
- def official_bug_tags(self) -> list:
- """See `IHasBugs`."""
- return self.distribution.official_bug_tags
-
- @property
- def pillar(self) -> IDistribution:
- """See `IBugTarget`."""
- return self.distribution
-
- def _getOfficialTagClause(self):
- """See `IBugTarget`."""
- return self.distribution._getOfficialTagClause()
diff --git a/lib/lp/registry/tests/test_distribution.py b/lib/lp/registry/tests/test_distribution.py
index 09bd077..c1fafdd 100644
--- a/lib/lp/registry/tests/test_distribution.py
+++ b/lib/lp/registry/tests/test_distribution.py
@@ -67,7 +67,6 @@ from lp.registry.interfaces.accesspolicy import (
)
from lp.registry.interfaces.distribution import IDistribution, IDistributionSet
from lp.registry.interfaces.distributionmirror import MirrorContent
-from lp.registry.interfaces.externalpackage import ExternalPackageType
from lp.registry.interfaces.oopsreferences import IHasOOPSReferences
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.series import SeriesStatus
@@ -380,35 +379,6 @@ class TestDistribution(TestCaseWithFactory):
distro.getDefaultSpecificationInformationType(),
)
- def test_getExternalPackage(self):
- # Test that we get the ExternalPackage that belongs to the distribution
- # with the proper attributes
- distro = self.factory.makeDistribution()
- sourcepackagename = self.factory.getOrMakeSourcePackageName(
- "my-package"
- )
- channel = ("22.04", "candidate", "staging")
- externalpackage = distro.getExternalPackage(
- name=sourcepackagename,
- packagetype=ExternalPackageType.ROCK,
- channel=channel,
- )
- self.assertEqual(externalpackage.distribution, distro)
- self.assertEqual(externalpackage.name, "my-package")
- self.assertEqual(externalpackage.packagetype, ExternalPackageType.ROCK)
- self.assertEqual(externalpackage.channel, channel)
-
- # We can have external packages without channel
- externalpackage = distro.getExternalPackage(
- name=sourcepackagename,
- packagetype=ExternalPackageType.SNAP,
- channel=None,
- )
- self.assertEqual(externalpackage.distribution, distro)
- self.assertEqual(externalpackage.name, "my-package")
- self.assertEqual(externalpackage.packagetype, ExternalPackageType.SNAP)
- self.assertEqual(externalpackage.channel, None)
-
def test_getOCIProject(self):
distro = self.factory.makeDistribution()
first_project = self.factory.makeOCIProject(pillar=distro)
diff --git a/lib/lp/registry/tests/test_externalpackage.py b/lib/lp/registry/tests/test_externalpackage.py
deleted file mode 100644
index 032d605..0000000
--- a/lib/lp/registry/tests/test_externalpackage.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# Copyright 2009-2025 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Tests for ExternalPackage."""
-
-from zope.security.proxy import removeSecurityProxy
-
-from lp.registry.interfaces.externalpackage import ExternalPackageType
-from lp.registry.model.externalpackage import ExternalPackage
-from lp.testing import TestCaseWithFactory
-from lp.testing.layers import DatabaseFunctionalLayer
-
-
-class TestExternalPackage(TestCaseWithFactory):
- layer = DatabaseFunctionalLayer
-
- def setUp(self):
- super().setUp()
-
- self.sourcepackagename = self.factory.getOrMakeSourcePackageName(
- "mypackage"
- )
- self.channel = "12.81/edge/myfix"
- self.distribution = self.factory.makeDistribution(name="mydistro")
-
- self.externalpackage = self.distribution.getExternalPackage(
- name=self.sourcepackagename,
- packagetype=ExternalPackageType.SNAP,
- channel=self.channel,
- )
- self.externalpackage_maven = self.distribution.getExternalPackage(
- name=self.sourcepackagename,
- packagetype=ExternalPackageType.MAVEN,
- channel=None,
- )
- self.externalpackage_copy = ExternalPackage(
- self.distribution,
- sourcepackagename=self.sourcepackagename,
- packagetype=ExternalPackageType.SNAP,
- channel=self.channel,
- )
-
- def test_repr(self):
- """Test __repr__ function"""
- self.assertEqual(
- "<ExternalPackage 'mypackage - Snap @12.81/edge/myfix in "
- "Mydistro'>",
- self.externalpackage.__repr__(),
- )
- self.assertEqual(
- "<ExternalPackage 'mypackage - Maven in Mydistro'>",
- self.externalpackage_maven.__repr__(),
- )
-
- def test_name(self):
- """Test name property"""
- self.assertEqual("mypackage", self.externalpackage.name)
- self.assertEqual("mypackage", self.externalpackage_maven.name)
-
- def test_display_channel(self):
- """Test display_channel property"""
- self.assertEqual(
- self.externalpackage.display_channel, "12.81/edge/myfix"
- )
- self.assertEqual(self.externalpackage_maven.display_channel, None)
-
- removeSecurityProxy(self.externalpackage).channel = (
- "12.81",
- "candidate",
- None,
- )
- self.assertEqual(
- "12.81/candidate", self.externalpackage.display_channel
- )
-
- def test_channel_fields(self):
- """Test channel fields when creating an ExternalPackage"""
- # Channel is not a str, tuple or list
- self.assertRaises(
- ValueError,
- ExternalPackage,
- self.distribution,
- self.sourcepackagename,
- ExternalPackageType.SNAP,
- {},
- )
- self.assertRaises(
- ValueError,
- ExternalPackage,
- self.distribution,
- self.sourcepackagename,
- ExternalPackageType.CHARM,
- 16,
- )
- # Channel risk is missing
- self.assertRaises(
- ValueError,
- ExternalPackage,
- self.distribution,
- self.sourcepackagename,
- ExternalPackageType.ROCK,
- "16",
- )
- # Branch name is also risk name
- self.assertRaises(
- ValueError,
- ExternalPackage,
- self.distribution,
- self.sourcepackagename,
- ExternalPackageType.ROCK,
- "16/stable/stable",
- )
- # Invalid risk name
- self.assertRaises(
- ValueError,
- ExternalPackage,
- self.distribution,
- self.sourcepackagename,
- ExternalPackageType.ROCK,
- "16/foo/bar",
- )
-
- def test_display_name(self):
- """Test display_name property without channel"""
- self.assertEqual(
- "mypackage - Maven in Mydistro",
- self.externalpackage_maven.display_name,
- )
-
- def test_display_name_with_channel(self):
- """Test display_name property with channel"""
- self.assertEqual(
- "mypackage - Snap @12.81/edge/myfix in Mydistro",
- self.externalpackage.display_name,
- )
-
- def test_compare(self):
- """Test __eq__ and __neq__"""
- self.assertEqual(self.externalpackage, self.externalpackage_copy)
- self.assertNotEqual(self.externalpackage, self.externalpackage_maven)
-
- def test_hash(self):
- """Test __hash__"""
- self.assertEqual(
- removeSecurityProxy(self.externalpackage).__hash__(),
- removeSecurityProxy(self.externalpackage_copy).__hash__(),
- )
- self.assertNotEqual(
- removeSecurityProxy(self.externalpackage).__hash__(),
- removeSecurityProxy(self.externalpackage_maven).__hash__(),
- )
-
- def test_pillar(self):
- """Test pillar property"""
- self.assertEqual(self.externalpackage.pillar, self.distribution)
-
- def test_official_bug_tags(self):
- """Test official_bug_tags property"""
- self.assertEqual(
- self.externalpackage.official_bug_tags,
- self.distribution.official_bug_tags,
- )
-
- def test__getOfficialTagClause(self):
- """Test _getOfficialTagClause"""
- self.assertEqual(
- self.distribution._getOfficialTagClause(),
- self.externalpackage._getOfficialTagClause(),
- )
-
- def test_drivers_are_distributions(self):
- """Drivers property returns the drivers for the distribution."""
- self.assertNotEqual([], self.distribution.drivers)
- self.assertEqual(
- self.externalpackage.drivers, self.distribution.drivers
- )
-
- def test_personHasDriverRights(self):
- """A distribution driver has driver permissions on an
- externalpackage."""
- driver = self.distribution.drivers[0]
- self.assertTrue(self.externalpackage.personHasDriverRights(driver))
diff --git a/lib/lp/services/channels.py b/lib/lp/services/channels.py
index 418cd02..2d36856 100644
--- a/lib/lp/services/channels.py
+++ b/lib/lp/services/channels.py
@@ -33,38 +33,29 @@ def channel_string_to_list(channel):
:raises ValueError: If the channel string is invalid.
"""
- if isinstance(channel, str):
- components = channel.split(CHANNEL_COMPONENTS_DELIMITER)
- else:
- components = channel
-
- # Only 1, 2, or 3 components are allowed
- if len(components) > 3:
- raise ValueError("Invalid channel name: %r" % channel)
-
- track = None
- risk = None
- branch = None
-
+ components = channel.split(CHANNEL_COMPONENTS_DELIMITER)
if len(components) == 3:
track, risk, branch = components
elif len(components) == 2:
+ # Identify risk to determine if this is track/risk or risk/branch.
if _is_risk(components[0]):
+ if _is_risk(components[1]):
+ raise ValueError(
+ "Branch name cannot match a risk name: %r" % channel
+ )
+ track = None
risk, branch = components
elif _is_risk(components[1]):
track, risk = components
+ branch = None
else:
raise ValueError("No valid risk provided: %r" % channel)
elif len(components) == 1:
+ track = None
risk = components[0]
-
- # Validate risk and branch names
- if not _is_risk(risk):
- raise ValueError("No valid risk provided: %r" % channel)
-
- if branch and _is_risk(branch):
- raise ValueError("Branch name cannot match a risk name: %r" % channel)
-
+ branch = None
+ else:
+ raise ValueError("Invalid channel name: %r" % channel)
return track, risk, branch
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 74ee801..d23c481 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -178,7 +178,6 @@ from lp.registry.interfaces.distroseriesdifferencecomment import (
IDistroSeriesDifferenceCommentSource,
)
from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
-from lp.registry.interfaces.externalpackage import ExternalPackageType
from lp.registry.interfaces.gpg import IGPGKeySet
from lp.registry.interfaces.mailinglist import (
IMailingListSet,
@@ -5586,28 +5585,6 @@ class LaunchpadObjectFactory(ObjectFactory):
)
return dsp
- def makeExternalPackage(
- self,
- sourcepackagename=None,
- packagetype=None,
- channel=None,
- distribution=None,
- ):
- if sourcepackagename is None or isinstance(sourcepackagename, str):
- sourcepackagename = self.getOrMakeSourcePackageName(
- sourcepackagename
- )
- if distribution is None:
- distribution = self.makeDistribution()
- if packagetype is None:
- packagetype = ExternalPackageType.SNAP
- if channel is None:
- channel = ("12.1", "stable", None)
-
- return distribution.getExternalPackage(
- sourcepackagename, packagetype, channel
- )
-
def makeEmailMessage(
self,
body=None,