Exemplo n.º 1
0
 def getErrorRecipients(self):
     # If something goes wrong we want to let the requestor know as well
     # as the pillar maintainer (if there is a pillar).
     result = set()
     result.add(format_address_for_person(self.requestor))
     for bug in self.bugs:
         for pillar in bug.affected_pillars:
             if pillar.owner.preferredemail:
                 result.add(format_address_for_person(pillar.owner))
     return list(result)
Exemplo n.º 2
0
 def getErrorRecipients(self):
     # If something goes wrong we want to let the requestor know as well
     # as the pillar maintainer (if there is a pillar).
     result = set()
     result.add(format_address_for_person(self.requestor))
     for bug in self.bugs:
         for pillar in bug.affected_pillars:
             if pillar.owner.preferredemail:
                 result.add(format_address_for_person(pillar.owner))
     return list(result)
Exemplo n.º 3
0
 def send(self):
     """Send a message to the user about the product's licence."""
     if not self.needs_notification(self.product):
         # The project has a common licence.
         return False
     maintainer = self.product.owner
     if maintainer.is_team:
         user_address = maintainer.getTeamAdminsEmailAddresses()
     else:
         user_address = format_address_for_person(maintainer)
     from_address = format_address("Launchpad",
                                   config.canonical.noreply_from_address)
     commercial_address = format_address('Commercial',
                                         '*****@*****.**')
     substitutions = dict(
         user_displayname=maintainer.displayname,
         user_name=maintainer.name,
         product_name=self.product.name,
         product_url=canonical_url(self.product),
         commercial_use_expiration=self.getCommercialUseMessage(),
     )
     # Email the user about licence policy.
     subject = ("Licence information for %(product_name)s "
                "in Launchpad" % substitutions)
     template = get_email_template(self.getTemplateName(), app='registry')
     message = template % substitutions
     simple_sendmail(from_address,
                     user_address,
                     subject,
                     message,
                     headers={'Reply-To': commercial_address})
     # Inform that Launchpad recognized the licence change.
     self._addLicenseChangeToReviewWhiteboard()
     return True
Exemplo n.º 4
0
 def test_fetch_information_spr(self):
     creator = self.factory.makePerson(displayname=u"foø")
     maintainer = self.factory.makePerson(displayname=u"bær")
     spr = self.factory.makeSourcePackageRelease(creator=creator,
                                                 maintainer=maintainer)
     info = fetch_information(spr, None, None)
     self.assertEqual(info['date'], spr.dateuploaded)
     self.assertEqual(info['changelog'], spr.changelog_entry)
     self.assertEqual(info['changedby'],
                      format_address_for_person(spr.creator))
     self.assertEqual(info['maintainer'],
                      format_address_for_person(spr.maintainer))
     self.assertEqual(u"foø <%s>" % spr.creator.preferredemail.email,
                      info['changedby_displayname'])
     self.assertEqual(u"bær <%s>" % spr.maintainer.preferredemail.email,
                      info['maintainer_displayname'])
Exemplo n.º 5
0
 def assertRecipientsEqual(self,
                           expected,
                           changes,
                           blamer,
                           maintainer,
                           changer,
                           purpose=ArchivePurpose.PRIMARY):
     distribution = self.factory.makeDistribution()
     archive = self.factory.makeArchive(distribution=distribution,
                                        purpose=purpose)
     distroseries = self.factory.makeDistroSeries(distribution=distribution)
     # Now set the uploaders.
     component = getUtility(IComponentSet).ensure('main')
     if component not in distroseries.components:
         store = Store.of(distroseries)
         store.add(
             ComponentSelection(distroseries=distroseries,
                                component=component))
     distribution.main_archive.newComponentUploader(maintainer, component)
     distribution.main_archive.newComponentUploader(changer, component)
     observed = get_upload_notification_recipients(blamer,
                                                   archive,
                                                   distroseries,
                                                   logger=None,
                                                   changes=changes)
     self.assertContentEqual(
         [format_address_for_person(person) for person in expected],
         observed)
