launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #17331
[Merge] lp:~wgrant/launchpad/copyPackage-no-send-email into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/copyPackage-no-send-email into lp:launchpad.
Requested reviews:
William Grant (wgrant): code
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/copyPackage-no-send-email/+merge/231141
--
https://code.launchpad.net/~wgrant/launchpad/copyPackage-no-send-email/+merge/231141
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2014-08-06 16:10:18 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2014-08-18 02:18:13 +0000
@@ -1371,6 +1371,12 @@
description=_("Automatically approve this copy (queue admins "
"only)."),
required=False),
+ silent=Bool(
+ title=_("Silent"),
+ description=_(
+ "Don't notify anyone about this copy. For use by queue "
+ "admins only."),
+ required=False),
from_pocket=TextLine(title=_("Source pocket name"), required=False),
from_series=TextLine(
title=_("Source distroseries name"), required=False),
@@ -1386,7 +1392,7 @@
def copyPackage(source_name, version, from_archive, to_pocket,
person, to_series=None, include_binaries=False,
sponsored=None, unembargo=False, auto_approve=False,
- from_pocket=None, from_series=None,
+ silent=False, from_pocket=None, from_series=None,
phased_update_percentage=None):
"""Copy a single named source into this archive.
@@ -1420,6 +1426,8 @@
:param auto_approve: if True and the `IPerson` requesting the sync
has queue admin permissions on the target, accept the copy
immediately rather than setting it to unapproved.
+ :param silent: Suppress any emails that the copy would generate.
+ Only usable with queue admin permissions on the target.
:param from_pocket: the source pocket (as a string). If omitted,
copy from any pocket with a matching version.
:param from_series: the source distroseries (as a string). If
@@ -1464,12 +1472,19 @@
description=_("Automatically approve this copy (queue admins "
"only)."),
required=False),
+ silent=Bool(
+ title=_("Silent"),
+ description=_(
+ "Don't notify anyone about this copy. For use by queue "
+ "admins only."),
+ required=False),
)
@export_write_operation()
@operation_for_version('devel')
def copyPackages(source_names, from_archive, to_pocket, person,
to_series=None, from_series=None, include_binaries=False,
- sponsored=None, unembargo=False, auto_approve=False):
+ sponsored=None, unembargo=False, auto_approve=False,
+ silent=False):
"""Copy multiple named sources into this archive from another.
Asynchronously copy the most recent PUBLISHED versions of the named
@@ -1505,6 +1520,8 @@
:param auto_approve: if True and the `IPerson` requesting the sync
has queue admin permissions on the target, accept the copies
immediately rather than setting it to unapproved.
+ :param silent: Suppress any emails that the copy would generate.
+ Only usable with queue admin permissions on the target.
:raises NoSuchSourcePackageName: if the source name is invalid
:raises PocketNotFound: if the pocket name is invalid
=== modified file 'lib/lp/soyuz/interfaces/packagecopyjob.py'
--- lib/lp/soyuz/interfaces/packagecopyjob.py 2013-06-21 09:36:02 +0000
+++ lib/lp/soyuz/interfaces/packagecopyjob.py 2014-08-18 02:18:13 +0000
@@ -129,7 +129,7 @@
include_binaries=False, package_version=None,
copy_policy=PackageCopyPolicy.INSECURE, requester=None,
sponsored=None, unembargo=False, auto_approve=False,
- source_distroseries=None, source_pocket=None,
+ silent=False, source_distroseries=None, source_pocket=None,
phased_update_percentage=None):
"""Create a new `IPlainPackageCopyJob`.
@@ -152,6 +152,8 @@
:param auto_approve: if True and the user requesting the sync has
queue admin permissions on the target, accept the copy
immediately rather than setting it to unapproved.
+ :param silent: Suppress any emails that the copy would generate.
+ Only usable with queue admin permissions on the target.
:param source_distroseries: The `IDistroSeries` from which to copy
the packages. If omitted, copy from any series with a matching
version.
@@ -165,7 +167,7 @@
def createMultiple(target_distroseries, copy_tasks, requester,
copy_policy=PackageCopyPolicy.INSECURE,
include_binaries=False, unembargo=False,
- auto_approve=False):
+ auto_approve=False, silent=False):
"""Create multiple new `IPlainPackageCopyJob`s at once.
:param target_distroseries: The `IDistroSeries` to which to copy the
@@ -177,10 +179,12 @@
:param copy_policy: Applicable `PackageCopyPolicy`.
:param include_binaries: As in `do_copy`.
:param unembargo: As in `do_copy`.
+ :param auto_approve: if True and the user requesting the sync has
+ queue admin permissions on the target, accept the copy
+ immediately rather than setting it to unapproved.
+ :param silent: Suppress any emails that the copy would generate.
+ Only usable with queue admin permissions on the target.
:return: An iterable of `PackageCopyJob` ids.
- :param auto_approve: if True and the user requesting the sync has
- queue admin permissions on the target, accept the copy
- immediately rather than setting it to unapproved.
"""
def getActiveJobs(target_archive):
@@ -237,6 +241,8 @@
title=_("Automatic approval"),
required=False, readonly=True)
+ silent = Bool(title=_("Silent"), required=False, readonly=True)
+
source_distroseries = Reference(
schema=IDistroSeries, title=_('Source DistroSeries.'),
required=False, readonly=True)
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2014-08-09 19:31:03 +0000
+++ lib/lp/soyuz/model/archive.py 2014-08-18 02:18:13 +0000
@@ -1718,7 +1718,7 @@
def copyPackage(self, source_name, version, from_archive, to_pocket,
person, to_series=None, include_binaries=False,
sponsored=None, unembargo=False, auto_approve=False,
- from_pocket=None, from_series=None,
+ silent=False, from_pocket=None, from_series=None,
phased_update_percentage=None):
"""See `IArchive`."""
# Asynchronously copy a package using the job system.
@@ -1754,14 +1754,14 @@
package_version=version, include_binaries=include_binaries,
copy_policy=PackageCopyPolicy.INSECURE, requester=person,
sponsored=sponsored, unembargo=unembargo,
- auto_approve=auto_approve, source_distroseries=from_series,
- source_pocket=from_pocket,
+ auto_approve=auto_approve, silent=silent,
+ source_distroseries=from_series, source_pocket=from_pocket,
phased_update_percentage=phased_update_percentage)
def copyPackages(self, source_names, from_archive, to_pocket,
person, to_series=None, from_series=None,
include_binaries=None, sponsored=None, unembargo=False,
- auto_approve=False):
+ auto_approve=False, silent=False):
"""See `IArchive`."""
from lp.soyuz.scripts.packagecopier import check_copy_permissions
sources = self._collectLatestPublishedSources(
@@ -1789,7 +1789,7 @@
job_source.createMultiple(
copy_tasks, person, copy_policy=PackageCopyPolicy.MASS_SYNC,
include_binaries=include_binaries, sponsored=sponsored,
- unembargo=unembargo, auto_approve=auto_approve)
+ unembargo=unembargo, auto_approve=auto_approve, silent=silent)
def _collectLatestPublishedSources(self, from_archive, from_series,
source_names):
=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
--- lib/lp/soyuz/model/packagecopyjob.py 2014-07-23 13:25:14 +0000
+++ lib/lp/soyuz/model/packagecopyjob.py 2014-08-18 02:18:13 +0000
@@ -275,8 +275,9 @@
@classmethod
def _makeMetadata(cls, target_pocket, package_version,
include_binaries, sponsored=None, unembargo=False,
- auto_approve=False, source_distroseries=None,
- source_pocket=None, phased_update_percentage=None):
+ auto_approve=False, silent=False,
+ source_distroseries=None, source_pocket=None,
+ phased_update_percentage=None):
"""Produce a metadata dict for this job."""
return {
'target_pocket': target_pocket.value,
@@ -285,6 +286,7 @@
'sponsored': sponsored.name if sponsored else None,
'unembargo': unembargo,
'auto_approve': auto_approve,
+ 'silent': silent,
'source_distroseries':
source_distroseries.name if source_distroseries else None,
'source_pocket': source_pocket.value if source_pocket else None,
@@ -297,15 +299,15 @@
include_binaries=False, package_version=None,
copy_policy=PackageCopyPolicy.INSECURE, requester=None,
sponsored=None, unembargo=False, auto_approve=False,
- source_distroseries=None, source_pocket=None,
+ silent=False, source_distroseries=None, source_pocket=None,
phased_update_percentage=None):
"""See `IPlainPackageCopyJobSource`."""
assert package_version is not None, "No package version specified."
assert requester is not None, "No requester specified."
metadata = cls._makeMetadata(
target_pocket, package_version, include_binaries, sponsored,
- unembargo, auto_approve, source_distroseries, source_pocket,
- phased_update_percentage)
+ unembargo, auto_approve, silent, source_distroseries,
+ source_pocket, phased_update_percentage)
job = PackageCopyJob(
job_type=cls.class_job_type,
source_archive=source_archive,
@@ -323,7 +325,7 @@
@classmethod
def _composeJobInsertionTuple(cls, copy_policy, include_binaries, job_id,
copy_task, sponsored, unembargo,
- auto_approve):
+ auto_approve, silent):
"""Create an SQL fragment for inserting a job into the database.
:return: A string representing an SQL tuple containing initializers
@@ -340,7 +342,7 @@
) = copy_task
metadata = cls._makeMetadata(
target_pocket, package_version, include_binaries, sponsored,
- unembargo, auto_approve)
+ unembargo, auto_approve, silent)
data = (
cls.class_job_type, target_distroseries, copy_policy,
source_archive, target_archive, package_name, job_id,
@@ -351,14 +353,14 @@
def createMultiple(cls, copy_tasks, requester,
copy_policy=PackageCopyPolicy.INSECURE,
include_binaries=False, sponsored=None,
- unembargo=False, auto_approve=False):
+ unembargo=False, auto_approve=False, silent=False):
"""See `IPlainPackageCopyJobSource`."""
store = IMasterStore(Job)
job_ids = Job.createMultiple(store, len(copy_tasks), requester)
job_contents = [
cls._composeJobInsertionTuple(
copy_policy, include_binaries, job_id, task, sponsored,
- unembargo, auto_approve)
+ unembargo, auto_approve, silent)
for job_id, task in zip(job_ids, copy_tasks)]
return bulk.create(
(PackageCopyJob.job_type, PackageCopyJob.target_distroseries,
@@ -445,6 +447,10 @@
return self.metadata.get('auto_approve', False)
@property
+ def silent(self):
+ return self.metadata.get('silent', False)
+
+ @property
def source_distroseries(self):
name = self.metadata.get('source_distroseries')
if name is None:
@@ -462,6 +468,12 @@
def phased_update_percentage(self):
return self.metadata.get('phased_update_percentage')
+ @property
+ def requester_can_admin_target(self):
+ return self.target_archive.canAdministerQueue(
+ self.requester, self.getSourceOverride().component,
+ self.target_pocket, self.target_distroseries)
+
def _createPackageUpload(self, unapproved=False):
pu = self.target_distroseries.createQueueEntry(
pocket=self.target_pocket, archive=self.target_archive,
@@ -524,10 +536,7 @@
# Put the (existing or default) override in the metadata.
self.addSourceOverride(override)
- if auto_approve:
- auto_approve = self.target_archive.canAdministerQueue(
- self.requester, self.getSourceOverride().component,
- self.target_pocket, self.target_distroseries)
+ auto_approve = auto_approve and self.requester_can_admin_target
if override.new:
approve_new = auto_approve or copy_policy.autoApproveNew(
@@ -617,6 +626,10 @@
# Wrap any forbidden-pocket error in CannotCopy.
raise CannotCopy(unicode(reason))
+ if self.silent and not self.requester_can_admin_target:
+ raise CannotCopy(
+ "Silent copies need queue admin privileges on the target.")
+
source_package = self.findSourcePublication()
# If there's a PackageUpload associated with this job then this
@@ -637,7 +650,8 @@
pocket=self.target_pocket, exact_match=True)
override = self.getSourceOverride()
copy_policy = self.getPolicyImplementation()
- send_email = copy_policy.send_email(self.target_archive)
+ send_email = (
+ copy_policy.send_email(self.target_archive) and not self.silent)
copied_publications = do_copy(
sources=[source_package], archive=self.target_archive,
series=self.target_distroseries, pocket=self.target_pocket,
=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
--- lib/lp/soyuz/tests/test_packagecopyjob.py 2014-07-29 07:29:32 +0000
+++ lib/lp/soyuz/tests/test_packagecopyjob.py 2014-08-18 02:18:13 +0000
@@ -12,6 +12,7 @@
MatchesRegex,
MatchesStructure,
)
+from testtools.testcase import ExpectedException
import transaction
from zope.component import getUtility
from zope.security.interfaces import Unauthorized
@@ -65,7 +66,10 @@
TestCaseWithFactory,
verifyObject,
)
-from lp.testing.dbuser import switch_dbuser
+from lp.testing.dbuser import (
+ dbuser,
+ switch_dbuser,
+ )
from lp.testing.fakemethod import FakeMethod
from lp.testing.layers import (
CeleryJobLayer,
@@ -84,7 +88,7 @@
DistroSeriesDifferenceComment.distro_series_difference == dsd)
-def create_proper_job(factory):
+def create_proper_job(factory, silent=False):
"""Create a job that will complete successfully."""
publisher = SoyuzTestPublisher()
with admin_logged_in():
@@ -123,7 +127,7 @@
target_distroseries=target_series,
target_pocket=PackagePublishingPocket.RELEASE,
package_version="2.8-1", include_binaries=False,
- requester=requester)
+ requester=requester, silent=silent)
class LocalTestHelper:
@@ -1233,6 +1237,25 @@
self.assertEqual(
"Nancy Requester <requester@xxxxxxxxxxx>", emails[1]['From'])
+ def test_silent(self):
+ # Copies into a non-PPA archive normally send emails. They can
+ # be suppressed by queue admin with the 'silent' flag.
+ job = create_proper_job(self.factory, silent=True)
+ self.assertEqual("libc", job.package_name)
+ self.assertEqual("2.8-1", job.package_version)
+
+ # An unprivileged requester can't copy silently.
+ with dbuser(self.dbuser):
+ with ExpectedException(
+ CannotCopy, "Silent copies need queue admin.*"):
+ removeSecurityProxy(job).attemptCopy()
+
+ # But if we give them queue admin privileges it works fine.
+ job.target_archive.newQueueAdmin(job.requester, "main")
+ with dbuser(self.dbuser):
+ removeSecurityProxy(job).attemptCopy()
+ self.assertEqual(0, len(pop_notifications()))
+
def test_copying_closes_bugs(self):
# Copying a package into a primary archive should close any bugs
# mentioned in its changelog for versions added since the most
References