예제 #1
0
 def test_calculate_subject_spr(self):
     spr = self.factory.makeSourcePackageRelease()
     archive = self.factory.makeArchive()
     pocket = self.factory.getAnyPocket()
     distroseries = self.factory.makeDistroSeries()
     expected_subject = '[PPA %s] [%s/%s] %s %s (Accepted)' % (
         get_ppa_reference(archive), distroseries.distribution.name,
         distroseries.getSuite(pocket), spr.name, spr.version)
     subject = calculate_subject(spr, [], [], archive, distroseries, pocket,
                                 'accepted')
     self.assertEqual(expected_subject, subject)
 def test_calculate_subject_spr(self):
     spr = self.factory.makeSourcePackageRelease()
     archive = self.factory.makeArchive()
     pocket = self.factory.getAnyPocket()
     distroseries = self.factory.makeDistroSeries()
     expected_subject = '[PPA %s] [%s/%s] %s %s (Accepted)' % (
         get_ppa_reference(archive), distroseries.distribution.name,
         distroseries.getSuite(pocket), spr.name, spr.version)
     subject = calculate_subject(
         spr, [], [], archive, distroseries, pocket, 'accepted')
     self.assertEqual(expected_subject, subject)
예제 #3
0
 def test_calculate_subject_bprs(self):
     bpr = self.factory.makeBinaryPackageRelease()
     archive = self.factory.makeArchive()
     pocket = self.factory.getAnyPocket()
     distroseries = self.factory.makeDistroSeries()
     expected_subject = '[PPA %s] [%s/%s] %s %s (Accepted)' % (
         get_ppa_reference(archive), distroseries.distribution.name,
         distroseries.getSuite(pocket),
         bpr.build.source_package_release.name, bpr.version)
     subject = calculate_subject(None, [bpr], [], archive, distroseries,
                                 pocket, 'accepted')
     self.assertEqual(expected_subject, subject)
 def test_calculate_subject_bprs(self):
     bpr = self.factory.makeBinaryPackageRelease()
     archive = self.factory.makeArchive()
     pocket = self.factory.getAnyPocket()
     distroseries = self.factory.makeDistroSeries()
     expected_subject = '[PPA %s] [%s/%s] %s %s (Accepted)' % (
         get_ppa_reference(archive), distroseries.distribution.name,
         distroseries.getSuite(pocket),
         bpr.build.source_package_release.name, bpr.version)
     subject = calculate_subject(
         None, [bpr], [], archive, distroseries, pocket, 'accepted')
     self.assertEqual(expected_subject, subject)
예제 #5
0
 def test_calculate_subject_customfile(self):
     lfa = self.factory.makeLibraryFileAlias()
     package_upload = self.factory.makePackageUpload()
     customfile = package_upload.addCustom(
         lfa, PackageUploadCustomFormat.DEBIAN_INSTALLER)
     archive = self.factory.makeArchive()
     pocket = self.factory.getAnyPocket()
     distroseries = self.factory.makeDistroSeries()
     expected_subject = '[PPA %s] [%s/%s] %s - (Accepted)' % (
         get_ppa_reference(archive), distroseries.distribution.name,
         distroseries.getSuite(pocket), lfa.filename)
     subject = calculate_subject(None, [], [customfile], archive,
                                 distroseries, pocket, 'accepted')
     self.assertEqual(expected_subject, subject)
 def test_calculate_subject_customfile(self):
     lfa = self.factory.makeLibraryFileAlias()
     package_upload = self.factory.makePackageUpload()
     customfile = package_upload.addCustom(
         lfa, PackageUploadCustomFormat.DEBIAN_INSTALLER)
     archive = self.factory.makeArchive()
     pocket = self.factory.getAnyPocket()
     distroseries = self.factory.makeDistroSeries()
     expected_subject = '[PPA %s] [%s/%s] %s - (Accepted)' % (
         get_ppa_reference(archive), distroseries.distribution.name,
         distroseries.getSuite(pocket), lfa.filename)
     subject = calculate_subject(
         None, [], [customfile], archive, distroseries, pocket,
         'accepted')
     self.assertEqual(expected_subject, subject)