Exemplo n.º 6
0
 def send(self):
     """Send a message to the user about the product's licence."""
     if not self.needs_notification(self.product):
         # The project has a common licence.
         return False
     maintainer = self.product.owner
     if maintainer.is_team:
         user_address = maintainer.getTeamAdminsEmailAddresses()
     else:
         user_address = format_address_for_person(maintainer)
     from_address = format_address(
         "Launchpad", config.canonical.noreply_from_address)
     commercial_address = format_address(
         'Commercial', '*****@*****.**')
     substitutions = dict(
         user_displayname=maintainer.displayname,
         user_name=maintainer.name,
         product_name=self.product.name,
         product_url=canonical_url(self.product),
         commercial_use_expiration=self.getCommercialUseMessage(),
         )
     # Email the user about licence policy.
     subject = (
         "Licence information for %(product_name)s "
         "in Launchpad" % substitutions)
     template = get_email_template(
         self.getTemplateName(), app='registry')
     message = template % substitutions
     simple_sendmail(
         from_address, user_address,
         subject, message, headers={'Reply-To': commercial_address})
     # Inform that Launchpad recognized the licence change.
     self._addLicenseChangeToReviewWhiteboard()
     return True
 def getBzrCommitterID(self):
     """Find the committer id to pass to bzr."""
     if self.committer_id is not None:
         return self.committer_id
     elif self.committer.preferredemail is not None:
         return format_address_for_person(self.committer)
     else:
         return '"%s" <%s>' % (self.committer.displayname, config.canonical.noreply_from_address)
Exemplo n.º 8
0
 def test_to_addrs_for_review_request(self):
     request, requester = self.makeReviewRequest()
     switch_dbuser(config.IBranchMergeProposalJobSource.dbuser)
     mailer = BMPMailer.forReviewRequest(
         request, request.merge_proposal, requester)
     ctrl = mailer.generateEmail(request.recipient.preferredemail.email,
                                 request.recipient)
     recipient_addr = format_address_for_person(request.recipient)
     self.assertEqual([recipient_addr], ctrl.to_addrs)
 def test_fetch_information_bprs(self):
     bpr = self.factory.makeBinaryPackageRelease()
     info = fetch_information(None, [bpr], None)
     spr = bpr.build.source_package_release
     self.assertEqual(info['date'], spr.dateuploaded)
     self.assertEqual(info['changelog'], spr.changelog_entry)
     self.assertEqual(
         info['changedby'], format_address_for_person(spr.creator))
     self.assertEqual(
         info['maintainer'], format_address_for_person(spr.maintainer))
     self.assertEqual(
         info['changedby_displayname'],
         formataddr((spr.creator.displayname,
                     spr.creator.preferredemail.email)))
     self.assertEqual(
         info['maintainer_displayname'],
         formataddr((spr.maintainer.displayname,
                     spr.maintainer.preferredemail.email)))
Exemplo n.º 10
0
 def test_fetch_information_bprs(self):
     bpr = self.factory.makeBinaryPackageRelease()
     info = fetch_information(None, [bpr], None)
     spr = bpr.build.source_package_release
     self.assertEqual(info['date'], spr.dateuploaded)
     self.assertEqual(info['changelog'], spr.changelog_entry)
     self.assertEqual(info['changedby'],
                      format_address_for_person(spr.creator))
     self.assertEqual(info['maintainer'],
                      format_address_for_person(spr.maintainer))
     self.assertEqual(
         info['changedby_displayname'],
         formataddr(
             (spr.creator.displayname, spr.creator.preferredemail.email)))
     self.assertEqual(
         info['maintainer_displayname'],
         formataddr((spr.maintainer.displayname,
                     spr.maintainer.preferredemail.email)))
Exemplo n.º 11
0
 def test_fetch_information_spr(self):
     creator = self.factory.makePerson(displayname=u"foø")
     maintainer = self.factory.makePerson(displayname=u"bær")
     spr = self.factory.makeSourcePackageRelease(
         creator=creator, maintainer=maintainer)
     info = fetch_information(spr, None, None)
     self.assertEqual(info['date'], spr.dateuploaded)
     self.assertEqual(info['changelog'], spr.changelog_entry)
     self.assertEqual(
         info['changedby'], format_address_for_person(spr.creator))
     self.assertEqual(
         info['maintainer'], format_address_for_person(spr.maintainer))
     self.assertEqual(
         u"foø <%s>" % spr.creator.preferredemail.email,
         info['changedby_displayname'])
     self.assertEqual(
         u"bær <%s>" % spr.maintainer.preferredemail.email,
         info['maintainer_displayname'])
 def getBzrCommitterID(self):
     """Find the committer id to pass to bzr."""
     if self.committer_id is not None:
         return self.committer_id
     elif self.committer.preferredemail is not None:
         return format_address_for_person(self.committer)
     else:
         return '"%s" <%s>' % (
             self.committer.displayname,
             config.canonical.noreply_from_address)
Exemplo n.º 13
0
 def _assert_mails_are_correct(self, build, reasons, ppa=False):
     notifications = pop_notifications()
     reasons = sorted(reasons,
                      key=lambda r: format_address_for_person(r[0]))
     for notification, (recipient, reason) in zip(notifications, reasons):
         self._assert_mail_is_correct(build,
                                      notification,
                                      recipient,
                                      reason,
                                      ppa=ppa)
