def test_notify_from_person_override_with_unicode_names(self): # notify() takes an optional from_person to override the calculated # From: address in announcement emails. Non-ASCII real names should be # correctly encoded in the From heade. spr = self.factory.makeSourcePackageRelease() self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() from_person = self.factory.makePerson(email="*****@*****.**", displayname=u"Loïc Motörhead") notify(blamer, spr, [], [], archive, distroseries, pocket, action='accepted', announce_from_person=from_person) notifications = pop_notifications() self.assertEqual(2, len(notifications)) # The first notification is to the blamer, the second notification is # to the announce list, which is the one that gets the overridden # From: self.assertEqual( "=?utf-8?q?Lo=C3=AFc_Mot=C3=B6rhead?= <*****@*****.**>", notifications[1]["From"])
def test_notify_from_person_override(self): # notify() takes an optional from_person to override the calculated # From: address in announcement emails. spr = self.factory.makeSourcePackageRelease() self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() from_person = self.factory.makePerson(email="*****@*****.**", displayname="Lemmy Kilmister") notify(blamer, spr, [], [], archive, distroseries, pocket, action='accepted', announce_from_person=from_person) notifications = pop_notifications() self.assertEqual(2, len(notifications)) # The first notification is to the blamer, the second notification is # to the announce list, which is the one that gets the overridden # From: self.assertEqual("Lemmy Kilmister <*****@*****.**>", notifications[1]["From"])
def test_reject_with_no_changes(self): # If we don't have any files and no changes content, nothing happens. archive = self.factory.makeArchive() distroseries = self.factory.makeDistroSeries() pocket = self.factory.getAnyPocket() notify(None, None, (), (), archive, distroseries, pocket) notifications = pop_notifications() self.assertEqual(0, len(notifications))
def test_reject_with_no_changes(self): # If we don't have any files and no changes content, nothing happens. archive = self.factory.makeArchive() distroseries = self.factory.makeDistroSeries() pocket = self.factory.getAnyPocket() notify(None, None, (), (), archive, distroseries, pocket) notifications = pop_notifications() self.assertEqual(0, len(notifications))
def test_notify_bpr(self): # If we notify about an accepted bpr with no source, it is from a # build, and no notification is sent. bpr = self.factory.makeBinaryPackageRelease() archive = self.factory.makeArchive() pocket = self.factory.getAnyPocket() distroseries = self.factory.makeDistroSeries() person = self.factory.makePerson() notify( person, None, [bpr], [], archive, distroseries, pocket, action='accepted') notifications = pop_notifications() self.assertEqual(0, len(notifications))
def test_notify_bpr(self): # If we notify about an accepted bpr with no source, it is from a # build, and no notification is sent. bpr = self.factory.makeBinaryPackageRelease() archive = self.factory.makeArchive() pocket = self.factory.getAnyPocket() distroseries = self.factory.makeDistroSeries() person = self.factory.makePerson() notify(person, None, [bpr], [], archive, distroseries, pocket, action='accepted') notifications = pop_notifications() self.assertEqual(0, len(notifications))
def _setup_notification(self, from_person=None, distroseries=None, spr=None): if spr is None: spr = self.factory.makeSourcePackageRelease() self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE if distroseries is None: distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() if from_person is None: from_person = self.factory.makePerson() notify( blamer, spr, [], [], archive, distroseries, pocket, action='accepted', announce_from_person=from_person)
def test_notify_bpr_rejected(self): # If we notify about a rejected bpr with no source, a notification is # sent. bpr = self.factory.makeBinaryPackageRelease() changelog = self.factory.makeChangelog(spn="foo", versions=["1.1"]) removeSecurityProxy( bpr.build.source_package_release).changelog = changelog self.layer.txn.commit() person = self.factory.makePerson(name='archiver') archive = self.factory.makeArchive(owner=person, name='ppa') pocket = self.factory.getAnyPocket() distroseries = self.factory.makeDistroSeries() person = self.factory.makePerson() notify(person, None, [bpr], [], archive, distroseries, pocket, summary_text="Rejected by archive administrator.", action='rejected') [notification] = pop_notifications() body = notification.get_payload()[0].get_payload() self.assertEqual(person_to_email(person), notification['To']) expected_body = dedent("""\ Rejected: Rejected by archive administrator. foo (1.1) unstable; urgency=3Dlow * 1.1. =3D=3D=3D If you don't understand why your files were rejected please send an email to [email protected] for help (requires membership). -- http://launchpad.dev/~archiver/+archive/ppa You are receiving this email because you are the uploader of the above PPA package. """) self.assertEqual(expected_body, body)
def test_notify_from_unicode_names(self): # People with unicode in their names should appear correctly in the # email and not get smashed to ASCII or otherwise transliterated. RANDOM_UNICODE = u"Loïc" creator = self.factory.makePerson(displayname=RANDOM_UNICODE) spr = self.factory.makeSourcePackageRelease(creator=creator) self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() notify( blamer, spr, [], [], archive, distroseries, pocket, action='accepted') notifications = pop_notifications() self.assertEqual(2, len(notifications)) msg = notifications[1].get_payload(0) body = msg.get_payload(decode=True) self.assertIn("Loïc", body)
def _setup_notification(self, from_person=None, distroseries=None, spr=None): if spr is None: spr = self.factory.makeSourcePackageRelease() self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE if distroseries is None: distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() if from_person is None: from_person = self.factory.makePerson() notify(blamer, spr, [], [], archive, distroseries, pocket, action='accepted', announce_from_person=from_person)
def test_notify_bpr_rejected(self): # If we notify about a rejected bpr with no source, a notification is # sent. bpr = self.factory.makeBinaryPackageRelease() changelog = self.factory.makeChangelog(spn="foo", versions=["1.1"]) removeSecurityProxy( bpr.build.source_package_release).changelog = changelog self.layer.txn.commit() person = self.factory.makePerson(name='archiver') archive = self.factory.makeArchive(owner=person, name='ppa') pocket = self.factory.getAnyPocket() distroseries = self.factory.makeDistroSeries() person = self.factory.makePerson() notify( person, None, [bpr], [], archive, distroseries, pocket, summary_text="Rejected by archive administrator.", action='rejected') [notification] = pop_notifications() body = notification.get_payload()[0].get_payload() self.assertEqual(person_to_email(person), notification['To']) expected_body = dedent("""\ Rejected: Rejected by archive administrator. foo (1.1) unstable; urgency=3Dlow * 1.1. =3D=3D=3D If you don't understand why your files were rejected please send an email to [email protected] for help (requires membership). -- http://launchpad.dev/~archiver/+archive/ppa You are receiving this email because you are the uploader of the above PPA package. """) self.assertEqual(expected_body, body)
def test_notify_from_person_override(self): # notify() takes an optional from_person to override the calculated # From: address in announcement emails. spr = self.factory.makeSourcePackageRelease() self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() from_person = self.factory.makePerson( email="*****@*****.**", displayname="Lemmy Kilmister") notify( blamer, spr, [], [], archive, distroseries, pocket, action='accepted', announce_from_person=from_person) notifications = pop_notifications() self.assertEqual(2, len(notifications)) # The first notification is to the blamer, the second notification is # to the announce list, which is the one that gets the overridden # From: self.assertEqual( "Lemmy Kilmister <*****@*****.**>", notifications[1]["From"])
def test_notify_from_unicode_names(self): # People with unicode in their names should appear correctly in the # email and not get smashed to ASCII or otherwise transliterated. RANDOM_UNICODE = u"Loïc" creator = self.factory.makePerson(displayname=RANDOM_UNICODE) spr = self.factory.makeSourcePackageRelease(creator=creator) self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() notify(blamer, spr, [], [], archive, distroseries, pocket, action='accepted') notifications = pop_notifications() self.assertEqual(2, len(notifications)) msg = notifications[1].get_payload(0) body = msg.get_payload(decode=True) self.assertIn("Loïc", body)
def test_notify_from_person_override_with_unicode_names(self): # notify() takes an optional from_person to override the calculated # From: address in announcement emails. Non-ASCII real names should be # correctly encoded in the From heade. spr = self.factory.makeSourcePackageRelease() self.factory.makeSourcePackageReleaseFile(sourcepackagerelease=spr) archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY) pocket = PackagePublishingPocket.RELEASE distroseries = self.factory.makeDistroSeries() distroseries.changeslist = "*****@*****.**" blamer = self.factory.makePerson() from_person = self.factory.makePerson( email="*****@*****.**", displayname=u"Loïc Motörhead") notify( blamer, spr, [], [], archive, distroseries, pocket, action='accepted', announce_from_person=from_person) notifications = pop_notifications() self.assertEqual(2, len(notifications)) # The first notification is to the blamer, the second notification is # to the announce list, which is the one that gets the overridden # From: self.assertEqual( "=?utf-8?q?Lo=C3=AFc_Mot=C3=B6rhead?= <*****@*****.**>", notifications[1]["From"])
def do_copy(sources, archive, series, pocket, include_binaries=False, person=None, check_permissions=True, overrides=None, send_email=False, strict_binaries=True, close_bugs=True, create_dsd_job=True, announce_from_person=None, sponsored=None, packageupload=None, unembargo=False, phased_update_percentage=None, logger=None): """Perform the complete copy of the given sources incrementally. Verifies if each copy can be performed using `CopyChecker` and raises `CannotCopy` if one or more copies could not be performed. When `CannotCopy` is raised, call sites are responsible for rolling back the transaction. Otherwise, performed copies will be commited. Wrapper for `do_direct_copy`. :param sources: a list of `ISourcePackagePublishingHistory`. :param archive: the target `IArchive`. :param series: the target `IDistroSeries`, if None is given the same current source distroseries will be used as destination. :param pocket: the target `PackagePublishingPocket`. :param include_binaries: optional boolean, controls whether or not the published binaries for each given source should be also copied along with the source. :param person: the requester `IPerson`. :param check_permissions: boolean indicating whether or not the requester's permissions to copy should be checked. :param overrides: A list of `IOverride` as returned from one of the copy policies which will be used as a manual override insyead of using the default override returned by IArchive.getOverridePolicy(). There must be the same number of overrides as there are sources and each override must be for the corresponding source in the sources list. :param send_email: Should we notify for the copy performed? NOTE: If running in zopeless mode, the email is sent even if the transaction is later aborted. (See bug 29744) :param announce_from_person: If send_email is True, then send announcement emails with this person as the From: :param strict_binaries: If 'include_binaries' is True then setting this to True will make the copy fail if binaries cannot be also copied. :param close_bugs: A boolean indicating whether or not bugs on the copied publications should be closed. :param create_dsd_job: A boolean indicating whether or not a dsd job should be created for the new source publication. :param sponsored: An `IPerson` representing the person who is being sponsored for this copy. May be None, but if present will affect the "From:" address on notifications and the creator of the publishing record will be set to this person. :param packageupload: The `IPackageUpload` that caused this publication to be created. :param unembargo: If True, allow copying restricted files from a private archive to a public archive, and unrestrict their library files when doing so. :param phased_update_percentage: The phased update percentage to apply to the copied publication. :param logger: An optional logger. :raise CannotCopy when one or more copies were not allowed. The error will contain the reason why each copy was denied. :return: a list of `ISourcePackagePublishingHistory` and `BinaryPackagePublishingHistory` corresponding to the copied publications. """ copies = [] errors = [] copy_checker = CopyChecker(archive, include_binaries, strict_binaries=strict_binaries, unembargo=unembargo) for source in sources: if series is None: destination_series = source.distroseries else: destination_series = series try: copy_checker.checkCopy(source, destination_series, pocket, person, check_permissions) except CannotCopy as reason: errors.append("%s (%s)" % (source.displayname, reason)) continue if len(errors) != 0: error_text = "\n".join(errors) if send_email: source = sources[0] # Although the interface allows multiple sources to be copied # at once, we can only send rejection email if a single source # is specified for now. This is only relied on by packagecopyjob # which will only process one package at a time. We need to # make the notification code handle atomic rejections such that # it notifies about multiple packages. if series is None: series = source.distroseries # In zopeless mode this email will be sent immediately. notify(person, source.sourcepackagerelease, [], [], archive, series, pocket, summary_text=error_text, action='rejected') raise CannotCopy(error_text) overrides_index = 0 for source in copy_checker.getCheckedCopies(): if series is None: destination_series = source.distroseries else: destination_series = series override = None if overrides: override = overrides[overrides_index] # Make a note of the destination source's version for use in sending # the email notification and closing bugs. existing = archive.getPublishedSources( name=source.sourcepackagerelease.name, exact_match=True, status=active_publishing_status, distroseries=series, pocket=pocket).first() if existing: old_version = existing.sourcepackagerelease.version else: old_version = None if sponsored is not None: announce_from_person = sponsored creator = sponsored sponsor = person else: creator = person sponsor = None sub_copies = _do_direct_copy( source, archive, destination_series, pocket, include_binaries, override, close_bugs=close_bugs, create_dsd_job=create_dsd_job, close_bugs_since_version=old_version, creator=creator, sponsor=sponsor, packageupload=packageupload, phased_update_percentage=phased_update_percentage, logger=logger) if send_email: notify(person, source.sourcepackagerelease, [], [], archive, destination_series, pocket, action='accepted', announce_from_person=announce_from_person, previous_version=old_version) if not archive.private and has_restricted_files(source): # Fix copies by unrestricting files with privacy mismatch. # We must do this *after* calling notify (which only # actually sends mail on commit), because otherwise the new # changelog LFA won't be visible without a commit, which may # not be safe here. for pub_record in sub_copies: for changed_file in update_files_privacy(pub_record): if logger is not None: logger.info("Made %s public" % changed_file.filename) overrides_index += 1 copies.extend(sub_copies) return copies
def do_copy(sources, archive, series, pocket, include_binaries=False, person=None, check_permissions=True, overrides=None, send_email=False, strict_binaries=True, close_bugs=True, create_dsd_job=True, announce_from_person=None, sponsored=None, packageupload=None, unembargo=False, phased_update_percentage=None, logger=None): """Perform the complete copy of the given sources incrementally. Verifies if each copy can be performed using `CopyChecker` and raises `CannotCopy` if one or more copies could not be performed. When `CannotCopy` is raised, call sites are responsible for rolling back the transaction. Otherwise, performed copies will be commited. Wrapper for `do_direct_copy`. :param sources: a list of `ISourcePackagePublishingHistory`. :param archive: the target `IArchive`. :param series: the target `IDistroSeries`, if None is given the same current source distroseries will be used as destination. :param pocket: the target `PackagePublishingPocket`. :param include_binaries: optional boolean, controls whether or not the published binaries for each given source should be also copied along with the source. :param person: the requester `IPerson`. :param check_permissions: boolean indicating whether or not the requester's permissions to copy should be checked. :param overrides: A list of `IOverride` as returned from one of the copy policies which will be used as a manual override insyead of using the default override returned by IArchive.getOverridePolicy(). There must be the same number of overrides as there are sources and each override must be for the corresponding source in the sources list. :param send_email: Should we notify for the copy performed? NOTE: If running in zopeless mode, the email is sent even if the transaction is later aborted. (See bug 29744) :param announce_from_person: If send_email is True, then send announcement emails with this person as the From: :param strict_binaries: If 'include_binaries' is True then setting this to True will make the copy fail if binaries cannot be also copied. :param close_bugs: A boolean indicating whether or not bugs on the copied publications should be closed. :param create_dsd_job: A boolean indicating whether or not a dsd job should be created for the new source publication. :param sponsored: An `IPerson` representing the person who is being sponsored for this copy. May be None, but if present will affect the "From:" address on notifications and the creator of the publishing record will be set to this person. :param packageupload: The `IPackageUpload` that caused this publication to be created. :param unembargo: If True, allow copying restricted files from a private archive to a public archive, and unrestrict their library files when doing so. :param phased_update_percentage: The phased update percentage to apply to the copied publication. :param logger: An optional logger. :raise CannotCopy when one or more copies were not allowed. The error will contain the reason why each copy was denied. :return: a list of `ISourcePackagePublishingHistory` and `BinaryPackagePublishingHistory` corresponding to the copied publications. """ copies = [] errors = [] copy_checker = CopyChecker( archive, include_binaries, strict_binaries=strict_binaries, unembargo=unembargo) for source in sources: if series is None: destination_series = source.distroseries else: destination_series = series try: copy_checker.checkCopy( source, destination_series, pocket, person, check_permissions) except CannotCopy as reason: errors.append("%s (%s)" % (source.displayname, reason)) continue if len(errors) != 0: error_text = "\n".join(errors) if send_email: source = sources[0] # Although the interface allows multiple sources to be copied # at once, we can only send rejection email if a single source # is specified for now. This is only relied on by packagecopyjob # which will only process one package at a time. We need to # make the notification code handle atomic rejections such that # it notifies about multiple packages. if series is None: series = source.distroseries # In zopeless mode this email will be sent immediately. notify( person, source.sourcepackagerelease, [], [], archive, series, pocket, summary_text=error_text, action='rejected') raise CannotCopy(error_text) overrides_index = 0 for source in copy_checker.getCheckedCopies(): if series is None: destination_series = source.distroseries else: destination_series = series override = None if overrides: override = overrides[overrides_index] # Make a note of the destination source's version for use in sending # the email notification and closing bugs. existing = archive.getPublishedSources( name=source.sourcepackagerelease.name, exact_match=True, status=active_publishing_status, distroseries=series, pocket=pocket).first() if existing: old_version = existing.sourcepackagerelease.version else: old_version = None if sponsored is not None: announce_from_person = sponsored creator = sponsored sponsor = person else: creator = person sponsor = None sub_copies = _do_direct_copy( source, archive, destination_series, pocket, include_binaries, override, close_bugs=close_bugs, create_dsd_job=create_dsd_job, close_bugs_since_version=old_version, creator=creator, sponsor=sponsor, packageupload=packageupload, phased_update_percentage=phased_update_percentage, logger=logger) if send_email: notify( person, source.sourcepackagerelease, [], [], archive, destination_series, pocket, action='accepted', announce_from_person=announce_from_person, previous_version=old_version) if not archive.private and has_restricted_files(source): # Fix copies by unrestricting files with privacy mismatch. # We must do this *after* calling notify (which only # actually sends mail on commit), because otherwise the new # changelog LFA won't be visible without a commit, which may # not be safe here. for pub_record in sub_copies: for changed_file in update_files_privacy(pub_record): if logger is not None: logger.info("Made %s public" % changed_file.filename) overrides_index += 1 copies.extend(sub_copies) return copies