예제 #7
0
    def _getOrigin(self):
        """Return the contents of the Release file Origin field.

        Primary, Partner and Copy archives use the distribution displayname.
        For PPAs we use a more specific value that follows
        `get_ppa_reference`.

        :return: a text that should be used as the value of the Release file
            'Origin' field.
        """
        # XXX al-maisan, 2008-11-19, bug=299981. If this file is released
        # from a copy archive then modify the origin to indicate so.
        if self.archive.purpose == ArchivePurpose.PARTNER:
            return "Canonical"
        if not self.archive.is_ppa:
            return self.distro.displayname
        return "LP-PPA-%s" % get_ppa_reference(self.archive)
예제 #8
0
def reject_changes_file(blamer,
                        changes_file_path,
                        changes,
                        archive,
                        distroseries,
                        reason,
                        logger=None):
    """Notify about a rejection where all of the details are not known.

    :param blamer: The `IPerson` that is to blame for this notification.
    :param changes_file_path: The path to the changes file.
    :param changes: A dictionary of the parsed changes file.
    :param archive: The `IArchive` the notification is regarding.
    :param distroseries: The `IDistroSeries` the notification is regarding.
    :param reason: The reason for the rejection.
    """
    ignored, filename = os.path.split(changes_file_path)
    information = {
        'SUMMARY': reason,
        'CHANGESFILE': '',
        'DATE': '',
        'CHANGEDBY': '',
        'MAINTAINER': '',
        'SIGNER': '',
        'ORIGIN': '',
        'ARCHIVE_URL': '',
        'USERS_ADDRESS': config.launchpad.users_address,
    }
    subject = '%s rejected' % filename
    if archive and archive.is_ppa:
        subject = '[PPA %s] %s' % (get_ppa_reference(archive), subject)
        information['ARCHIVE_URL'] = '\n%s' % canonical_url(archive)
    template = get_template(archive, 'rejected')
    body = template % information
    to_addrs = get_upload_notification_recipients(blamer,
                                                  archive,
                                                  distroseries,
                                                  logger,
                                                  changes=changes)
    debug(logger, "Sending rejection email.")
    if not to_addrs:
        debug(logger, "No recipients have a preferred email.")
        return
    send_mail(None, archive, to_addrs, subject, body, False, logger=logger)
예제 #9
0
 def test__getHeaders_ppa(self):
     # _getHeaders returns useful values for headers used for filtering.
     # For a PPA, this includes other people with component upload
     # permissions.
     blamer = self.factory.makePerson()
     uploader = self.factory.makePerson()
     distroseries = self.factory.makeUbuntuDistroSeries()
     archive = self.factory.makeArchive(
         distribution=distroseries.distribution, purpose=ArchivePurpose.PPA)
     component = getUtility(IComponentSet).ensure("main")
     if component not in distroseries.components:
         self.factory.makeComponentSelection(
             distroseries=distroseries, component=component)
     archive.newComponentUploader(uploader, component)
     spr = self.factory.makeSourcePackageRelease(
         component=component, section_name="libs")
     changes = {
         'Date': '2001-01-01',
         'Changed-By': 'Changer <*****@*****.**>',
         'Maintainer': 'Maintainer <*****@*****.**>',
         'Changes': ' * Foo!',
         }
     mailer = PackageUploadMailer.forAction(
         "accepted", blamer, spr, [], [], archive, distroseries,
         PackagePublishingPocket.RELEASE, changes=changes)
     recipients = dict(mailer._recipients.getRecipientPersons())
     for person, rationale in (
             (blamer, "Requester"),
             (uploader, "PPA Uploader")):
         email = person.preferredemail.email
         headers = mailer._getHeaders(email, recipients[email])
         self.assertThat(
             headers,
             ContainsDict({
                 "X-Launchpad-Message-Rationale": Equals(rationale),
                 "X-Launchpad-Message-For": Equals(person.name),
                 "X-Launchpad-Notification-Type": Equals("package-upload"),
                 "X-Katie": Equals("Launchpad actually"),
                 "X-Launchpad-Archive": Equals(archive.reference),
                 "X-Launchpad-PPA": Equals(get_ppa_reference(archive)),
                 "X-Launchpad-Component": Equals(
                     "component=main, section=libs"),
                 }))