Exemplo n.º 14
0
def get_upload_notification_recipients(blamer,
                                       archive,
                                       distroseries,
                                       logger=None,
                                       changes=None,
                                       spr=None,
                                       bprs=None):
    """Return a list of recipients for notification emails."""
    debug(logger, "Building recipients list.")
    candidate_recipients = [blamer]
    info = fetch_information(spr, bprs, changes)

    changer = email_to_person(info['changedby'])
    maintainer = email_to_person(info['maintainer'])

    if blamer is None and not archive.is_copy:
        debug(logger, "Changes file is unsigned; adding changer as recipient.")
        candidate_recipients.append(changer)

    if archive.is_ppa:
        # For PPAs, any person or team mentioned explicitly in the
        # ArchivePermissions as uploaders for the archive will also
        # get emailed.
        candidate_recipients.extend([
            permission.person
            for permission in archive.getUploadersForComponent()
        ])
    elif archive.is_copy:
        # For copy archives, notifying anyone else will probably only
        # confuse them.
        pass
    else:
        # If this is not a PPA, we also consider maintainer and changed-by.
        if blamer is not None:
            if is_valid_uploader(maintainer, distroseries.distribution):
                debug(logger, "Adding maintainer to recipients")
                candidate_recipients.append(maintainer)

            if is_valid_uploader(changer, distroseries.distribution):
                debug(logger, "Adding changed-by to recipients")
                candidate_recipients.append(changer)

    # Collect email addresses to notify.  Skip persons who do not have a
    # preferredemail set, such as people who have not activated their
    # Launchpad accounts (and are therefore not expecting this email).
    recipients = [
        format_address_for_person(person)
        for person in filter(None, set(candidate_recipients))
        if person.preferredemail is not None
    ]

    for recipient in recipients:
        debug(logger, "Adding recipient: '%s'", recipient)

    return recipients
Exemplo n.º 15
0
 def test_no_warning_for_UserToUserEmail_recipient(self):
     # A UserToUserEmail.recipient reference does not cause a warning.
     # Since the fix for https://bugs.launchpad.net/launchpad/+bug/246022
     # it's no longer possible to create these without also having a
     # TeamMembership.person reference (which separately inhibits
     # visibility changes), but there are still examples on production.
     sender = self.factory.makePerson()
     self.team.setContactAddress(
         getUtility(IEmailAddressSet).new("*****@*****.**", self.team))
     message = MIMEText("")
     message["From"] = format_address_for_person(sender)
     message["To"] = format_address_for_person(self.team)
     message["Subject"] = ""
     message["Message-ID"] = make_msgid("launchpad")
     message["Date"] = formatdate()
     IDirectEmailAuthorization(sender).record(message)
     transaction.commit()
     self.assertEqual(
         None,
         self.team.visibilityConsistencyWarning(PersonVisibility.PRIVATE))
Exemplo n.º 16
0
 def test_getErrorRecipients(self):
     # The pillar owner and job requestor are the error recipients.
     requestor = self.factory.makePerson()
     product = self.factory.makeProduct()
     bug = self.factory.makeBug(target=product)
     job = getUtility(IRemoveArtifactSubscriptionsJobSource).create(
         requestor, [bug], pillar=product)
     expected_emails = [
         format_address_for_person(person)
         for person in (product.owner, requestor)]
     self.assertContentEqual(
         expected_emails, job.getErrorRecipients())
Exemplo n.º 17
0
 def forReviewRequest(cls, reason, merge_proposal, from_user):
     """Return a mailer for a request to review a BranchMergeProposal."""
     from_address = format_address_for_person(from_user)
     recipients = {reason.subscriber: reason}
     return cls('%(proposal_title)s',
                'review-requested.txt',
                recipients,
                merge_proposal,
                from_address,
                message_id=get_msgid(),
                preview_diff=merge_proposal.preview_diff,
                direct_email=True)
Exemplo n.º 18
0
 def test_getErrorRecipients(self):
     # The pillar owner and job requestor are the error recipients.
     requestor = self.factory.makePerson()
     product = self.factory.makeProduct()
     bug = self.factory.makeBug(target=product)
     job = getUtility(IRemoveArtifactSubscriptionsJobSource).create(
         requestor, [bug], pillar=product)
     expected_emails = [
         format_address_for_person(person)
         for person in (product.owner, requestor)
     ]
     self.assertContentEqual(expected_emails, job.getErrorRecipients())
