Merge lp:~cjwatson/launchpad/bpb-basemailer into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17642
Proposed branch: lp:~cjwatson/launchpad/bpb-basemailer
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/separate-build-notifications
Diff against target: 771 lines (+305/-209)
5 files modified
lib/lp/soyuz/doc/build-failedtoupload-workflow.txt (+5/-4)
lib/lp/soyuz/emailtemplates/build-notification.txt (+0/-6)
lib/lp/soyuz/mail/binarypackagebuild.py (+241/-0)
lib/lp/soyuz/model/binarypackagebuild.py (+4/-155)
lib/lp/soyuz/tests/test_build_notify.py (+55/-44)
To merge this branch: bzr merge lp:~cjwatson/launchpad/bpb-basemailer
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+264698@code.launchpad.net

Commit message

Convert BinaryPackageBuild notifications to BaseMailer.

Description of the change

Convert BinaryPackageBuild notifications to BaseMailer.

This is a better layout for mail notification code in general, and gives us X-Launchpad-Message-Rationale and X-Launchpad-Notification-Type more or less for free. I'm doing this as part of my plan to make notifications more filterable by Gmail users, which relies on making more widespread use of BaseMailer.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/doc/build-failedtoupload-workflow.txt'
2--- lib/lp/soyuz/doc/build-failedtoupload-workflow.txt 2015-07-14 11:01:53 +0000
3+++ lib/lp/soyuz/doc/build-failedtoupload-workflow.txt 2015-07-14 11:01:53 +0000
4@@ -61,9 +61,9 @@
5
6 >>> for build_notification in notifications:
7 ... build_notification['To']
8- 'celso.providelo@canonical.com'
9- 'foo.bar@canonical.com'
10- 'mark@example.com'
11+ 'Celso Providelo <celso.providelo@canonical.com>'
12+ 'Foo Bar <foo.bar@canonical.com>'
13+ 'Mark Shuttleworth <mark@example.com>'
14
15 Note that the generated notification contain the 'extra_info' content:
16
17@@ -99,7 +99,8 @@
18 If you want further information about this situation, feel free to
19 contact a member of the Launchpad Buildd Administrators team.
20 <BLANKLINE>
21- --
22+ -- =
23+ <BLANKLINE>
24 i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE
25 http://launchpad.dev/ubuntu/+source/cdrkit/1.0/+build/22
26 <BLANKLINE>
27
28=== modified file 'lib/lp/soyuz/emailtemplates/build-notification.txt'
29--- lib/lp/soyuz/emailtemplates/build-notification.txt 2015-07-14 11:01:53 +0000
30+++ lib/lp/soyuz/emailtemplates/build-notification.txt 2015-07-14 11:01:53 +0000
31@@ -14,9 +14,3 @@
32
33 If you want further information about this situation, feel free to
34 contact a member of the Launchpad Buildd Administrators team.
35-
36---
37-%(build_title)s
38-%(build_url)s
39-
40-%(reason)s
41
42=== added file 'lib/lp/soyuz/mail/binarypackagebuild.py'
43--- lib/lp/soyuz/mail/binarypackagebuild.py 1970-01-01 00:00:00 +0000
44+++ lib/lp/soyuz/mail/binarypackagebuild.py 2015-07-14 11:01:53 +0000
45@@ -0,0 +1,241 @@
46+# Copyright 2015 Canonical Ltd. This software is licensed under the
47+# GNU Affero General Public License version 3 (see the file LICENSE).
48+
49+__metaclass__ = type
50+__all__ = [
51+ 'BinaryPackageBuildMailer',
52+ ]
53+
54+from collections import OrderedDict
55+
56+from zope.component import getUtility
57+
58+from lp.app.browser.tales import DurationFormatterAPI
59+from lp.app.interfaces.launchpad import ILaunchpadCelebrities
60+from lp.archivepublisher.utils import get_ppa_reference
61+from lp.buildmaster.enums import BuildStatus
62+from lp.services.config import config
63+from lp.services.mail.basemailer import (
64+ BaseMailer,
65+ RecipientReason,
66+ )
67+from lp.services.mail.helpers import get_contact_email_addresses
68+from lp.services.mail.mailwrapper import MailWrapper
69+from lp.services.mail.sendmail import format_address
70+from lp.services.webapp import canonical_url
71+
72+
73+class BinaryPackageBuildRecipientReason(RecipientReason):
74+
75+ @classmethod
76+ def forCreator(cls, creator, recipient):
77+ header = cls.makeRationale("Creator", creator)
78+ reason = (
79+ "You are receiving this email because you created this version of "
80+ "this package.")
81+ return cls(creator, recipient, header, reason)
82+
83+ @classmethod
84+ def forSigner(cls, signer, recipient):
85+ header = cls.makeRationale("Signer", signer)
86+ reason = (
87+ "You are receiving this email because you signed this package.")
88+ return cls(signer, recipient, header, reason)
89+
90+ @classmethod
91+ def forBuilddAdmins(cls, buildd_admins, recipient):
92+ header = cls.makeRationale("Buildd-Admin", buildd_admins)
93+ # The team is always the same, so don't bother with %(lc_entity_is)s
94+ # here.
95+ reason = (
96+ "You are receiving this email because you are a buildd "
97+ "administrator.")
98+ return cls(buildd_admins, recipient, header, reason)
99+
100+ @classmethod
101+ def forArchiveOwner(cls, owner, recipient):
102+ header = cls.makeRationale("Owner", owner)
103+ reason = (
104+ "You are receiving this email because %(lc_entity_is)s the owner "
105+ "of this archive.")
106+ return cls(owner, recipient, header, reason)
107+
108+ def getReason(self):
109+ """See `RecipientReason`."""
110+ return MailWrapper(width=72).format(
111+ super(BinaryPackageBuildRecipientReason, self).getReason())
112+
113+
114+class BinaryPackageBuildMailer(BaseMailer):
115+
116+ app = 'soyuz'
117+
118+ @classmethod
119+ def forStatus(cls, build, extra_info=None):
120+ """Create a mailer for notifying about package build status.
121+
122+ :param build: The relevant build.
123+ """
124+ # Circular import.
125+ from lp.registry.model.person import get_recipients
126+
127+ recipients = OrderedDict()
128+
129+ # Currently there are 7038 SPR published in edgy which the creators
130+ # have no preferredemail. They are the autosync ones (creator = katie,
131+ # 3583 packages) and the untouched sources since we have migrated from
132+ # DAK (the rest). We should not spam Debian maintainers.
133+
134+ # Please note that both the package creator and the package uploader
135+ # will be notified of failures if:
136+ # * the 'notify_owner' flag is set
137+ # * the package build (failure) occurred in the original
138+ # archive.
139+ creator = build.source_package_release.creator
140+ package_was_not_copied = (
141+ build.archive == build.source_package_release.upload_archive)
142+
143+ if package_was_not_copied and config.builddmaster.notify_owner:
144+ if (build.archive.is_ppa and creator.inTeam(build.archive.owner)
145+ or not build.archive.is_ppa):
146+ # If this is a PPA, the package creator should only be
147+ # notified if they are the PPA owner or in the PPA team.
148+ # (see bug 375757)
149+ # Non-PPA notifications inform the creator regardless.
150+ for recipient in get_recipients(creator):
151+ if recipient not in recipients:
152+ reason = BinaryPackageBuildRecipientReason.forCreator(
153+ creator, recipient)
154+ recipients[recipient] = reason
155+ dsc_key = build.source_package_release.dscsigningkey
156+ if dsc_key:
157+ for recipient in get_recipients(dsc_key.owner):
158+ if recipient not in recipients:
159+ reason = BinaryPackageBuildRecipientReason.forSigner(
160+ dsc_key.owner, recipient)
161+ recipients[recipient] = reason
162+
163+ if not build.archive.is_ppa:
164+ buildd_admins = getUtility(ILaunchpadCelebrities).buildd_admin
165+ for recipient in get_recipients(buildd_admins):
166+ if recipient not in recipients:
167+ reason = BinaryPackageBuildRecipientReason.forBuilddAdmins(
168+ buildd_admins, recipient)
169+ recipients[recipient] = reason
170+ else:
171+ for recipient in get_recipients(build.archive.owner):
172+ if recipient not in recipients:
173+ reason = BinaryPackageBuildRecipientReason.forArchiveOwner(
174+ build.archive.owner, recipient)
175+ recipients[recipient] = reason
176+
177+ # XXX cprov 2006-08-02: pending security recipients for SECURITY
178+ # pocket build. We don't build SECURITY yet :(
179+
180+ fromaddress = format_address(
181+ config.builddmaster.default_sender_name,
182+ config.builddmaster.default_sender_address)
183+ subject = "[Build #%d] %s" % (build.id, build.title)
184+ if build.archive.is_ppa:
185+ subject += " [%s]" % build.archive.reference
186+ return cls(
187+ subject, "build-notification.txt", recipients, fromaddress, build,
188+ extra_info=extra_info)
189+
190+ def __init__(self, subject, template_name, recipients, from_address,
191+ build, extra_info=None):
192+ super(BinaryPackageBuildMailer, self).__init__(
193+ subject, template_name, recipients, from_address,
194+ notification_type="package-build-status")
195+ self.build = build
196+ self.extra_info = extra_info
197+
198+ def _getHeaders(self, email):
199+ """See `BaseMailer`."""
200+ headers = super(BinaryPackageBuildMailer, self)._getHeaders(email)
201+ build = self.build
202+ headers.update({
203+ "X-Launchpad-Archive": build.archive.reference,
204+ "X-Launchpad-Build-State": build.status.name,
205+ "X-Launchpad-Build-Component": build.current_component.name,
206+ "X-Launchpad-Build-Arch": build.distro_arch_series.architecturetag,
207+ # XXX cprov 2006-10-27: Temporary extra debug info about the
208+ # SPR.creator in context, to be used during the service
209+ # quarantine, notify_owner will be disabled to avoid *spamming*
210+ # Debian people.
211+ "X-Creator-Recipient": ",".join(get_contact_email_addresses(
212+ build.source_package_release.creator)),
213+ })
214+ # The deprecated PPA reference header is included for Ubuntu PPAs to
215+ # avoid breaking existing consumers.
216+ if (build.archive.is_ppa and
217+ build.archive.distribution.name == u'ubuntu'):
218+ headers["X-Launchpad-PPA"] = get_ppa_reference(build.archive)
219+ return headers
220+
221+ def _getTemplateParams(self, email, recipient):
222+ params = super(BinaryPackageBuildMailer, self)._getTemplateParams(
223+ email, recipient)
224+ build = self.build
225+ extra_info = self.extra_info
226+
227+ if build.archive.is_ppa:
228+ source_url = "not available"
229+ else:
230+ source_url = canonical_url(build.distributionsourcepackagerelease)
231+
232+ # XXX cprov 2006-08-02: find out a way to glue parameters reported
233+ # with the state in the build workflow, maybe by having an
234+ # IBuild.statusReport property, which could also be used in the
235+ # respective page template.
236+ if build.status in (BuildStatus.NEEDSBUILD, BuildStatus.SUPERSEDED):
237+ # untouched builds
238+ buildduration = "not available"
239+ buildlog_url = "not available"
240+ builder_url = "not available"
241+ elif build.status == BuildStatus.UPLOADING:
242+ buildduration = "uploading"
243+ buildlog_url = "see builder page"
244+ builder_url = "not available"
245+ elif build.status == BuildStatus.BUILDING:
246+ # build in process
247+ buildduration = "not finished"
248+ buildlog_url = "see builder page"
249+ builder_url = canonical_url(build.buildqueue_record.builder)
250+ else:
251+ # completed states (success and failure)
252+ buildduration = DurationFormatterAPI(
253+ build.duration).approximateduration()
254+ buildlog_url = build.log_url
255+ builder_url = canonical_url(build.builder)
256+
257+ if build.status == BuildStatus.FAILEDTOUPLOAD:
258+ assert extra_info is not None, (
259+ "Extra information is required for FAILEDTOUPLOAD "
260+ "notifications.")
261+ extra_info = "Upload log:\n%s" % extra_info
262+ else:
263+ extra_info = ""
264+
265+ params.update({
266+ "source_name": build.source_package_release.name,
267+ "source_version": build.source_package_release.version,
268+ "architecturetag": build.distro_arch_series.architecturetag,
269+ "build_state": build.status.title,
270+ "build_duration": buildduration,
271+ "buildlog_url": buildlog_url,
272+ "builder_url": builder_url,
273+ "build_title": build.title,
274+ "build_url": canonical_url(build),
275+ "source_url": source_url,
276+ "extra_info": extra_info,
277+ "archive_tag": build.archive.reference,
278+ "component_tag": build.current_component.name,
279+ })
280+ return params
281+
282+ def _getFooter(self, params):
283+ """See `BaseMailer`."""
284+ return ("%(build_title)s\n"
285+ "%(build_url)s\n\n"
286+ "%(reason)s\n" % params)
287
288=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
289--- lib/lp/soyuz/model/binarypackagebuild.py 2015-07-14 11:01:53 +0000
290+++ lib/lp/soyuz/model/binarypackagebuild.py 2015-07-14 11:01:53 +0000
291@@ -17,7 +17,6 @@
292 attrgetter,
293 itemgetter,
294 )
295-import textwrap
296
297 import apt_pkg
298 import pytz
299@@ -47,10 +46,7 @@
300 from zope.interface import implementer
301 from zope.security.proxy import removeSecurityProxy
302
303-from lp.app.browser.tales import DurationFormatterAPI
304 from lp.app.errors import NotFoundError
305-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
306-from lp.archivepublisher.utils import get_ppa_reference
307 from lp.buildmaster.enums import (
308 BuildFarmJobType,
309 BuildStatus,
310@@ -83,15 +79,6 @@
311 LibraryFileAlias,
312 LibraryFileContent,
313 )
314-from lp.services.mail.helpers import (
315- get_contact_email_addresses,
316- get_email_template,
317- )
318-from lp.services.mail.sendmail import (
319- format_address,
320- simple_sendmail,
321- )
322-from lp.services.webapp import canonical_url
323 from lp.soyuz.enums import (
324 ArchivePurpose,
325 PackagePublishingStatus,
326@@ -106,6 +93,7 @@
327 )
328 from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
329 from lp.soyuz.interfaces.packageset import IPackagesetSet
330+from lp.soyuz.mail.binarypackagebuild import BinaryPackageBuildMailer
331 from lp.soyuz.model.binarypackagename import BinaryPackageName
332 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
333 from lp.soyuz.model.files import BinaryPackageFile
334@@ -717,152 +705,13 @@
335 the record in question (all states are supported), see
336 doc/build-notification.txt for further information.
337 """
338-
339 if not config.builddmaster.send_build_notification:
340 return
341 if self.status == BuildStatus.FULLYBUILT:
342 return
343-
344- recipients = {}
345-
346- fromaddress = format_address(
347- config.builddmaster.default_sender_name,
348- config.builddmaster.default_sender_address)
349-
350- extra_headers = {
351- 'X-Launchpad-Archive': self.archive.reference,
352- 'X-Launchpad-Build-State': self.status.name,
353- 'X-Launchpad-Build-Component': self.current_component.name,
354- 'X-Launchpad-Build-Arch':
355- self.distro_arch_series.architecturetag,
356- }
357-
358- # XXX cprov 2006-10-27: Temporary extra debug info about the
359- # SPR.creator in context, to be used during the service quarantine,
360- # notify_owner will be disabled to avoid *spamming* Debian people.
361- creator = self.source_package_release.creator
362- extra_headers['X-Creator-Recipient'] = ",".join(
363- get_contact_email_addresses(creator))
364-
365- # Currently there are 7038 SPR published in edgy which the creators
366- # have no preferredemail. They are the autosync ones (creator = katie,
367- # 3583 packages) and the untouched sources since we have migrated from
368- # DAK (the rest). We should not spam Debian maintainers.
369-
370- # Please note that both the package creator and the package uploader
371- # will be notified of failures if:
372- # * the 'notify_owner' flag is set
373- # * the package build (failure) occurred in the original
374- # archive.
375- package_was_not_copied = (
376- self.archive == self.source_package_release.upload_archive)
377-
378- if package_was_not_copied and config.builddmaster.notify_owner:
379- if (self.archive.is_ppa and creator.inTeam(self.archive.owner)
380- or
381- not self.archive.is_ppa):
382- # If this is a PPA, the package creator should only be
383- # notified if they are the PPA owner or in the PPA team.
384- # (see bug 375757)
385- # Non-PPA notifications inform the creator regardless.
386- for recipient in get_contact_email_addresses(creator):
387- recipients.setdefault(
388- recipient, "you created this version of this package")
389- dsc_key = self.source_package_release.dscsigningkey
390- if dsc_key:
391- for recipient in get_contact_email_addresses(dsc_key.owner):
392- recipients.setdefault(recipient, "you signed this package")
393-
394- # Modify notification contents according to the targeted archive.
395- # 'Archive Tag', 'Subject' and 'Source URL' are customized for PPA.
396- # We only send build-notifications to 'buildd-admin' celebrity for
397- # main archive candidates.
398- # For PPA build notifications we include the archive.owner
399- # contact_address.
400- subject = "[Build #%d] %s" % (self.id, self.title)
401- if not self.archive.is_ppa:
402- buildd_admins = getUtility(ILaunchpadCelebrities).buildd_admin
403- for recipient in get_contact_email_addresses(buildd_admins):
404- recipients.setdefault(
405- recipient, "you are a buildd administrator")
406- source_url = canonical_url(self.distributionsourcepackagerelease)
407- else:
408- for recipient in get_contact_email_addresses(self.archive.owner):
409- recipients.setdefault(recipient, "you own this archive")
410- # For PPAs we run the risk of having no available contact_address,
411- # for instance, when both, SPR.creator and Archive.owner have
412- # not enabled it.
413- if len(recipients) == 0:
414- return
415- subject += ' [%s]' % self.archive.reference
416- source_url = 'not available'
417- # The deprecated PPA reference header is included for Ubuntu
418- # PPAs to avoid breaking existing consumers.
419- if self.archive.distribution.name == u'ubuntu':
420- extra_headers['X-Launchpad-PPA'] = get_ppa_reference(
421- self.archive)
422-
423- # XXX cprov 2006-08-02: pending security recipients for SECURITY
424- # pocket build. We don't build SECURITY yet :(
425-
426- # XXX cprov 2006-08-02: find out a way to glue parameters reported
427- # with the state in the build workflow, maybe by having an
428- # IBuild.statusReport property, which could also be used in the
429- # respective page template.
430- if self.status in [
431- BuildStatus.NEEDSBUILD, BuildStatus.SUPERSEDED]:
432- # untouched builds
433- buildduration = 'not available'
434- buildlog_url = 'not available'
435- builder_url = 'not available'
436- elif self.status == BuildStatus.UPLOADING:
437- buildduration = 'uploading'
438- buildlog_url = 'see builder page'
439- builder_url = 'not available'
440- elif self.status == BuildStatus.BUILDING:
441- # build in process
442- buildduration = 'not finished'
443- buildlog_url = 'see builder page'
444- builder_url = canonical_url(self.buildqueue_record.builder)
445- else:
446- # completed states (success and failure)
447- buildduration = DurationFormatterAPI(
448- self.duration).approximateduration()
449- buildlog_url = self.log_url
450- builder_url = canonical_url(self.builder)
451-
452- if self.status == BuildStatus.FAILEDTOUPLOAD:
453- assert extra_info is not None, (
454- 'Extra information is required for FAILEDTOUPLOAD '
455- 'notifications.')
456- extra_info = 'Upload log:\n%s' % extra_info
457- else:
458- extra_info = ''
459-
460- template = get_email_template('build-notification.txt', app='soyuz')
461- replacements = {
462- 'source_name': self.source_package_release.name,
463- 'source_version': self.source_package_release.version,
464- 'architecturetag': self.distro_arch_series.architecturetag,
465- 'build_state': self.status.title,
466- 'build_duration': buildduration,
467- 'buildlog_url': buildlog_url,
468- 'builder_url': builder_url,
469- 'build_title': self.title,
470- 'build_url': canonical_url(self),
471- 'source_url': source_url,
472- 'extra_info': extra_info,
473- 'archive_tag': self.archive.reference,
474- 'component_tag': self.current_component.name,
475- }
476-
477- for toaddress, reason in recipients.items():
478- replacements['reason'] = textwrap.fill(
479- 'You are receiving this email because %s.' % reason, width=72)
480- message = template % replacements
481- simple_sendmail(
482- fromaddress, toaddress, subject, message,
483- headers=extra_headers)
484+ mailer = BinaryPackageBuildMailer.forStatus(
485+ self, extra_info=extra_info)
486+ mailer.sendAll()
487
488 def _getDebByFileName(self, filename):
489 """Helper function to get a .deb LFA in the context of this build."""
490
491=== modified file 'lib/lp/soyuz/tests/test_build_notify.py'
492--- lib/lp/soyuz/tests/test_build_notify.py 2015-07-14 11:01:53 +0000
493+++ lib/lp/soyuz/tests/test_build_notify.py 2015-07-14 11:01:53 +0000
494@@ -1,10 +1,9 @@
495-# Copyright 2011-2014 Canonical Ltd. This software is licensed under the
496+# Copyright 2011-2015 Canonical Ltd. This software is licensed under the
497 # GNU Affero General Public License version 3 (see the file LICENSE).
498
499 __metaclass__ = type
500
501 from datetime import timedelta
502-from operator import itemgetter
503 from textwrap import dedent
504
505 from zope.component import getUtility
506@@ -15,6 +14,7 @@
507 from lp.buildmaster.enums import BuildStatus
508 from lp.registry.interfaces.person import IPersonSet
509 from lp.services.config import config
510+from lp.services.mail.sendmail import format_address_for_person
511 from lp.services.webapp import canonical_url
512 from lp.soyuz.enums import ArchivePurpose
513 from lp.soyuz.interfaces.publishing import PackagePublishingPocket
514@@ -36,7 +36,9 @@
515 "buildd-admin": (
516 "You are receiving this email because you are a buildd "
517 "administrator."),
518- "owner": "You are receiving this email because you own this archive.",
519+ "owner": (
520+ "You are receiving this email because you are the owner of this "
521+ "archive."),
522 }
523
524
525@@ -62,17 +64,14 @@
526 purpose=ArchivePurpose.PPA)
527 buildd_admins = getUtility(IPersonSet).getByName(
528 'launchpad-buildd-admins')
529- self.buildd_admins_email = []
530 with person_logged_in(self.admin):
531- self.ppa_owner_email = self.ppa.owner.preferredemail.email
532 self.publisher = SoyuzTestPublisher()
533 self.publisher.prepareBreezyAutotest()
534 self.distroseries.nominatedarchindep = self.das
535 self.publisher.addFakeChroots(distroseries=self.distroseries)
536 self.builder = self.factory.makeBuilder(
537 processors=[self.processor])
538- for member in buildd_admins.activemembers:
539- self.buildd_admins_email.append(member.preferredemail.email)
540+ self.buildd_admins_members = list(buildd_admins.activemembers)
541 self.builds = []
542
543 def create_builds(self, archive):
544@@ -101,7 +100,17 @@
545 ppa=False):
546 # Assert that the mail sent (which is in notification), matches
547 # the data from the build
548- self.assertEqual(recipient, notification['To'])
549+ self.assertEqual(
550+ format_address_for_person(recipient), notification['To'])
551+ if reason == "buildd-admin":
552+ rationale = "Buildd-Admin @launchpad-buildd-admins"
553+ else:
554+ rationale = reason.title()
555+ self.assertEqual(
556+ rationale, notification['X-Launchpad-Message-Rationale'])
557+ self.assertEqual(
558+ 'package-build-status',
559+ notification['X-Launchpad-Notification-Type'])
560 self.assertEqual(
561 'test@example.com', notification['X-Creator-Recipient'])
562 self.assertEqual(
563@@ -155,20 +164,21 @@
564 If you want further information about this situation, feel free to
565 contact a member of the Launchpad Buildd Administrators team.
566
567- --
568+ %s
569 %s
570 %s
571 """ % (
572 build.source_package_release.sourcepackagename.name,
573 build.source_package_release.version, self.das.architecturetag,
574 build.archive.reference, build.status.title, duration, build_log,
575- builder, source, build.title, canonical_url(build)))
576+ builder, source, "-- ", build.title, canonical_url(build)))
577 expected_body += "\n" + REASONS[reason] + "\n"
578 self.assertEqual(expected_body, body)
579
580 def _assert_mails_are_correct(self, build, reasons, ppa=False):
581 notifications = pop_notifications()
582- reasons = sorted(reasons, key=itemgetter(0))
583+ reasons = sorted(
584+ reasons, key=lambda r: format_address_for_person(r[0]))
585 for notification, (recipient, reason) in zip(notifications, reasons):
586 self._assert_mail_is_correct(
587 build, notification, recipient, reason, ppa=ppa)
588@@ -180,8 +190,8 @@
589 build = self.builds[BuildStatus.FAILEDTOBUILD.value]
590 build.notify()
591 expected_reasons = [
592- (email, "buildd-admin") for email in self.buildd_admins_email]
593- expected_reasons.append(("test@example.com", "creator"))
594+ (person, "buildd-admin") for person in self.buildd_admins_members]
595+ expected_reasons.append((self.creator, "creator"))
596 self._assert_mails_are_correct(build, expected_reasons)
597
598 def test_notify_failed_to_build_ppa(self):
599@@ -191,8 +201,8 @@
600 build = self.builds[BuildStatus.FAILEDTOBUILD.value]
601 build.notify()
602 expected_reasons = [
603- ("test@example.com", "signer"),
604- (self.ppa_owner_email, "owner"),
605+ (self.creator, "signer"),
606+ (self.ppa.owner, "owner"),
607 ]
608 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
609
610@@ -203,8 +213,8 @@
611 build = self.builds[BuildStatus.NEEDSBUILD.value]
612 build.notify()
613 expected_reasons = [
614- (email, "buildd-admin") for email in self.buildd_admins_email]
615- expected_reasons.append(("test@example.com", "creator"))
616+ (person, "buildd-admin") for person in self.buildd_admins_members]
617+ expected_reasons.append((self.creator, "creator"))
618 self._assert_mails_are_correct(build, expected_reasons)
619
620 def test_notify_needs_building_ppa(self):
621@@ -214,8 +224,8 @@
622 build = self.builds[BuildStatus.NEEDSBUILD.value]
623 build.notify()
624 expected_reasons = [
625- ("test@example.com", "signer"),
626- (self.ppa_owner_email, "owner"),
627+ (self.creator, "signer"),
628+ (self.ppa.owner, "owner"),
629 ]
630 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
631
632@@ -233,8 +243,8 @@
633 build = self.builds[BuildStatus.MANUALDEPWAIT.value]
634 build.notify()
635 expected_reasons = [
636- (email, "buildd-admin") for email in self.buildd_admins_email]
637- expected_reasons.append(("test@example.com", "creator"))
638+ (person, "buildd-admin") for person in self.buildd_admins_members]
639+ expected_reasons.append((self.creator, "creator"))
640 self._assert_mails_are_correct(build, expected_reasons)
641
642 def test_notify_dependency_wait_ppa(self):
643@@ -244,8 +254,8 @@
644 build = self.builds[BuildStatus.MANUALDEPWAIT.value]
645 build.notify()
646 expected_reasons = [
647- ("test@example.com", "signer"),
648- (self.ppa_owner_email, "owner"),
649+ (self.creator, "signer"),
650+ (self.ppa.owner, "owner"),
651 ]
652 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
653
654@@ -256,8 +266,8 @@
655 build = self.builds[BuildStatus.CHROOTWAIT.value]
656 build.notify()
657 expected_reasons = [
658- (email, "buildd-admin") for email in self.buildd_admins_email]
659- expected_reasons.append(("test@example.com", "creator"))
660+ (person, "buildd-admin") for person in self.buildd_admins_members]
661+ expected_reasons.append((self.creator, "creator"))
662 self._assert_mails_are_correct(build, expected_reasons)
663
664 def test_notify_chroot_problem_ppa(self):
665@@ -267,8 +277,8 @@
666 build = self.builds[BuildStatus.CHROOTWAIT.value]
667 build.notify()
668 expected_reasons = [
669- ("test@example.com", "signer"),
670- (self.ppa_owner_email, "owner"),
671+ (self.creator, "signer"),
672+ (self.ppa.owner, "owner"),
673 ]
674 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
675
676@@ -280,8 +290,8 @@
677 build = self.builds[BuildStatus.SUPERSEDED.value]
678 build.notify()
679 expected_reasons = [
680- (email, "buildd-admin") for email in self.buildd_admins_email]
681- expected_reasons.append(("test@example.com", "creator"))
682+ (person, "buildd-admin") for person in self.buildd_admins_members]
683+ expected_reasons.append((self.creator, "creator"))
684 self._assert_mails_are_correct(build, expected_reasons)
685
686 def test_notify_build_for_superseded_source_ppa(self):
687@@ -292,8 +302,8 @@
688 build = self.builds[BuildStatus.SUPERSEDED.value]
689 build.notify()
690 expected_reasons = [
691- ("test@example.com", "signer"),
692- (self.ppa_owner_email, "owner"),
693+ (self.creator, "signer"),
694+ (self.ppa.owner, "owner"),
695 ]
696 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
697
698@@ -304,8 +314,8 @@
699 build = self.builds[BuildStatus.BUILDING.value]
700 build.notify()
701 expected_reasons = [
702- (email, "buildd-admin") for email in self.buildd_admins_email]
703- expected_reasons.append(("test@example.com", "creator"))
704+ (person, "buildd-admin") for person in self.buildd_admins_members]
705+ expected_reasons.append((self.creator, "creator"))
706 self._assert_mails_are_correct(build, expected_reasons)
707
708 def test_notify_currently_building_ppa(self):
709@@ -315,8 +325,8 @@
710 build = self.builds[BuildStatus.BUILDING.value]
711 build.notify()
712 expected_reasons = [
713- ("test@example.com", "signer"),
714- (self.ppa_owner_email, "owner"),
715+ (self.creator, "signer"),
716+ (self.ppa.owner, "owner"),
717 ]
718 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
719
720@@ -327,8 +337,8 @@
721 build = self.builds[BuildStatus.UPLOADING.value]
722 build.notify()
723 expected_reasons = [
724- (email, "buildd-admin") for email in self.buildd_admins_email]
725- expected_reasons.append(("test@example.com", "creator"))
726+ (person, "buildd-admin") for person in self.buildd_admins_members]
727+ expected_reasons.append((self.creator, "creator"))
728 self._assert_mails_are_correct(build, expected_reasons)
729
730 def test_notify_uploading_build_ppa(self):
731@@ -338,8 +348,8 @@
732 build = self.builds[BuildStatus.UPLOADING.value]
733 build.notify()
734 expected_reasons = [
735- ("test@example.com", "signer"),
736- (self.ppa_owner_email, "owner"),
737+ (self.creator, "signer"),
738+ (self.ppa.owner, "owner"),
739 ]
740 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
741
742@@ -354,7 +364,7 @@
743 [ppa_build] = ppa_spph.createMissingBuilds()
744 ppa_build.notify()
745 self._assert_mails_are_correct(
746- ppa_build, [(self.ppa_owner_email, "owner")], ppa=True)
747+ ppa_build, [(self.ppa.owner, "owner")], ppa=True)
748
749 def test_notify_owner_suppresses_mail(self):
750 # When the 'notify_owner' config option is False, we don't send mail
751@@ -370,7 +380,8 @@
752 build.notify()
753 self._assert_mails_are_correct(
754 build,
755- [(email, "buildd-admin") for email in self.buildd_admins_email])
756+ [(person, "buildd-admin")
757+ for person in self.buildd_admins_members])
758 # And undo what we just did.
759 config.pop('notify_owner')
760
761@@ -402,7 +413,7 @@
762 removeSecurityProxy(spr).dscsigningkey = key
763 build.notify()
764 expected_reasons = [
765- (email, "buildd-admin") for email in self.buildd_admins_email]
766- expected_reasons.append(("test@example.com", "creator"))
767- expected_reasons.append(("sponsor@example.com", "signer"))
768+ (person, "buildd-admin") for person in self.buildd_admins_members]
769+ expected_reasons.append((self.creator, "creator"))
770+ expected_reasons.append((sponsor, "signer"))
771 self._assert_mails_are_correct(build, expected_reasons)