예제 #10
0
def calculate_subject(spr, bprs, customfiles, archive, distroseries,
                      pocket, action):
    """Return the e-mail subject for the notification."""
    suite = distroseries.getSuite(pocket)
    names = set()
    version = '-'
    if spr:
        names.add(spr.name)
        version = spr.version
    elif bprs:
        names.add(bprs[0].build.source_package_release.name)
        version = bprs[0].build.source_package_release.version
    for custom in customfiles:
        names.add(custom.libraryfilealias.filename)
    name_str = ', '.join(names)
    subject = '[%s/%s] %s %s (%s)' % (
        distroseries.distribution.name, suite, name_str, version,
        ACTION_DESCRIPTIONS[action])
    if archive.is_ppa:
        subject = '[PPA %s] %s' % (get_ppa_reference(archive), subject)
    return subject
예제 #11
0
def calculate_subject(spr, bprs, customfiles, archive, distroseries, pocket,
                      action):
    """Return the e-mail subject for the notification."""
    suite = distroseries.getSuite(pocket)
    names = set()
    version = '-'
    if spr:
        names.add(spr.name)
        version = spr.version
    elif bprs:
        names.add(bprs[0].build.source_package_release.name)
        version = bprs[0].build.source_package_release.version
    for custom in customfiles:
        names.add(custom.libraryfilealias.filename)
    name_str = ', '.join(names)
    subject = '[%s/%s] %s %s (%s)' % (distroseries.distribution.name, suite,
                                      name_str, version,
                                      ACTION_DESCRIPTIONS[action])
    if archive.is_ppa:
        subject = '[PPA %s] %s' % (get_ppa_reference(archive), subject)
    return subject
예제 #12
0
    def _getHeaders(self, email, recipient):
        """See `BaseMailer`."""
        headers = super(PackageUploadMailer,
                        self)._getHeaders(email, recipient)
        headers['X-Katie'] = 'Launchpad actually'
        headers['X-Launchpad-Archive'] = self.archive.reference

        # The deprecated PPA reference header is included for Ubuntu PPAs to
        # avoid breaking existing consumers.
        if self.archive.is_ppa and self.archive.distribution.name == u'ubuntu':
            headers['X-Launchpad-PPA'] = get_ppa_reference(self.archive)

        # Include a 'X-Launchpad-Component' header with the component and
        # the section of the source package uploaded in order to facilitate
        # filtering on the part of the email recipients.
        if self.spr:
            headers['X-Launchpad-Component'] = 'component=%s, section=%s' % (
                self.spr.component.name, self.spr.section.name)

        # All emails from here have a Bcc to the default recipient.
        bcc_text = format_address(config.uploader.default_recipient_name,
                                  config.uploader.default_recipient_address)
        if zope_isinstance(recipient, AnnouncementStubPerson):
            name = None
            if self.spr:
                name = self.spr.name
            elif self.bprs:
                name = self.bprs[0].build.source_package_release.name
            if name:
                distribution = self.distroseries.distribution
                email_base = distribution.package_derivatives_email
                if email_base:
                    bcc_text += ", " + email_base.format(package_name=name)
        headers['Bcc'] = bcc_text

        return headers
예제 #13
0
def reject_changes_file(blamer, changes_file_path, changes, archive,
                        distroseries, reason, logger=None):
    """Notify about a rejection where all of the details are not known.

    :param blamer: The `IPerson` that is to blame for this notification.
    :param changes_file_path: The path to the changes file.
    :param changes: A dictionary of the parsed changes file.
    :param archive: The `IArchive` the notification is regarding.
    :param distroseries: The `IDistroSeries` the notification is regarding.
    :param reason: The reason for the rejection.
    """
    ignored, filename = os.path.split(changes_file_path)
    information = {
        'SUMMARY': reason,
        'CHANGESFILE': '',
        'DATE': '',
        'CHANGEDBY': '',
        'MAINTAINER': '',
        'SIGNER': '',
        'ORIGIN': '',
        'ARCHIVE_URL': '',
        'USERS_ADDRESS': config.launchpad.users_address,
    }
    subject = '%s rejected' % filename
    if archive and archive.is_ppa:
        subject = '[PPA %s] %s' % (get_ppa_reference(archive), subject)
        information['ARCHIVE_URL'] = '\n%s' % canonical_url(archive)
    template = get_template(archive, 'rejected')
    body = template % information
    to_addrs = get_upload_notification_recipients(
        blamer, archive, distroseries, logger, changes=changes)
    debug(logger, "Sending rejection email.")
    if not to_addrs:
        debug(logger, "No recipients have a preferred email.")
        return
    send_mail(None, archive, to_addrs, subject, body, False, logger=logger)