Exemplo n.º 19
0
def add_recipient(recipients, person, reason_factory, logger=None):
    # Circular import.
    from lp.registry.model.person import get_recipients

    if person is None:
        return
    for recipient in get_recipients(person):
        if recipient not in recipients:
            debug(
                logger, "Adding recipient: '%s'" %
                format_address_for_person(recipient))
            reason = reason_factory(person, recipient)
            recipients[recipient] = reason
Exemplo n.º 20
0
 def test_to_addrs_excludes_team_reviewers(self):
     """Addresses for the to header exclude requested team reviewers."""
     bmp, subscriber = self.makeProposalWithSubscriber()
     team = self.factory.makeTeam(email='*****@*****.**')
     CodeReviewVoteReference(
         branch_merge_proposal=bmp, reviewer=team, registrant=subscriber)
     switch_dbuser(config.IBranchMergeProposalJobSource.dbuser)
     mailer = BMPMailer.forCreation(bmp, bmp.registrant)
     ctrl = mailer.generateEmail(subscriber.preferredemail.email,
                                 subscriber)
     reviewer = bmp.target_branch.owner
     reviewer_id = format_address_for_person(reviewer)
     self.assertEqual(set([reviewer_id, bmp.address]), set(ctrl.to_addrs))
Exemplo n.º 21
0
 def test_run(self):
     # Running a job produces a notification.  Detailed tests of which
     # notifications go to whom live in the PackageUpload and
     # PackageUploadMailer tests.
     distroseries = self.factory.makeDistroSeries()
     creator = self.factory.makePerson()
     changes = Changes({"Changed-By": format_address_for_person(creator)})
     upload = self.factory.makePackageUpload(
         distroseries=distroseries,
         archive=distroseries.main_archive,
         changes_file_content=changes.dump().encode("UTF-8"))
     upload.addSource(self.factory.makeSourcePackageRelease())
     self.factory.makeComponentSelection(
         upload.distroseries, upload.sourcepackagerelease.component)
     upload.setAccepted()
     job = PackageUploadNotificationJob.create(upload,
                                               summary_text='Fake summary')
     with dbuser(job.config.dbuser):
         JobRunner([job]).runAll()
     [email] = pop_notifications()
     self.assertEqual(format_address_for_person(creator), email['To'])
     self.assertIn('(Accepted)', email['Subject'])
     self.assertIn('Fake summary', email.get_payload()[0].get_payload())
Exemplo n.º 22
0
 def test_to_addrs_includes_reviewers(self):
     """The addresses for the to header include requested reviewers"""
     request, requester = self.makeReviewRequest()
     bmp = request.merge_proposal
     bmp.source_branch.subscribe(
         bmp.registrant, BranchSubscriptionNotificationLevel.NOEMAIL, None,
         CodeReviewNotificationLevel.FULL, bmp.registrant)
     switch_dbuser(config.IBranchMergeProposalJobSource.dbuser)
     mailer = BMPMailer.forCreation(bmp, bmp.registrant)
     ctrl = mailer.generateEmail(bmp.registrant.preferredemail.email,
                                 bmp.registrant)
     reviewer = request.recipient
     reviewer_id = format_address_for_person(reviewer)
     self.assertEqual(set([reviewer_id, bmp.address]), set(ctrl.to_addrs))
Exemplo n.º 23
0
    def test_generateCreationEmail(self):
        """Ensure that the contents of the mail are as expected"""
        bmp, subscriber = self.makeProposalWithSubscriber()
        switch_dbuser(config.IBranchMergeProposalJobSource.dbuser)
        mailer = BMPMailer.forCreation(bmp, bmp.registrant)
        assert mailer.message_id is not None, 'Message-id should be set'
        mailer.message_id = '<foobar-example-com>'
        reason = mailer._recipients.getReason(
            subscriber.preferredemail.email)[0]
        bmp.root_message_id = None
        ctrl = mailer.generateEmail('*****@*****.**', subscriber)
        reviewer = bmp.target_branch.owner
        expected = dedent("""\
        Baz Qux has proposed merging %(source)s into %(target)s.

        Commit message:
        %(commit_message)s

        Requested reviews:
          %(reviewer)s

        For more details, see:
        %(bmp)s
        --\x20
        %(reason)s
        """) % {
            'source': bmp.source_branch.bzr_identity,
            'target': bmp.target_branch.bzr_identity,
            'commit_message': bmp.commit_message,
            'reviewer': reviewer.unique_displayname,
            'bmp': canonical_url(bmp),
            'reason': reason.getReason()}
        self.assertEqual(expected, ctrl.body)
        self.assertEqual('[Merge] '
            'lp://dev/~bob/super-product/fix-foo-for-bar into '
            'lp://dev/~mary/super-product/bar', ctrl.subject)
        self.assertEqual(
            {'X-Launchpad-Branch': bmp.source_branch.unique_name,
             'X-Launchpad-Message-Rationale': 'Subscriber',
             'X-Launchpad-Message-For': subscriber.name,
             'X-Launchpad-Notification-Type': 'code-review',
             'X-Launchpad-Project': bmp.source_branch.product.name,
             'Reply-To': bmp.address,
             'Message-Id': '<foobar-example-com>'},
            ctrl.headers)
        self.assertEqual('Baz Qux <*****@*****.**>', ctrl.from_addr)
        reviewer_id = format_address_for_person(reviewer)
        self.assertEqual(set([reviewer_id, bmp.address]), set(ctrl.to_addrs))
        mailer.sendAll()
