Merge lp:~cjwatson/launchpad/bpb-basemailer into lp:launchpad
- bpb-basemailer
- Merge into devel
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 |
Related bugs: |
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-
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) |