예제 #14
0
    def _assert_mail_is_correct(self, build, notification, ppa=False):
        # Assert that the mail sent (which is in notification), matches
        # the data from the build
        self.assertEquals('*****@*****.**',
            notification['X-Creator-Recipient'])
        self.assertEquals(
            self.das.architecturetag, notification['X-Launchpad-Build-Arch'])
        self.assertEquals(
            'main', notification['X-Launchpad-Build-Component'])
        self.assertEquals(
            build.status.name, notification['X-Launchpad-Build-State'])
        if ppa is True:
            self.assertEquals(
                get_ppa_reference(self.ppa), notification['X-Launchpad-PPA'])
        body = notification.get_payload(decode=True)
        build_log = 'None'
        if ppa is True:
            archive = '%s PPA' % get_ppa_reference(build.archive)
            source = 'not available'
        else:
            archive = '%s primary archive' % (
                self.distroseries.distribution.name)
            source = canonical_url(build.distributionsourcepackagerelease)
        builder = canonical_url(build.builder)
        if build.status == BuildStatus.BUILDING:
            duration = 'not finished'
            build_log = 'see builder page'
        elif (
            build.status == BuildStatus.SUPERSEDED or
            build.status == BuildStatus.NEEDSBUILD):
            duration = 'not available'
            build_log = 'not available'
            builder = 'not available'
        elif build.status == BuildStatus.UPLOADING:
            duration = 'uploading'
            build_log = 'see builder page'
            builder = 'not available'
        else:
            duration = DurationFormatterAPI(
                build.duration).approximateduration()
        expected_body = dedent("""
         * Source Package: %s
         * Version: %s
         * Architecture: %s
         * Archive: %s
         * Component: main
         * State: %s
         * Duration: %s
         * Build Log: %s
         * Builder: %s
         * Source: %s



        If you want further information about this situation, feel free to
        contact a member of the Launchpad Buildd Administrators team.

        --
        %s
        %s
        """ % (
            build.source_package_release.sourcepackagename.name,
            build.source_package_release.version, self.das.architecturetag,
            archive, build.status.title, duration, build_log, builder,
            source, build.title, canonical_url(build)))
        self.assertEquals(expected_body, body)
예제 #15
0
def send_mail(
    spr, archive, to_addrs, subject, mail_text, dry_run, from_addr=None,
    bcc=None, changesfile_content=None, attach_changes=False, logger=None):
    """Send an email to to_addrs with the given text and subject.

    :param spr: The `ISourcePackageRelease` to be notified about.
    :param archive: The target `IArchive`.
    :param to_addrs: A list of email addresses to be used as recipients.
        Each email must be a valid ASCII str instance or a unicode one.
    :param subject: The email's subject.
    :param mail_text: The text body of the email. Unicode is preserved in the
        email.
    :param dry_run: Whether or not an email should actually be sent. But
        please note that this flag is (largely) ignored.
    :param from_addr: The email address to be used as the sender. Must be a
        valid ASCII str instance or a unicode one.  Defaults to the email
        for config.uploader.
    :param bcc: Optional email Blind Carbon Copy address(es).
    :param param changesfile_content: The content of the actual changesfile.
    :param attach_changes: A flag governing whether the original changesfile
        content shall be attached to the email.
    """
    extra_headers = {'X-Katie': 'Launchpad actually'}

    # Include the 'X-Launchpad-PPA' header for PPA upload notfications
    # containing the PPA owner name.
    if archive.is_ppa:
        extra_headers['X-Launchpad-PPA'] = get_ppa_reference(archive)

    # Include a 'X-Launchpad-Component' header with the component and
    # the section of the source package uploaded in order to facilitate
    # filtering on the part of the email recipients.
    if spr:
        xlp_component_header = 'component=%s, section=%s' % (
            spr.component.name, spr.section.name)
        extra_headers['X-Launchpad-Component'] = xlp_component_header

    if from_addr is None:
        from_addr = format_address(
            config.uploader.default_sender_name,
            config.uploader.default_sender_address)

    # `sendmail`, despite handling unicode message bodies, can't
    # cope with non-ascii sender/recipient addresses, so ascii_smash
    # is used on all addresses.

    # All emails from here have a Bcc to the default recipient.
    bcc_text = format_address(
        config.uploader.default_recipient_name,
        config.uploader.default_recipient_address)
    if bcc:
        bcc_text = "%s, %s" % (bcc_text, bcc)
    extra_headers['Bcc'] = ascii_smash(bcc_text)

    recipients = ascii_smash(", ".join(to_addrs))
    if isinstance(from_addr, unicode):
        # ascii_smash only works on unicode strings.
        from_addr = ascii_smash(from_addr)
    else:
        from_addr.encode('ascii')

    if dry_run and logger is not None:
        debug(logger, "Would have sent a mail:")
    else:
        debug(logger, "Sent a mail:")
    debug(logger, "  Subject: %s" % subject)
    debug(logger, "  Sender: %s" % from_addr)
    debug(logger, "  Recipients: %s" % recipients)
    if 'Bcc' in extra_headers:
        debug(logger, "  Bcc: %s" % extra_headers['Bcc'])
    debug(logger, "  Body:")
    for line in mail_text.splitlines():
        if isinstance(line, str):
            line = line.decode('utf-8', 'replace')
        debug(logger, line)

    if not dry_run:
        # Since we need to send the original changesfile as an
        # attachment the sendmail() method will be used as opposed to
        # simple_sendmail().
        message = MIMEMultipart()
        message['from'] = from_addr
        message['subject'] = subject
        message['to'] = recipients

        # Set the extra headers if any are present.
        for key, value in extra_headers.iteritems():
            message.add_header(key, value)

        # Add the email body.
        message.attach(
            MIMEText(sanitize_string(mail_text).encode('utf-8'),
                'plain', 'utf-8'))

        if attach_changes:
            # Add the original changesfile as an attachment.
            if changesfile_content is not None:
                changesfile_text = sanitize_string(changesfile_content)
            else:
                changesfile_text = ("Sorry, changesfile not available.")

            attachment = MIMEText(
                changesfile_text.encode('utf-8'), 'plain', 'utf-8')
            attachment.add_header(
                'Content-Disposition',
                'attachment; filename="changesfile"')
            message.attach(attachment)

        # And finally send the message.
        sendmail(message)