Exemplo n.º 24
0
def get_upload_notification_recipients(blamer, archive, distroseries,
                                       logger=None, changes=None, spr=None,
                                       bprs=None):
    """Return a list of recipients for notification emails."""
    debug(logger, "Building recipients list.")
    candidate_recipients = [blamer]
    info = fetch_information(spr, bprs, changes)

    changer = email_to_person(info['changedby'])
    maintainer = email_to_person(info['maintainer'])

    if blamer is None and not archive.is_copy:
        debug(logger, "Changes file is unsigned; adding changer as recipient.")
        candidate_recipients.append(changer)

    if archive.is_ppa:
        # For PPAs, any person or team mentioned explicitly in the
        # ArchivePermissions as uploaders for the archive will also
        # get emailed.
        candidate_recipients.extend([
            permission.person
            for permission in archive.getUploadersForComponent()])
    elif archive.is_copy:
        # For copy archives, notifying anyone else will probably only
        # confuse them.
        pass
    else:
        # If this is not a PPA, we also consider maintainer and changed-by.
        if blamer is not None:
            if is_valid_uploader(maintainer, distroseries.distribution):
                debug(logger, "Adding maintainer to recipients")
                candidate_recipients.append(maintainer)

            if is_valid_uploader(changer, distroseries.distribution):
                debug(logger, "Adding changed-by to recipients")
                candidate_recipients.append(changer)

    # Collect email addresses to notify.  Skip persons who do not have a
    # preferredemail set, such as people who have not activated their
    # Launchpad accounts (and are therefore not expecting this email).
    recipients = [
        format_address_for_person(person)
        for person in filter(None, set(candidate_recipients))
            if person.preferredemail is not None]

    for recipient in recipients:
        debug(logger, "Adding recipient: '%s'", recipient)

    return recipients
Exemplo n.º 25
0
    def forBranchModified(cls, branch, user, delta, delta_for_editors=None):
        """Construct a BranchMailer for mail about a branch modification.

        :param branch: The branch that was modified.
        :param user: The user making the change.
        :param delta: an IBranchDelta representing the modification as
            visible to people who cannot edit the branch.
        :param delta_for_editors: an IBranchDelta representing the
            notification as visible to people who can edit the branch.  If
            None, `delta` is used for people who can edit the branch too.
        :return: a BranchMailer.
        """
        recipients = branch.getNotificationRecipients()
        interested_levels = (BranchSubscriptionNotificationLevel.ATTRIBUTEONLY,
                             BranchSubscriptionNotificationLevel.FULL)
        actual_recipients = {}
        # If the person editing the branch isn't in the team of the owner
        # then notify the branch owner of the changes as well.
        if user is not None and not user.inTeam(branch.owner):
            # Existing rationales are kept.
            recipients.add(branch.owner, None, None)
        for recipient in recipients:
            subscription, rationale = recipients.getReason(recipient)
            if (subscription is not None and subscription.notification_level
                    not in interested_levels):
                continue
            if subscription is None:
                actual_recipients[recipient] = RecipientReason.forBranchOwner(
                    branch, recipient)
            else:
                actual_recipients[recipient] = \
                    RecipientReason.forBranchSubscriber(
                    subscription, branch, recipient, rationale)
        if user is not None:
            from_address = format_address_for_person(user)
        else:
            from_address = config.canonical.noreply_from_address
        return cls('[Branch %(unique_name)s]',
                   'branch-modified.txt',
                   actual_recipients,
                   from_address,
                   delta=delta,
                   delta_for_editors=delta_for_editors,
                   notification_type='branch-updated')
