launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24448
[Merge] ~pappacena/launchpad:change-override-log into launchpad:master
Thiago F. Pappacena has proposed merging ~pappacena/launchpad:change-override-log into launchpad:master.
Commit message:
Saving and showing the user that requested to override a package publishing.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #885739 in Launchpad itself: "queue and override manipulations should have an audit trail"
https://bugs.launchpad.net/launchpad/+bug/885739
For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/380252
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:change-override-log into launchpad:master.
diff --git a/lib/lp/soyuz/browser/publishing.py b/lib/lp/soyuz/browser/publishing.py
index 1a23bc3..15a2367 100644
--- a/lib/lp/soyuz/browser/publishing.py
+++ b/lib/lp/soyuz/browser/publishing.py
@@ -134,6 +134,9 @@ class BasePublishingRecordView(LaunchpadView):
accessor = attrgetter(self.timestamp_map[self.context.status])
return accessor(self.context)
+ def isPending(self):
+ return self.context.status == PackagePublishingStatus.PENDING
+
def wasDeleted(self):
"""Whether or not a publishing record deletion was requested.
diff --git a/lib/lp/soyuz/interfaces/publishing.py b/lib/lp/soyuz/interfaces/publishing.py
index 02c3615..4697209 100644
--- a/lib/lp/soyuz/interfaces/publishing.py
+++ b/lib/lp/soyuz/interfaces/publishing.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2019 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Publishing interfaces."""
@@ -561,8 +561,9 @@ class ISourcePackagePublishingHistoryEdit(IPublishingEdit):
new_component=TextLine(title=u"The new component name."),
new_section=TextLine(title=u"The new section name."))
@export_write_operation()
+ @call_with(creator=REQUEST_USER)
@operation_for_version("devel")
- def changeOverride(new_component=None, new_section=None):
+ def changeOverride(new_component=None, new_section=None, creator=None):
"""Change the component and/or section of this publication.
It is changed only if the argument is not None.
@@ -663,6 +664,13 @@ class IBinaryPackagePublishingHistoryPublic(IPublishingView):
title=_('The build which superseded this one'),
required=False, readonly=False,
)
+ creator = exported(
+ Reference(
+ IPerson,
+ title=_('Publication Creator'),
+ description=_('The IPerson who created this publication.'),
+ required=False, readonly=True
+ ))
datecreated = exported(
Datetime(
title=_('Date Created'),
@@ -840,9 +848,11 @@ class IBinaryPackagePublishingHistoryEdit(IPublishingEdit):
new_phased_update_percentage=Int(
title=u"The new phased update percentage."))
@export_write_operation()
+ @call_with(creator=REQUEST_USER)
@operation_for_version("devel")
def changeOverride(new_component=None, new_section=None,
- new_priority=None, new_phased_update_percentage=None):
+ new_priority=None, new_phased_update_percentage=None,
+ creator=None):
"""Change the component/section/priority/phase of this publication.
It is changed only if the argument is not None.
diff --git a/lib/lp/soyuz/model/publishing.py b/lib/lp/soyuz/model/publishing.py
index 3e73e98..9fb502e 100644
--- a/lib/lp/soyuz/model/publishing.py
+++ b/lib/lp/soyuz/model/publishing.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2019 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -49,7 +49,10 @@ from zope.security.proxy import (
from lp.app.errors import NotFoundError
from lp.buildmaster.enums import BuildStatus
-from lp.registry.interfaces.person import validate_public_person
+from lp.registry.interfaces.person import (
+ IPerson,
+ validate_public_person,
+ )
from lp.registry.interfaces.pocket import PackagePublishingPocket
from lp.services.database import bulk
from lp.services.database.constants import UTC_NOW
@@ -471,7 +474,8 @@ class SourcePackagePublishingHistory(SQLBase, ArchivePublisherBase):
self.supersededby = dominant.sourcepackagerelease
- def changeOverride(self, new_component=None, new_section=None):
+ def changeOverride(self, new_component=None, new_section=None,
+ creator=None):
"""See `ISourcePackagePublishingHistory`."""
# Check we have been asked to do something
if (new_component is None and
@@ -516,6 +520,7 @@ class SourcePackagePublishingHistory(SQLBase, ArchivePublisherBase):
pocket=self.pocket,
component=new_component,
section=new_section,
+ creator=creator,
archive=self.archive)
def copyTo(self, distroseries, pocket, archive, override=None,
@@ -629,6 +634,9 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
phased_update_percentage = IntCol(
dbName='phased_update_percentage', notNull=False, default=None)
scheduleddeletiondate = UtcDateTimeCol(default=None)
+ creator = ForeignKey(
+ dbName='creator', foreignKey='Person',
+ storm_validator=validate_public_person, notNull=False, default=None)
datepublished = UtcDateTimeCol(default=None)
datecreated = UtcDateTimeCol(default=UTC_NOW)
datesuperseded = UtcDateTimeCol(default=None)
@@ -794,7 +802,8 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
dominated.supersede(dominant, logger)
def changeOverride(self, new_component=None, new_section=None,
- new_priority=None, new_phased_update_percentage=None):
+ new_priority=None, new_phased_update_percentage=None,
+ creator=None):
"""See `IBinaryPackagePublishingHistory`."""
# Check we have been asked to do something
@@ -873,6 +882,7 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
component=new_component,
section=new_section,
priority=new_priority,
+ creator=creator,
archive=debug.archive,
phased_update_percentage=new_phased_update_percentage)
@@ -888,6 +898,7 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
section=new_section,
priority=new_priority,
archive=self.archive,
+ creator=creator,
phased_update_percentage=new_phased_update_percentage)
def copyTo(self, distroseries, pocket, archive):
diff --git a/lib/lp/soyuz/stories/soyuz/xx-packagepublishinghistory.txt b/lib/lp/soyuz/stories/soyuz/xx-packagepublishinghistory.txt
index 31e98b7..f2377f7 100644
--- a/lib/lp/soyuz/stories/soyuz/xx-packagepublishinghistory.txt
+++ b/lib/lp/soyuz/stories/soyuz/xx-packagepublishinghistory.txt
@@ -27,6 +27,29 @@ shows the complete history of a package in all series.
>>> print(table.findAll("tr")[2].td["colspan"])
8
+A change-override request should show who made the request
+
+ >>> from lp.registry.interfaces.person import IPersonSet
+ >>> from zope.component import getUtility
+
+ >>> login('foo.bar@xxxxxxxxxxxxx')
+ >>> person = getUtility(IPersonSet).getByEmail('foo.bar@xxxxxxxxxxxxx')
+ >>> new_pub = source_pub.changeOverride(
+ ... new_component='universe', creator=person)
+ >>> logout()
+
+ >>> anon_browser.open(
+ ... 'http://launchpad.test/ubuntutest/+source/test-history/'
+ ... '+publishinghistory')
+ >>> table = find_tag_by_id(anon_browser.contents, 'publishing-summary')
+ >>> print(extract_text(table))
+ Date Status Target Pocket Component Section Version
+ ... UTC Pending Breezy ... release universe base 666
+ Created ... ago by Foo Bar
+ ... UTC Published Breezy ... release main base 666
+ Published ... ago
+
+
A publishing record will be shown as deleted in the publishing history after a
request for deletion by a user.
@@ -42,6 +65,8 @@ request for deletion by a user.
>>> table = find_tag_by_id(anon_browser.contents, 'publishing-summary')
>>> print(extract_text(table))
Date Status Target Pocket Component Section Version
+ ... UTC Pending Breezy ... release universe base 666
+ Created ... ago by Foo Bar
Deleted Breezy ... release main base 666
Deleted ... ago by ... fix bug 1
Published ... ago
diff --git a/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt b/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt
index 81482f2..722c4c0 100644
--- a/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt
+++ b/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt
@@ -71,6 +71,7 @@ Each binary publication exposes a number of properties:
binary_package_version: u'1.0'
build_link: u'http://.../~cprov/+archive/ubuntu/ppa/+build/28'
component_name: u'main'
+ creator_link: None
date_created: u'2007-08-10T13:00:00+00:00'
date_made_pending: None
date_published: u'2007-08-10T13:00:01+00:00'
diff --git a/lib/lp/soyuz/templates/packagepublishing-details.pt b/lib/lp/soyuz/templates/packagepublishing-details.pt
index 0b0102c..77132e3 100644
--- a/lib/lp/soyuz/templates/packagepublishing-details.pt
+++ b/lib/lp/soyuz/templates/packagepublishing-details.pt
@@ -4,6 +4,14 @@
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
omit-tag="">
<ul>
+ <li tal:condition="python: context.creator
+ and not view.wasCopied()
+ and view.isPending()">
+ Created
+ <span tal:attributes="title context/datecreated/fmt:datetime"
+ tal:content="context/datecreated/fmt:displaydate" />
+ by <a tal:replace="structure context/creator/fmt:link"/>
+ </li>
<li tal:condition="view/isRemoved">
Removed from disk
<span tal:attributes="title context/dateremoved/fmt:datetime"
diff --git a/lib/lp/soyuz/tests/test_publishing.py b/lib/lp/soyuz/tests/test_publishing.py
index c29028e..ef59d06 100644
--- a/lib/lp/soyuz/tests/test_publishing.py
+++ b/lib/lp/soyuz/tests/test_publishing.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2019 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Test native publication workflow for Soyuz. """
@@ -62,6 +62,9 @@ from lp.soyuz.model.publishing import (
SourcePackagePublishingHistory,
)
from lp.testing import (
+ login,
+ person_logged_in,
+ record_two_runs,
StormStatementRecorder,
TestCaseWithFactory,
)
@@ -72,10 +75,12 @@ from lp.testing.dbuser import (
from lp.testing.factory import LaunchpadObjectFactory
from lp.testing.layers import (
DatabaseFunctionalLayer,
+ LaunchpadFunctionalLayer,
LaunchpadZopelessLayer,
ZopelessDatabaseLayer,
)
from lp.testing.matchers import HasQueryCount
+from lp.testing.sampledata import ADMIN_EMAIL
class SoyuzTestPublisher:
@@ -997,13 +1002,15 @@ class TestPublishingSetLite(TestCaseWithFactory):
debug_non_match_bpph.status, PackagePublishingStatus.PENDING)
def test_changeOverride_also_overrides_debug_package(self):
+ user = self.factory.makeAdministrator()
bpph, debug_bpph = self.factory.makeBinaryPackagePublishingHistory(
pocket=PackagePublishingPocket.RELEASE, with_debug=True)
new_section = self.factory.makeSection()
- new_bpph = bpph.changeOverride(new_section=new_section)
+ new_bpph = bpph.changeOverride(new_section=new_section, creator=user)
publishing_set = getUtility(IPublishingSet)
[new_debug_bpph] = publishing_set.findCorrespondingDDEBPublications(
[new_bpph])
+ self.assertEqual(new_debug_bpph.creator, user)
self.assertEqual(new_debug_bpph.section, new_section)
def test_requestDeletion_forbids_debug_package(self):
@@ -1524,6 +1531,21 @@ class TestChangeOverride(TestNativePublishingBase):
binary=True, new_component="universe", new_section="misc",
new_priority="extra", new_phased_update_percentage=90)
+ def test_change_binary_logged_in_user(self):
+ person = self.factory.makePerson()
+ new_pub = self.assertCanOverride(
+ binary=True, new_component="universe", new_section="misc",
+ new_priority="extra", new_phased_update_percentage=90,
+ creator=person)
+ self.assertEqual(person, new_pub.creator)
+
+ def test_change_source_logged_in_user(self):
+ person = self.factory.makePerson()
+ new_pub = self.assertCanOverride(
+ binary=False, new_component="universe", new_section="misc",
+ creator=person)
+ self.assertEqual(person, new_pub.creator)
+
def test_set_and_clear_phased_update_percentage(self):
# new_phased_update_percentage=<integer> sets a phased update
# percentage; new_phased_update_percentage=100 clears it.
@@ -1572,3 +1594,45 @@ class TestChangeOverride(TestNativePublishingBase):
# archive.
self.assertCannotOverride(new_component="partner")
self.assertCannotOverride(binary=True, new_component="partner")
+
+
+class TestPublishingHistoryView(TestCaseWithFactory):
+ layer = LaunchpadFunctionalLayer
+
+ def test_constant_query_counts_on_publishing_history_change_override(self):
+ admin = self.factory.makeAdministrator()
+ with person_logged_in(admin):
+ test_publisher = SoyuzTestPublisher()
+ test_publisher.prepareBreezyAutotest()
+
+ source_pub = test_publisher.getPubSource(
+ "test-history", status=PackagePublishingStatus.PUBLISHED)
+ url = ("http://launchpad.test/ubuntutest/+source/test-history"
+ "/+publishinghistory")
+
+ def insert_more_publish_history():
+ person1 = self.factory.makeAdministrator()
+ new_component = (
+ 'universe' if source_pub.component.name == 'main'
+ else 'main')
+ source_pub.changeOverride(
+ new_component=new_component, creator=person1)
+
+ person2 = self.factory.makeAdministrator()
+ new_section = ('web' if source_pub.section.name == 'base'
+ else 'base')
+ source_pub.changeOverride(
+ new_section=new_section, creator=person2)
+
+ def show_page():
+ self.getUserBrowser(url, admin)
+
+ # Make sure to have all the history fitting in one page.
+ self.pushConfig("launchpad", default_batch_size=50)
+
+ recorder1, recorder2 = record_two_runs(
+ show_page, insert_more_publish_history,
+ 1, 10, login_method=lambda: login(ADMIN_EMAIL))
+
+ self.assertThat(recorder1, HasQueryCount(Equals(26)))
+ self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 6a65466..91b7428 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -3989,7 +3989,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
sourcepackagename=None,
version=None,
architecturespecific=False,
- with_debug=False, with_file=False):
+ with_debug=False, with_file=False,
+ creator=None):
"""Make a `BinaryPackagePublishingHistory`."""
if distroarchseries is None:
if archive is None:
@@ -4082,7 +4083,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
binpackageformat=BinaryPackageFormat.DDEB,
sourcepackagename=sourcepackagename,
architecturespecific=architecturespecific,
- with_file=with_file)
+ with_file=with_file,
+ creator=creator)
removeSecurityProxy(bpph.binarypackagerelease).debug_package = (
debug_bpph.binarypackagerelease)
return bpphs[0], debug_bpph