예제 #16
0
def send_mail(spr,
              archive,
              to_addrs,
              subject,
              mail_text,
              dry_run,
              from_addr=None,
              bcc=None,
              changesfile_content=None,
              attach_changes=False,
              logger=None):
    """Send an email to to_addrs with the given text and subject.

    :param spr: The `ISourcePackageRelease` to be notified about.
    :param archive: The target `IArchive`.
    :param to_addrs: A list of email addresses to be used as recipients.
        Each email must be a valid ASCII str instance or a unicode one.
    :param subject: The email's subject.
    :param mail_text: The text body of the email. Unicode is preserved in the
        email.
    :param dry_run: Whether or not an email should actually be sent. But
        please note that this flag is (largely) ignored.
    :param from_addr: The email address to be used as the sender. Must be a
        valid ASCII str instance or a unicode one.  Defaults to the email
        for config.uploader.
    :param bcc: Optional email Blind Carbon Copy address(es).
    :param param changesfile_content: The content of the actual changesfile.
    :param attach_changes: A flag governing whether the original changesfile
        content shall be attached to the email.
    """
    extra_headers = {'X-Katie': 'Launchpad actually'}

    # Include the 'X-Launchpad-PPA' header for PPA upload notfications
    # containing the PPA owner name.
    if archive.is_ppa:
        extra_headers['X-Launchpad-PPA'] = get_ppa_reference(archive)

    # Include a 'X-Launchpad-Component' header with the component and
    # the section of the source package uploaded in order to facilitate
    # filtering on the part of the email recipients.
    if spr:
        xlp_component_header = 'component=%s, section=%s' % (
            spr.component.name, spr.section.name)
        extra_headers['X-Launchpad-Component'] = xlp_component_header

    if from_addr is None:
        from_addr = format_address(config.uploader.default_sender_name,
                                   config.uploader.default_sender_address)

    # `sendmail`, despite handling unicode message bodies, can't
    # cope with non-ascii sender/recipient addresses, so ascii_smash
    # is used on all addresses.

    # All emails from here have a Bcc to the default recipient.
    bcc_text = format_address(config.uploader.default_recipient_name,
                              config.uploader.default_recipient_address)
    if bcc:
        bcc_text = "%s, %s" % (bcc_text, bcc)
    extra_headers['Bcc'] = ascii_smash(bcc_text)

    recipients = ascii_smash(", ".join(to_addrs))
    if isinstance(from_addr, unicode):
        # ascii_smash only works on unicode strings.
        from_addr = ascii_smash(from_addr)
    else:
        from_addr.encode('ascii')

    if dry_run and logger is not None:
        debug(logger, "Would have sent a mail:")
    else:
        debug(logger, "Sent a mail:")
    debug(logger, "  Subject: %s" % subject)
    debug(logger, "  Sender: %s" % from_addr)
    debug(logger, "  Recipients: %s" % recipients)
    if 'Bcc' in extra_headers:
        debug(logger, "  Bcc: %s" % extra_headers['Bcc'])
    debug(logger, "  Body:")
    for line in mail_text.splitlines():
        if isinstance(line, str):
            line = line.decode('utf-8', 'replace')
        debug(logger, line)

    if not dry_run:
        # Since we need to send the original changesfile as an
        # attachment the sendmail() method will be used as opposed to
        # simple_sendmail().
        message = MIMEMultipart()
        message['from'] = from_addr
        message['subject'] = subject
        message['to'] = recipients

        # Set the extra headers if any are present.
        for key, value in extra_headers.iteritems():
            message.add_header(key, value)

        # Add the email body.
        message.attach(
            MIMEText(
                sanitize_string(mail_text).encode('utf-8'), 'plain', 'utf-8'))

        if attach_changes:
            # Add the original changesfile as an attachment.
            if changesfile_content is not None:
                changesfile_text = sanitize_string(changesfile_content)
            else:
                changesfile_text = ("Sorry, changesfile not available.")

            attachment = MIMEText(changesfile_text.encode('utf-8'), 'plain',
                                  'utf-8')
            attachment.add_header('Content-Disposition',
                                  'attachment; filename="changesfile"')
            message.attach(attachment)

        # And finally send the message.
        sendmail(message)