Exemplo n.º 26
0
    def _getToAddresses(self, email, recipient):
        """Return the addresses to use for the to header.

        If the email is being sent directly to the recipient, their email
        address is returned.  Otherwise, the merge proposal and requested
        reviewers are returned.
        """
        if self.direct_email:
            return BaseMailer._getToAddresses(self, email, recipient)
        to_addrs = [self.merge_proposal.address]
        for vote in self.merge_proposal.votes:
            if vote.reviewer == vote.registrant:
                continue
            if vote.reviewer.is_team:
                continue
            if vote.reviewer.hide_email_addresses:
                continue
            to_addrs.append(format_address_for_person(vote.reviewer))
        return to_addrs
Exemplo n.º 27
0
 def assertRecipientsEqual(self, expected, changes, blamer, maintainer,
                           changer, purpose=ArchivePurpose.PRIMARY):
     distribution = self.factory.makeDistribution()
     archive = self.factory.makeArchive(
         distribution=distribution, purpose=purpose)
     distroseries = self.factory.makeDistroSeries(distribution=distribution)
     # Now set the uploaders.
     component = getUtility(IComponentSet).ensure('main')
     if component not in distroseries.components:
         store = Store.of(distroseries)
         store.add(
             ComponentSelection(
                 distroseries=distroseries, component=component))
     distribution.main_archive.newComponentUploader(maintainer, component)
     distribution.main_archive.newComponentUploader(changer, component)
     observed = get_upload_notification_recipients(
         blamer, archive, distroseries, logger=None, changes=changes)
     self.assertContentEqual(
         [format_address_for_person(person) for person in expected],
         observed)
Exemplo n.º 28
0
    def forCreation(cls, merge_proposal, from_user):
        """Return a mailer for BranchMergeProposal creation.

        :param merge_proposal: The BranchMergeProposal that was created.
        :param from_user: The user that the creation notification should
            come from.
        """
        recipients = merge_proposal.getNotificationRecipients(
            CodeReviewNotificationLevel.STATUS)

        assert from_user.preferredemail is not None, (
            'The sender must have an email address.')
        from_address = format_address_for_person(from_user)

        return cls('%(proposal_title)s',
                   'branch-merge-proposal-created.txt',
                   recipients,
                   merge_proposal,
                   from_address,
                   message_id=get_msgid(),
                   requested_reviews=merge_proposal.votes,
                   preview_diff=merge_proposal.preview_diff)
Exemplo n.º 29
0
    def forModification(cls, merge_proposal, delta_text, from_user):
        """Return a mailer for BranchMergeProposal creation.

        :param merge_proposal: The BranchMergeProposal that was created.
        :param from_user: The user that the creation notification should
            come from.  Optional.
        """
        recipients = merge_proposal.getNotificationRecipients(
            CodeReviewNotificationLevel.STATUS)
        if from_user is not None:
            assert from_user.preferredemail is not None, (
                'The sender must have an email address.')
            from_address = format_address_for_person(from_user)
        else:
            from_address = config.canonical.noreply_from_address
        return cls('%(proposal_title)s',
                   'branch-merge-proposal-updated.txt',
                   recipients,
                   merge_proposal,
                   from_address,
                   delta=delta_text,
                   message_id=get_msgid())
Exemplo n.º 30
0
 def test_run_failed(self):
     # A failed run sets the job status to FAILED and records the error
     # message.
     # The job requests builds and records the result.
     distroseries, processors = self.makeSeriesAndProcessors(
         ["avr2001", "sparc64", "x32"])
     [git_ref] = self.factory.makeGitRefs()
     snap = self.factory.makeSnap(
         git_ref=git_ref, distroseries=distroseries, processors=processors)
     expected_date_created = get_transaction_timestamp(IStore(snap))
     job = SnapRequestBuildsJob.create(
         snap, snap.registrant, distroseries.main_archive,
         PackagePublishingPocket.RELEASE, {"core": "stable"})
     self.useFixture(GitHostingFixture()).getBlob.failure = (
         CannotParseSnapcraftYaml("Nonsense on stilts"))
     with dbuser(config.ISnapRequestBuildsJobSource.dbuser):
         JobRunner([job]).runAll()
     now = get_transaction_timestamp(IStore(snap))
     [notification] = self.assertEmailQueueLength(1)
     self.assertThat(dict(notification), ContainsDict({
         "From": Equals(config.canonical.noreply_from_address),
         "To": Equals(format_address_for_person(snap.registrant)),
         "Subject": Equals(
             "Launchpad error while requesting builds of %s" % snap.name),
         }))
     self.assertEqual(
         "Launchpad encountered an error during the following operation: "
         "requesting builds of %s.  Nonsense on stilts" % snap.name,
         notification.get_payload(decode=True))
     self.assertThat(job, MatchesStructure(
         job=MatchesStructure.byEquality(status=JobStatus.FAILED),
         date_created=Equals(expected_date_created),
         date_finished=MatchesAll(
             GreaterThan(expected_date_created), LessThan(now)),
         error_message=Equals("Nonsense on stilts"),
         builds=AfterPreprocessing(set, MatchesSetwise())))