예제 #17
0
    def _assert_mail_is_correct(self,
                                build,
                                notification,
                                recipient,
                                reason,
                                ppa=False):
        # Assert that the mail sent (which is in notification), matches
        # the data from the build
        self.assertEqual(format_address_for_person(recipient),
                         notification['To'])
        if reason == "buildd-admin":
            rationale = "Buildd-Admin @launchpad-buildd-admins"
            expected_for = "launchpad-buildd-admins"
        else:
            rationale = reason.title()
            expected_for = recipient.name
        self.assertEqual(rationale,
                         notification['X-Launchpad-Message-Rationale'])
        self.assertEqual(expected_for, notification['X-Launchpad-Message-For'])
        self.assertEqual('package-build-status',
                         notification['X-Launchpad-Notification-Type'])
        self.assertEqual('*****@*****.**',
                         notification['X-Creator-Recipient'])
        self.assertEqual(self.das.architecturetag,
                         notification['X-Launchpad-Build-Arch'])
        self.assertEqual('main', notification['X-Launchpad-Build-Component'])
        self.assertEqual(build.status.name,
                         notification['X-Launchpad-Build-State'])
        self.assertEqual(build.archive.reference,
                         notification['X-Launchpad-Archive'])
        if ppa and build.archive.distribution.name == 'ubuntu':
            self.assertEqual(get_ppa_reference(self.ppa),
                             notification['X-Launchpad-PPA'])
        body = notification.get_payload(decode=True)
        build_log = 'None'
        if ppa:
            source = 'not available'
        else:
            source = canonical_url(build.distributionsourcepackagerelease)
        if build.status == BuildStatus.BUILDING:
            duration = 'not finished'
            build_log = 'see builder page'
            builder = canonical_url(build.builder)
        elif (build.status == BuildStatus.SUPERSEDED
              or build.status == BuildStatus.NEEDSBUILD):
            duration = 'not available'
            build_log = 'not available'
            builder = 'not available'
        elif build.status == BuildStatus.UPLOADING:
            duration = 'uploading'
            build_log = 'see builder page'
            builder = 'not available'
        else:
            duration = DurationFormatterAPI(
                build.duration).approximateduration()
            builder = canonical_url(build.builder)
        expected_body = dedent(
            """
         * Source Package: %s
         * Version: %s
         * Architecture: %s
         * Archive: %s
         * Component: main
         * State: %s
         * Duration: %s
         * Build Log: %s
         * Builder: %s
         * Source: %s



        If you want further information about this situation, feel free to
        contact us by asking a question on Launchpad
        (https://answers.launchpad.net/launchpad/+addquestion).

        %s
        %s
        %s
        """ %
            (build.source_package_release.sourcepackagename.name,
             build.source_package_release.version, self.das.architecturetag,
             build.archive.reference, build.status.title, duration, build_log,
             builder, source, "-- ", build.title, canonical_url(build)))
        expected_body += "\n" + REASONS[reason] + "\n"
        self.assertEqual(expected_body, body)
예제 #18
0
    def _assert_mail_is_correct(self, build, notification, ppa=False):
        # Assert that the mail sent (which is in notification), matches
        # the data from the build
        self.assertEquals('*****@*****.**',
                          notification['X-Creator-Recipient'])
        self.assertEquals(self.das.architecturetag,
                          notification['X-Launchpad-Build-Arch'])
        self.assertEquals('main', notification['X-Launchpad-Build-Component'])
        self.assertEquals(build.status.name,
                          notification['X-Launchpad-Build-State'])
        if ppa is True:
            self.assertEquals(get_ppa_reference(self.ppa),
                              notification['X-Launchpad-PPA'])
        body = notification.get_payload(decode=True)
        build_log = 'None'
        if ppa is True:
            archive = '%s PPA' % get_ppa_reference(build.archive)
            source = 'not available'
        else:
            archive = '%s primary archive' % (
                self.distroseries.distribution.name)
            source = canonical_url(build.distributionsourcepackagerelease)
        builder = canonical_url(build.builder)
        if build.status == BuildStatus.BUILDING:
            duration = 'not finished'
            build_log = 'see builder page'
        elif (build.status == BuildStatus.SUPERSEDED
              or build.status == BuildStatus.NEEDSBUILD):
            duration = 'not available'
            build_log = 'not available'
            builder = 'not available'
        elif build.status == BuildStatus.UPLOADING:
            duration = 'uploading'
            build_log = 'see builder page'
            builder = 'not available'
        else:
            duration = DurationFormatterAPI(
                build.duration).approximateduration()
        expected_body = dedent("""
         * Source Package: %s
         * Version: %s
         * Architecture: %s
         * Archive: %s
         * Component: main
         * State: %s
         * Duration: %s
         * Build Log: %s
         * Builder: %s
         * Source: %s



        If you want further information about this situation, feel free to
        contact a member of the Launchpad Buildd Administrators team.

        --
        %s
        %s
        """ % (build.source_package_release.sourcepackagename.name,
               build.source_package_release.version, self.das.architecturetag,
               archive, build.status.title, duration, build_log, builder,
               source, build.title, canonical_url(build)))
        self.assertEquals(expected_body, body)