Exemplo n.º 31
0
 def getErrorRecipients(self):
     """See `BaseRunnableJob`."""
     return [format_address_for_person(self.reviewer)]
 def getErrorRecipients(self):
     """Return a list of email-ids to notify about user errors."""
     registrant = self.branch_merge_proposal.registrant
     return format_address_for_person(registrant)
 def getErrorRecipients(self):
     """Return a list of email-ids to notify about user errors."""
     recipients = []
     if self.editor is not None:
         recipients.append(format_address_for_person(self.editor))
     return recipients
 def getErrorRecipients(self):
     """Return a list of email-ids to notify about user errors."""
     commenter = self.code_review_comment.message.owner
     return [format_address_for_person(commenter)]
Exemplo n.º 35
0
 def getErrorRecipients(self):
     """See `IPlainPackageCopyJob`."""
     return [format_address_for_person(self.requester)]
Exemplo n.º 36
0
def notify(blamer, spr, bprs, customfiles, archive, distroseries, pocket,
           summary_text=None, changes=None, changesfile_content=None,
           changesfile_object=None, action=None, dry_run=False,
           logger=None, announce_from_person=None, previous_version=None):
    """Notify about an upload or package copy.

    :param blamer: The `IPerson` who is to blame for this notification.
    :param spr: The `ISourcePackageRelease` that was created.
    :param bprs: A list of `IBinaryPackageRelease` that were created.
    :param customfiles: An `ILibraryFileAlias` that was created.
    :param archive: The target `IArchive`.
    :param distroseries: The target `IDistroSeries`.
    :param pocket: The target `PackagePublishingPocket`.
    :param summary_text: The summary of the notification.
    :param changes: A dictionary of the parsed changes file.
    :param changesfile_content: The raw content of the changes file, so it
        can be attached to the mail if desired.
    :param changesfile_object: The raw object of the changes file. Only used
        to work out the filename for `reject_changes_file`.
    :param action: A string of what action to notify for, such as 'new',
        'accepted'.
    :param dry_run: If True, only log the mail.
    :param announce_from_person: If passed, use this `IPerson` as the From: in
        announcement emails.  If the person has no preferred email address,
        the person is ignored and the default From: is used instead.
    :param previous_version: If specified, the change log on the email will
        include all of the source package's change logs after that version
        up to and including the passed spr's version.
    """
    # If this is a binary or mixed upload, we don't send *any* emails
    # provided it's not a rejection or a security upload:
    if (
        bprs and action != 'rejected' and
        pocket != PackagePublishingPocket.SECURITY):
        debug(logger, "Not sending email; upload is from a build.")
        return

    if spr and spr.source_package_recipe_build and action == 'accepted':
        debug(logger, "Not sending email; upload is from a recipe.")
        return

    if spr is None and not bprs and not customfiles:
        # We do not have enough context to do a normal notification, so
        # reject what we do have.
        if changesfile_object is None:
            return
        reject_changes_file(
            blamer, changesfile_object.name, changes, archive, distroseries,
            summary_text, logger=logger)
        return

    # "files" will contain a list of tuples of filename,component,section.
    # If files is empty, we don't need to send an email if this is not
    # a rejection.
    try:
        files = build_uploaded_files_list(spr, bprs, customfiles, logger)
    except LanguagePackEncountered:
        # Don't send emails for language packs.
        return

    if not files and action != 'rejected':
        return

    recipients = get_upload_notification_recipients(
        blamer, archive, distroseries, logger, changes=changes, spr=spr,
        bprs=bprs)

    # There can be no recipients if none of the emails are registered
    # in LP.
    if not recipients:
        debug(logger, "No recipients on email, not sending.")
        return

    if action == 'rejected':
        default_recipient = "%s <%s>" % (
            config.uploader.default_recipient_name,
            config.uploader.default_recipient_address)
        if not recipients:
            recipients = [default_recipient]
        debug(logger, "Sending rejection email.")
        summarystring = summary_text
    else:
        summary = build_summary(spr, files, action)
        if summary_text:
            summary.append(summary_text)
        summarystring = "\n".join(summary)

    attach_changes = not archive.is_ppa

    def build_and_send_mail(action, recipients, from_addr=None, bcc=None,
                            previous_version=None):
        subject = calculate_subject(
            spr, bprs, customfiles, archive, distroseries, pocket, action)
        body = assemble_body(
            blamer, spr, bprs, archive, distroseries, summarystring, changes,
            action, previous_version=previous_version)
        body = body.encode("utf8")
        send_mail(
            spr, archive, recipients, subject, body, dry_run,
            changesfile_content=changesfile_content,
            attach_changes=attach_changes, from_addr=from_addr, bcc=bcc,
            logger=logger)

    build_and_send_mail(
        action, recipients, previous_version=previous_version)

    info = fetch_information(spr, bprs, changes)
    from_addr = info['changedby']
    if announce_from_person is not None:
        if announce_from_person.preferredemail is not None:
            from_addr = format_address_for_person(announce_from_person)

    # If we're sending an acceptance notification for a non-PPA upload,
    # announce if possible. Avoid announcing backports, binary-only
    # security uploads, or autosync uploads.
    if (action == 'accepted' and distroseries.changeslist
        and not archive.is_ppa
        and pocket != PackagePublishingPocket.BACKPORTS
        and not (pocket == PackagePublishingPocket.SECURITY and spr is None)
        and not is_auto_sync_upload(spr, bprs, pocket, from_addr)):
        name = None
        bcc_addr = None
        if spr:
            name = spr.name
        elif bprs:
            name = bprs[0].build.source_package_release.name
        if name:
            email_base = distroseries.distribution.package_derivatives_email
            if email_base:
                bcc_addr = email_base.format(package_name=name)

        build_and_send_mail(
            'announcement', [str(distroseries.changeslist)], from_addr,
            bcc_addr, previous_version=previous_version)
Exemplo n.º 37
0
 def test_getErrorRecipients(self):
     # The requester is the recipient.
     email_id = format_address_for_person(self.requester)
     self.assertEqual([email_id], self.job.getErrorRecipients())
 def test_getErrorRecipients_requester(self):
     spr, job = self.makeJob()
     email = format_address_for_person(job.requester)
     self.assertEquals([email], job.getErrorRecipients())
     removeSecurityProxy(job).requester = None
     self.assertEquals([], job.getErrorRecipients())
 def test_getErrorRecipients(self):
     # The requester is the recipient.
     email_id = format_address_for_person(self.requester)
     self.assertEqual([email_id], self.job.getErrorRecipients())
Exemplo n.º 40
0
 def getErrorRecipients(self):
     """Return a list of email-ids to notify about user errors."""
     commenter = self.code_review_comment.message.owner
     return [format_address_for_person(commenter)]
Exemplo n.º 41
0
 def getErrorRecipients(self):
     if self.requester is None:
         return []
     return [format_address_for_person(self.requester)]
Exemplo n.º 42
0
 def getErrorRecipients(self):
     """Return a list of email-ids to notify about user errors."""
     recipients = []
     if self.editor is not None:
         recipients.append(format_address_for_person(self.editor))
     return recipients
Exemplo n.º 43
0
 def getErrorRecipients(self):
     """See `IRunnableJob`."""
     return [format_address_for_person(self.user)]
Exemplo n.º 44
0
def person_to_email(person):
    """Return a string of full name <e-mail address> given an IPerson."""
    if person and person.preferredemail:
        # This will use email.Header to encode any non-ASCII characters.
        return format_address_for_person(person)
 def getErrorRecipients(self):
     if self.requester is not None:
         return [format_address_for_person(self.requester)]
     return []
Exemplo n.º 46
0
 def getErrorRecipients(self):
     """Return a list of email-ids to notify about user errors."""
     registrant = self.branch_merge_proposal.registrant
     return format_address_for_person(registrant)
 def test_getErrorRecipients_requester(self):
     _, _, job = self.makeJob()
     email = format_address_for_person(job.requester)
     self.assertEqual([email], job.getErrorRecipients())
     removeSecurityProxy(job).requester = None
     self.assertEqual([], job.getErrorRecipients())
Exemplo n.º 48
0
 def getErrorRecipients(self):
     """See `IPersonMergeJob`."""
     return [format_address_for_person(self.person)]
Exemplo n.º 49
0
 def getErrorRecipients(self):
     """See `BaseRunnableJob`."""
     return [format_address_for_person(self.reviewer)]
 def test_getErrorRecipients(self):
     # The getErrorRecipients method matches the user.
     job = make_question_job(self.factory)
     self.assertEqual(
         [format_address_for_person(job.user)], job.getErrorRecipients())