예제 #19
0
    def notify(self, extra_info=None):
        """See `IPackageBuild`.

        If config.buildmaster.build_notification is disable, simply
        return.

        If config.builddmaster.notify_owner is enabled and SPR.creator
        has preferredemail it will send an email to the creator, Bcc:
        to the config.builddmaster.default_recipient. If one of the
        conditions was not satisfied, no preferredemail found (autosync
        or untouched packages from debian) or config options disabled,
        it will only send email to the specified default recipient.

        This notification will contain useful information about
        the record in question (all states are supported), see
        doc/build-notification.txt for further information.
        """

        if not config.builddmaster.send_build_notification:
            return
        if self.status == BuildStatus.FULLYBUILT:
            return

        recipients = set()

        fromaddress = format_address(
            config.builddmaster.default_sender_name,
            config.builddmaster.default_sender_address)

        extra_headers = {
            'X-Launchpad-Build-State': self.status.name,
            'X-Launchpad-Build-Component': self.current_component.name,
            'X-Launchpad-Build-Arch':
                self.distro_arch_series.architecturetag,
            }

        # XXX cprov 2006-10-27: Temporary extra debug info about the
        # SPR.creator in context, to be used during the service quarantine,
        # notify_owner will be disabled to avoid *spamming* Debian people.
        creator = self.source_package_release.creator
        extra_headers['X-Creator-Recipient'] = ",".join(
            get_contact_email_addresses(creator))

        # Currently there are 7038 SPR published in edgy which the creators
        # have no preferredemail. They are the autosync ones (creator = katie,
        # 3583 packages) and the untouched sources since we have migrated from
        # DAK (the rest). We should not spam Debian maintainers.

        # Please note that both the package creator and the package uploader
        # will be notified of failures if:
        #     * the 'notify_owner' flag is set
        #     * the package build (failure) occurred in the original
        #       archive.
        package_was_not_copied = (
            self.archive == self.source_package_release.upload_archive)

        if package_was_not_copied and config.builddmaster.notify_owner:
            if (self.archive.is_ppa and creator.inTeam(self.archive.owner)
                or
                not self.archive.is_ppa):
                # If this is a PPA, the package creator should only be
                # notified if they are the PPA owner or in the PPA team.
                # (see bug 375757)
                # Non-PPA notifications inform the creator regardless.
                recipients = recipients.union(
                    get_contact_email_addresses(creator))
            dsc_key = self.source_package_release.dscsigningkey
            if dsc_key:
                recipients = recipients.union(
                    get_contact_email_addresses(dsc_key.owner))

        # Modify notification contents according to the targeted archive.
        # 'Archive Tag', 'Subject' and 'Source URL' are customized for PPA.
        # We only send build-notifications to 'buildd-admin' celebrity for
        # main archive candidates.
        # For PPA build notifications we include the archive.owner
        # contact_address.
        if not self.archive.is_ppa:
            buildd_admins = getUtility(ILaunchpadCelebrities).buildd_admin
            recipients = recipients.union(
                get_contact_email_addresses(buildd_admins))
            archive_tag = '%s primary archive' % self.distribution.name
            subject = "[Build #%d] %s" % (self.id, self.title)
            source_url = canonical_url(self.distributionsourcepackagerelease)
        else:
            recipients = recipients.union(
                get_contact_email_addresses(self.archive.owner))
            # For PPAs we run the risk of having no available contact_address,
            # for instance, when both, SPR.creator and Archive.owner have
            # not enabled it.
            if len(recipients) == 0:
                return
            archive_tag = '%s PPA' % get_ppa_reference(self.archive)
            subject = "[Build #%d] %s (%s)" % (
                self.id, self.title, archive_tag)
            source_url = 'not available'
            extra_headers['X-Launchpad-PPA'] = get_ppa_reference(self.archive)

        # XXX cprov 2006-08-02: pending security recipients for SECURITY
        # pocket build. We don't build SECURITY yet :(

        # XXX cprov 2006-08-02: find out a way to glue parameters reported
        # with the state in the build worflow, maybe by having an
        # IBuild.statusReport property, which could also be used in the
        # respective page template.
        if self.status in [
            BuildStatus.NEEDSBUILD, BuildStatus.SUPERSEDED]:
            # untouched builds
            buildduration = 'not available'
            buildlog_url = 'not available'
            builder_url = 'not available'
        elif self.status == BuildStatus.UPLOADING:
            buildduration = 'uploading'
            buildlog_url = 'see builder page'
            builder_url = 'not available'
        elif self.status == BuildStatus.BUILDING:
            # build in process
            buildduration = 'not finished'
            buildlog_url = 'see builder page'
            builder_url = canonical_url(self.buildqueue_record.builder)
        else:
            # completed states (success and failure)
            buildduration = DurationFormatterAPI(
                self.duration).approximateduration()
            buildlog_url = self.log_url
            builder_url = canonical_url(self.builder)

        if self.status == BuildStatus.FAILEDTOUPLOAD:
            assert extra_info is not None, (
                'Extra information is required for FAILEDTOUPLOAD '
                'notifications.')
            extra_info = 'Upload log:\n%s' % extra_info
        else:
            extra_info = ''

        template = get_email_template('build-notification.txt', app='soyuz')
        replacements = {
            'source_name': self.source_package_release.name,
            'source_version': self.source_package_release.version,
            'architecturetag': self.distro_arch_series.architecturetag,
            'build_state': self.status.title,
            'build_duration': buildduration,
            'buildlog_url': buildlog_url,
            'builder_url': builder_url,
            'build_title': self.title,
            'build_url': canonical_url(self),
            'source_url': source_url,
            'extra_info': extra_info,
            'archive_tag': archive_tag,
            'component_tag': self.current_component.name,
            }
        message = template % replacements

        for toaddress in recipients:
            simple_sendmail(
                fromaddress, toaddress, subject, message,
                headers=extra_headers)