Merge lp:~abentley/launchpad/build-mail3 into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 12275
Proposed branch: lp:~abentley/launchpad/build-mail3
Merge into: lp:launchpad
Prerequisite: lp:~abentley/launchpad/build-mail2
Diff against target: 1653 lines (+671/-619)
7 files modified
database/schema/security.cfg (+1/-0)
lib/lp/archiveuploader/tests/nascentupload-announcements.txt (+608/-592)
lib/lp/archiveuploader/tests/test_uploadprocessor.py (+28/-5)
lib/lp/archiveuploader/uploadprocessor.py (+6/-4)
lib/lp/soyuz/model/binarypackagebuild.py (+2/-0)
lib/lp/soyuz/model/queue.py (+20/-3)
lib/lp/soyuz/tests/test_build_notify.py (+6/-15)
To merge this branch: bzr merge lp:~abentley/launchpad/build-mail3
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
j.c.sackett (community) code* Approve
Review via email: mp+47679@code.launchpad.net

Commit message

[r=bac,jcsackett][ui=none][bug=613958,681125] Fix recipe build mail.

Description of the change

= Summary =
Fix bugs:

Bug #681125: Async uploader sends recipe mail as if it were source package
uploads

Bug #613958: upload failure emails should include the upload log

== Proposed fix ==
Extend queue to skip notifications for recipe build-based uploads, not only
binary build-based uploads.

Move responsibility for deciding whether to notify on success to the Build
itself.

The upload log is now attached to the build before sending emails.

== Pre-implementation notes ==
Discussed with thumper

== Implementation details ==
Much doctest lint was cleaned up.
Some BinaryBuild tests were adjusted to reflect the fact that emails are never
sent for successful binary builds.

== Tests ==
bin/test -vt testSourcePackageRecipeBuild_success_mail -t testSourcePackageRecipeBuild_fail_mail -t test_notify_successfully_built -t test_notify_owner_supresses_mail -t test_sponsored_upload_notification -t nascentupload-announcements.txt

== Demo and Q/A ==
Create a sourcepackage recipe. Do a successful build. You should get an email
with a subject line like "Successfully built".

Do a build with the same version number as the previous one. You should get an
email like "[recipe build #8558] of ~abentley wakeonlan-lucid in maverick:
Failed to upload", but not one like "[PPA abentley-test]
wakeonlan_0+4+2~maverick1_source.changes rejected".

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/archiveuploader/uploadprocessor.py
  lib/lp/archiveuploader/tests/test_ppauploadprocessor.py
  lib/lp/soyuz/model/queue.py
  lib/lp/archiveuploader/tests/test_recipeuploads.py
  lib/lp/archiveuploader/tests/nascentupload-announcements.txt
  database/schema/security.cfg
  lib/lp/soyuz/tests/test_build_notify.py
  lib/lp/archiveuploader/tests/test_buildduploads.py
  lib/lp/archiveuploader/tests/test_uploadprocessor.py
  lib/lp/soyuz/model/binarypackagebuild.py

./lib/lp/archiveuploader/tests/test_ppauploadprocessor.py
     873: E301 expected 1 blank line, found 2

^^^ due to a weirdly-positioned comment

./lib/lp/archiveuploader/tests/nascentupload-announcements.txt
     191: want exceeds 78 characters.
     192: want exceeds 78 characters.
     718: want exceeds 78 characters.
     720: want exceeds 78 characters.
     766: want exceeds 78 characters.
     768: want exceeds 78 characters.

^^^ cannot be fixed because tests specify strict whitespace handling
./database/schema/security.cfg
     704: Line exceeds 78 characters.
     705: Line exceeds 78 characters.
     706: Line exceeds 78 characters.
     732: Line exceeds 78 characters.
     736: Line exceeds 78 characters.
     791: Line exceeds 78 characters.
     804: Line exceeds 78 characters.
     805: Line exceeds 78 characters.
     822: Line exceeds 78 characters.
     823: Line exceeds 78 characters.
     824: Line exceeds 78 characters.
     825: Line exceeds 78 characters.
     826: Line exceeds 78 characters.
     878: Line exceeds 78 characters.
     879: Line exceeds 78 characters.
     880: Line exceeds 78 characters.
     910: Line exceeds 78 characters.
     991: Line exceeds 78 characters.
    1001: Line exceeds 78 characters.
    1002: Line exceeds 78 characters.

^^^ 78-char limit does not apply here AFAIK

./lib/lp/archiveuploader/tests/test_uploadprocessor.py
    1263: E301 expected 1 blank line, found 2

^^^ due to a weirdly-positioned comment

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Aaron--

This looks pretty good. I have one commenting suggestion below which isn't terribly important, and one potentially important performance question at the bottom of the diff below.

> === modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
> --- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-01-27 16:13:58 +0000
> +++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-01-27 16:13:59 +0000
> @@ -2012,11 +2022,23 @@
> BuildUploadHandler(self.uploadprocessor, self.incoming_folder,
> leaf_name).process()
> self.layer.txn.commit()
> + return build
> +
> + def testSourcePackageRecipeBuild_fail(self):
> + build = self.doFailureRecipeBuild()
> self.assertEquals(BuildStatus.FAILEDTOUPLOAD, build.status)
> self.assertEquals(None, build.builder)
> self.assertIsNot(None, build.duration)
> self.assertIsNot(None, build.upload_log)
>
> + def testSourcePackageRecipeBuild_fail_mail(self):
> + self.doFailureRecipeBuild()
> + (mail,) = pop_notifications()
> + subject = mail['Subject'].replace('\n\t', ' ')
> + self.assertIn('Failed to upload', subject)
> + body = mail.get_payload(decode=True)
> + self.assertIn('Upload Log: http', body)
> +
> def testBuildWithInvalidStatus(self):
> # Builds with an invalid (non-UPLOADING) status should trigger
> # a warning but be left alone.

A comment to the effect
+ # When a recipe fails it should include the log in the mail.
in testSourcePackageRecipeBuild_fail_mail would be good.

> @@ -192,9 +191,23 @@
> # PackageUploadSource objects which are related.
> sources = SQLMultipleJoin('PackageUploadSource',
> joinColumn='packageupload')
> + # Does not include source builds.
> builds = SQLMultipleJoin('PackageUploadBuild',
> joinColumn='packageupload')
>
> + def getSourceBuild(self):
> + #avoid circular import
> + from lp.code.model.sourcepackagerecipebuild import (
> + SourcePackageRecipeBuild)
> + from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
> + return Store.of(self).find(
> + SourcePackageRecipeBuild,
> + SourcePackageRecipeBuild.id ==
> + SourcePackageRelease.source_package_recipe_build_id,
> + SourcePackageRelease.id ==
> + PackageUploadSource.sourcepackagereleaseID,
> + PackageUploadSource.packageupload == self.id).one()
> +
> # Also the custom files associated with the build.
> customfiles = SQLMultipleJoin('PackageUploadCustom',
> joinColumn='packageupload')
> @@ -488,6 +501,10 @@
> """See `IPackageUpload`."""
> return self.builds
>
> + @cachedproperty
> + def from_build(self):
> + return bool(self.builds) or self.getSourceBuild()
> +

Is there any chance of a cold cache use of this method causing performance issues? I'm not saying it will, just asking. I have no idea how large the query fired for this could be.

review: Needs Information (code*)
Revision history for this message
j.c.sackett (jcsackett) wrote :

After talking in IRC, my performance question has been addressed.

11:57 AM jcsackett
abentley: builds is a SQLMultipleJoin; i'm wondering if when self.build gets called we may return a huge rowset in some cases.
11:57 AM abentley
jcsackett, as I said, it's always one or zero results.
11:57 AM jcsackett
abenltey, ah! i thought you meant from_build was called one or zero times.
11:57 AM abentley
jcsackett, there is a comment in the code saying it shouldn't be a multiple join.
11:59 AM jcsackett
abentley: yes, i see now.
11:59 AM
apologies for the confusion.

review: Approve (code*)
Revision history for this message
Brad Crittenden (bac) wrote :

Code and review look fine. Thanks to you both.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2010-12-02 16:15:46 +0000
+++ database/schema/security.cfg 2011-01-27 17:17:51 +0000
@@ -1363,6 +1363,7 @@
1363public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE1363public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE
1364public.distributionsourcepackage = SELECT, INSERT, UPDATE1364public.distributionsourcepackage = SELECT, INSERT, UPDATE
1365public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE1365public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE
1366public.sourcepackagerecipebuild = SELECT
1366public.sourcepackagerecipebuildjob = SELECT, INSERT, UPDATE1367public.sourcepackagerecipebuildjob = SELECT, INSERT, UPDATE
1367public.component = SELECT1368public.component = SELECT
1368public.section = SELECT1369public.section = SELECT
13691370
=== modified file 'lib/lp/archiveuploader/tests/nascentupload-announcements.txt'
--- lib/lp/archiveuploader/tests/nascentupload-announcements.txt 2010-12-23 12:16:57 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-announcements.txt 2011-01-27 17:17:51 +0000
@@ -1,4 +1,5 @@
1= NascentUpload Announcements =1NascentUpload Announcements
2===========================
23
3NascentUpload announces uploads according its final status (NEW,4NascentUpload announces uploads according its final status (NEW,
4AUTO-APPROVED, UNAPPROVED) and its destination pocket:5AUTO-APPROVED, UNAPPROVED) and its destination pocket:
@@ -32,272 +33,275 @@
3233
33We need to be logged into the security model in order to get any further34We need to be logged into the security model in order to get any further
3435
35 >>> from canonical.testing.layers import LaunchpadZopelessLayer36 >>> from canonical.testing.layers import LaunchpadZopelessLayer
36 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')37 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
37 >>> login('foo.bar@canonical.com')38 >>> login('foo.bar@canonical.com')
3839
39Helper functions to examine emails that were sent:40Helper functions to examine emails that were sent:
4041
41 >>> from lp.services.mail import stub42 >>> from lp.services.mail import stub
42 >>> from lp.testing.mail_helpers import pop_notifications43 >>> from lp.testing.mail_helpers import pop_notifications
4344
44 >>> def by_to_addrs(a, b):45 >>> def by_to_addrs(a, b):
45 ... return cmp(a[1], b[1])46 ... return cmp(a[1], b[1])
4647
47 >>> def print_addrlist(field):48 >>> def print_addrlist(field):
48 ... for entry in field.split(','):49 ... for entry in field.split(','):
49 ... print entry.strip()50 ... print entry.strip()
5051
51Import the test keys to use 'insecure' policy.52Import the test keys to use 'insecure' policy.
5253
53 >>> from canonical.launchpad.ftests import import_public_test_keys54 >>> from canonical.launchpad.ftests import import_public_test_keys
54 >>> import_public_test_keys()55 >>> import_public_test_keys()
5556
56For the purpose of this test, hoary needs to be an open (development)57For the purpose of this test, hoary needs to be an open (development)
57distroseries so that we can upload to it. Also adjust 'changeslist'58distroseries so that we can upload to it. Also adjust 'changeslist'
58address and allow uploads to universe:59address and allow uploads to universe:
5960
60 >>> from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet61 >>> from canonical.launchpad.interfaces.librarian import (
61 >>> from lp.registry.interfaces.distribution import IDistributionSet62 ... ILibraryFileAliasSet,
62 >>> from lp.registry.interfaces.series import SeriesStatus63 ... )
63 >>> ubuntu = getUtility(IDistributionSet)['ubuntu']64 >>> from lp.registry.interfaces.distribution import IDistributionSet
64 >>> hoary = ubuntu['hoary']65 >>> from lp.registry.interfaces.series import SeriesStatus
65 >>> hoary.status = SeriesStatus.DEVELOPMENT66 >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
66 >>> hoary.changeslist = "hoary-announce@lists.ubuntu.com"67 >>> hoary = ubuntu['hoary']
67 >>> fake_chroot = getUtility(ILibraryFileAliasSet)[1]68 >>> hoary.status = SeriesStatus.DEVELOPMENT
68 >>> trash = hoary['hppa'].addOrUpdateChroot(fake_chroot)69 >>> hoary.changeslist = "hoary-announce@lists.ubuntu.com"
70 >>> fake_chroot = getUtility(ILibraryFileAliasSet)[1]
71 >>> trash = hoary['hppa'].addOrUpdateChroot(fake_chroot)
6972
70NEW source upload to RELEASE pocket via 'sync' policy (it presents73NEW source upload to RELEASE pocket via 'sync' policy (it presents
71the same behaviour than using insecure policy, apart from allowing74the same behaviour than using insecure policy, apart from allowing
72unsigned changes):75unsigned changes):
7376
74 >>> from lp.archiveuploader.nascentupload import NascentUpload77 >>> from lp.archiveuploader.nascentupload import NascentUpload
75 >>> from lp.archiveuploader.tests import datadir, getPolicy78 >>> from lp.archiveuploader.tests import datadir, getPolicy
7679
77 >>> sync_policy = getPolicy(80 >>> sync_policy = getPolicy(
78 ... name='sync', distro='ubuntu', distroseries='hoary')81 ... name='sync', distro='ubuntu', distroseries='hoary')
7982
80 >>> from lp.services.log.logger import DevNullLogger83 >>> from lp.services.log.logger import DevNullLogger
81 >>> bar_src = NascentUpload.from_changesfile_path(84 >>> bar_src = NascentUpload.from_changesfile_path(
82 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),85 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),
83 ... sync_policy, DevNullLogger())86 ... sync_policy, DevNullLogger())
84 >>> bar_src.process()87 >>> bar_src.process()
8588
86 >>> from lp.services.log.logger import FakeLogger89 >>> from lp.services.log.logger import FakeLogger
87 >>> bar_src.logger = FakeLogger()90 >>> bar_src.logger = FakeLogger()
88 >>> result = bar_src.do_accept()91 >>> result = bar_src.do_accept()
89 DEBUG Creating queue entry92 DEBUG Creating queue entry
90 ...93 ...
91 DEBUG Sent a mail:94 DEBUG Sent a mail:
92 ...95 ...
93 DEBUG NEW: bar_1.0.orig.tar.gz96 DEBUG NEW: bar_1.0.orig.tar.gz
94 DEBUG NEW: bar_1.0-1.diff.gz97 DEBUG NEW: bar_1.0-1.diff.gz
95 DEBUG NEW: bar_1.0-1.dsc98 DEBUG NEW: bar_1.0-1.dsc
96 ...99 ...
97 DEBUG above if files already exist in other distroseries.100 DEBUG above if files already exist in other distroseries.
98 ...101 ...
99 DEBUG --102 DEBUG --
100 DEBUG You are receiving this email because you are the uploader, maintainer or103 DEBUG You are receiving this email because you are the uploader,
101 DEBUG signer of the above package.104 maintainer or
105 DEBUG signer of the above package.
102106
103There is only one email generated:107There is only one email generated:
104108
105 >>> [notification] = pop_notifications()109 >>> [notification] = pop_notifications()
106110
107 >>> notification['X-Katie']111 >>> notification['X-Katie']
108 'Launchpad actually'112 'Launchpad actually'
109113
110 >>> notification['Subject']114 >>> notification['Subject']
111 '[ubuntu/hoary] bar 1.0-1 (New)'115 '[ubuntu/hoary] bar 1.0-1 (New)'
112116
113 >>> print_addrlist(notification['To'])117 >>> print_addrlist(notification['To'])
114 Daniel Silverstone <daniel.silverstone@canonical.com>118 Daniel Silverstone <daniel.silverstone@canonical.com>
115119
116Let's ACCEPT bar sources in order to make the next uploads of this120Let's ACCEPT bar sources in order to make the next uploads of this
117series *known* in hoary:121series *known* in hoary:
118122
119 >>> bar_src.queue_root.setAccepted()123 >>> bar_src.queue_root.setAccepted()
120 >>> pub_records = bar_src.queue_root.realiseUpload()124 >>> pub_records = bar_src.queue_root.realiseUpload()
121125
122Make the uploaded orig file available to librarian lookups126Make the uploaded orig file available to librarian lookups
123127
124 >>> import transaction128 >>> import transaction
125 >>> transaction.commit()129 >>> transaction.commit()
126130
127Ensure the previous transaction is *really* in the database before the next131Ensure the previous transaction is *really* in the database before the next
128test:132test:
129133
130 >>> from canonical.launchpad.ftests import syncUpdate134 >>> from canonical.launchpad.ftests import syncUpdate
131 >>> syncUpdate(bar_src.queue_root)135 >>> syncUpdate(bar_src.queue_root)
132136
133Uploading the same package again will result in a rejection email:137Uploading the same package again will result in a rejection email:
134138
135 >>> bar_src = NascentUpload.from_changesfile_path(139 >>> bar_src = NascentUpload.from_changesfile_path(
136 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),140 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),
137 ... sync_policy, DevNullLogger())141 ... sync_policy, DevNullLogger())
138 >>> bar_src.process()142 >>> bar_src.process()
139143
140 >>> bar_src.logger = FakeLogger()144 >>> bar_src.logger = FakeLogger()
141 >>> result = bar_src.do_accept()145 >>> result = bar_src.do_accept()
142 DEBUG Creating queue entry146 DEBUG Creating queue entry
143 DEBUG bar diff from 1.0-1 to 1.0-1 requested147 DEBUG bar diff from 1.0-1 to 1.0-1 requested
144 DEBUG Setting it to ACCEPTED148 DEBUG Setting it to ACCEPTED
145 ...149 ...
146 DEBUG Sending rejection email.150 DEBUG Sending rejection email.
147 ...151 ...
148 DEBUG Rejected:152 DEBUG Rejected:
149 DEBUG The source bar - 1.0-1 is already accepted in ubuntu/hoary153 DEBUG The source bar - 1.0-1 is already accepted in ubuntu/hoary
150 and you cannot upload the same version within the same154 and you cannot upload the same version within the same
151 distribution. You have to modify the source version and155 distribution. You have to modify the source version and
152 re-upload.156 re-upload.
153 ...157 ...
154158
155 >>> [notification] = pop_notifications()159 >>> [notification] = pop_notifications()
156160
157 >>> notification['X-Katie']161 >>> notification['X-Katie']
158 'Launchpad actually'162 'Launchpad actually'
159163
160 >>> print_addrlist(notification['To'])164 >>> print_addrlist(notification['To'])
161 Daniel Silverstone <daniel.silverstone@canonical.com>165 Daniel Silverstone <daniel.silverstone@canonical.com>
162166
163Upload notifications from primary archive do not contain the167Upload notifications from primary archive do not contain the
164'X-Launchpad-PPA' header, since it doesn't apply to this context.168'X-Launchpad-PPA' header, since it doesn't apply to this context.
165169
166 >>> 'X-Launchpad-PPA' in notification.keys()170 >>> 'X-Launchpad-PPA' in notification.keys()
167 False171 False
168172
169Notifications for source uploads will contain the 'X-Launchpad-Component'173Notifications for source uploads will contain the 'X-Launchpad-Component'
170header however.174header however.
171175
172 >>> 'X-Launchpad-Component' in notification.keys()176 >>> 'X-Launchpad-Component' in notification.keys()
173 True177 True
174178
175 >>> notification['X-Launchpad-Component']179 >>> notification['X-Launchpad-Component']
176 'component=universe, section=devel'180 'component=universe, section=devel'
177181
178This is the body of the rejection email.182This is the body of the rejection email.
179183
180 >>> body = notification.get_payload()[0]184 >>> body = notification.get_payload()[0]
181 >>> print body.as_string() # doctest: -NORMALIZE_WHITESPACE185 >>> print body.as_string() # doctest: -NORMALIZE_WHITESPACE
182 Content-Type: text/plain; charset="utf-8"186 Content-Type: text/plain; charset="utf-8"
183 MIME-Version: 1.0187 MIME-Version: 1.0
184 Content-Transfer-Encoding: quoted-printable188 Content-Transfer-Encoding: quoted-printable
185 <BLANKLINE>189 <BLANKLINE>
186 Rejected:190 Rejected:
187 The source bar - 1.0-1 is already accepted in ubuntu/hoary and you cannot u=191 The source bar - 1.0-1 is already accepted in ubuntu/hoary and you cannot u=
188 pload the same version within the same distribution. You have to modify the=192 pload the same version within the same distribution. You have to modify the=
189 source version and re-upload.193 source version and re-upload.
190 <BLANKLINE>194 <BLANKLINE>
191 bar (1.0-1) breezy; urgency=3Dlow195 bar (1.0-1) breezy; urgency=3Dlow
192 <BLANKLINE>196 <BLANKLINE>
193 * Initial version197 * Initial version
194 <BLANKLINE>198 <BLANKLINE>
195 Date: Thu, 16 Feb 2006 15:34:09 +0000199 Date: Thu, 16 Feb 2006 15:34:09 +0000
196 Changed-By: Daniel Silverstone <daniel.silverstone@canonical.com>200 Changed-By: Daniel Silverstone <daniel.silverstone@canonical.com>
197 Maintainer: Launchpad team <launchpad@lists.canonical.com>201 Maintainer: Launchpad team <launchpad@lists.canonical.com>
198 <BLANKLINE>202 <BLANKLINE>
199 =3D=3D=3D203 =3D=3D=3D
200 <BLANKLINE>204 <BLANKLINE>
201 If you don't understand why your files were rejected, or if the205 If you don't understand why your files were rejected, or if the
202 override file requires editing, please go to:206 override file requires editing, please go to:
203 http://answers.launchpad.net/soyuz207 http://answers.launchpad.net/soyuz
204 <BLANKLINE>208 <BLANKLINE>
205 -- =209 -- =
206 <BLANKLINE>210 <BLANKLINE>
207 You are receiving this email because you are the uploader, maintainer or211 You are receiving this email because you are the uploader, maintainer or
208 signer of the above package.212 signer of the above package.
209 <BLANKLINE>213 <BLANKLINE>
210214
211In order to facilitate automated processing of announcement emails, the215In order to facilitate automated processing of announcement emails, the
212changes file is enclosed as an attachment.216changes file is enclosed as an attachment.
213217
214 >>> attachment = notification.get_payload()[1]218 >>> attachment = notification.get_payload()[1]
215 >>> print attachment.as_string() # doctest: -NORMALIZE_WHITESPACE219 >>> print attachment.as_string() # doctest: -NORMALIZE_WHITESPACE
216 Content-Type: text/plain; charset="utf-8"220 Content-Type: text/plain; charset="utf-8"
217 MIME-Version: 1.0221 MIME-Version: 1.0
218 Content-Transfer-Encoding: quoted-printable222 Content-Transfer-Encoding: quoted-printable
219 Content-Disposition: attachment; filename="changesfile"223 Content-Disposition: attachment; filename="changesfile"
220 <BLANKLINE>224 <BLANKLINE>
221 -----BEGIN PGP SIGNED MESSAGE-----225 -----BEGIN PGP SIGNED MESSAGE-----
222 Hash: SHA1226 Hash: SHA1
223 <BLANKLINE>227 <BLANKLINE>
224 Format: 1.7228 Format: 1.7
225 Date: Thu, 16 Feb 2006 15:34:09 +0000229 Date: Thu, 16 Feb 2006 15:34:09 +0000
226 Source: bar230 Source: bar
227 Binary: bar231 Binary: bar
228 Architecture: source232 Architecture: source
229 Version: 1.0-1233 Version: 1.0-1
230 Distribution: breezy234 Distribution: breezy
231 Urgency: low235 Urgency: low
232 Maintainer: Launchpad team <launchpad@lists.canonical.com>236 Maintainer: Launchpad team <launchpad@lists.canonical.com>
233 Changed-By: Daniel Silverstone <daniel.silverstone@canonical.com>237 Changed-By: Daniel Silverstone <daniel.silverstone@canonical.com>
234 Description: =238 Description: =
235 <BLANKLINE>239 <BLANKLINE>
236 bar - Stuff for testing240 bar - Stuff for testing
237 Changes: =241 Changes: =
238 <BLANKLINE>242 <BLANKLINE>
239 bar (1.0-1) breezy; urgency=3Dlow243 bar (1.0-1) breezy; urgency=3Dlow
240 .244 .
241 * Initial version245 * Initial version
242 Files: =246 Files: =
243 <BLANKLINE>247 <BLANKLINE>
244 5d533778b698edc1a122098a98c8490e 512 devel optional bar_1.0-1.dsc248 5d533778b698edc1a122098a98c8490e 512 devel optional bar_1.0-1.dsc
245 fc1464e5985b962a042d5354452f361d 164 devel optional bar_1.0.orig.tar.gz249 fc1464e5985b962a042d5354452f361d 164 devel optional bar_1.0.orig.tar.gz
246 1e35b810764f140af9616de8274e6e73 537 devel optional bar_1.0-1.diff.gz250 1e35b810764f140af9616de8274e6e73 537 devel optional bar_1.0-1.diff.gz
247 <BLANKLINE>251 <BLANKLINE>
248 -----BEGIN PGP SIGNATURE-----252 -----BEGIN PGP SIGNATURE-----
249 Version: GnuPG v1.4.3 (GNU/Linux)253 Version: GnuPG v1.4.3 (GNU/Linux)
250 <BLANKLINE>254 <BLANKLINE>
251 iD8DBQFFt7D9jn63CGxkqMURAk1BAJwIQfOMS+l9lDDwPORtuZb3hFI2OgCaArNc255 iD8DBQFFt7D9jn63CGxkqMURAk1BAJwIQfOMS+l9lDDwPORtuZb3hFI2OgCaArNc
252 oH5uIHeKtedJa5Ekpcfi2bY=3D256 oH5uIHeKtedJa5Ekpcfi2bY=3D
253 =3DDMqI257 =3DDMqI
254 -----END PGP SIGNATURE-----258 -----END PGP SIGNATURE-----
255 <BLANKLINE>259 <BLANKLINE>
256260
257A PPA upload will contain the X-Launchpad-PPA header.261A PPA upload will contain the X-Launchpad-PPA header.
258262
259 >>> from lp.registry.interfaces.distribution import IDistributionSet263 >>> from lp.registry.interfaces.distribution import IDistributionSet
260 >>> from lp.registry.interfaces.person import IPersonSet264 >>> from lp.registry.interfaces.person import IPersonSet
261 >>> from lp.soyuz.enums import ArchivePurpose265 >>> from lp.soyuz.enums import ArchivePurpose
262 >>> from lp.soyuz.interfaces.archive import IArchiveSet266 >>> from lp.soyuz.interfaces.archive import IArchiveSet
263267
264 >>> ubuntu = getUtility(IDistributionSet)['ubuntu']268 >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
265 >>> name16 = getUtility(IPersonSet).getByName('name16')269 >>> name16 = getUtility(IPersonSet).getByName('name16')
266 >>> name16_ppa = getUtility(IArchiveSet).new(270 >>> name16_ppa = getUtility(IArchiveSet).new(
267 ... owner=name16, distribution=ubuntu, purpose=ArchivePurpose.PPA)271 ... owner=name16, distribution=ubuntu, purpose=ArchivePurpose.PPA)
268272
269 >>> ppa_policy = getPolicy(273 >>> ppa_policy = getPolicy(
270 ... name='insecure', distro='ubuntu', distroseries=None)274 ... name='insecure', distro='ubuntu', distroseries=None)
271 >>> ppa_policy.archive = name16_ppa275 >>> ppa_policy.archive = name16_ppa
272 >>> ppa_policy.setDistroSeriesAndPocket('hoary')276 >>> ppa_policy.setDistroSeriesAndPocket('hoary')
273277
274 >>> ppa_bar_src = NascentUpload.from_changesfile_path(278 >>> ppa_bar_src = NascentUpload.from_changesfile_path(
275 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),279 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),
276 ... ppa_policy, DevNullLogger())280 ... ppa_policy, DevNullLogger())
277 >>> ppa_bar_src.process()281 >>> ppa_bar_src.process()
278 >>> result = ppa_bar_src.do_accept()282 >>> result = ppa_bar_src.do_accept()
279283
280 >>> [notification] = pop_notifications()284 >>> [notification] = pop_notifications()
281285
282 >>> notification['X-Katie']286 >>> notification['X-Katie']
283 'Launchpad actually'287 'Launchpad actually'
284288
285 >>> print_addrlist(notification['To'])289 >>> print_addrlist(notification['To'])
286 Foo Bar <foo.bar@canonical.com>290 Foo Bar <foo.bar@canonical.com>
287291
288On PPA upload notifications the 'X-Launchpad-PPA' is present and292On PPA upload notifications the 'X-Launchpad-PPA' is present and
289contains the target PPA owner account name.293contains the target PPA owner account name.
290294
291 >>> notification['X-Launchpad-PPA']295 >>> notification['X-Launchpad-PPA']
292 'name16'296 'name16'
293297
294However, PPA upload notifications do not contain an attachment with the298However, PPA upload notifications do not contain an attachment with the
295original changesfile.299original changesfile.
296300
297 >>> attachment = notification.get_payload()[1]301 >>> attachment = notification.get_payload()[1]
298 Traceback (most recent call last):302 Traceback (most recent call last):
299 ...303 ...
300 IndexError: list index out of range304 IndexError: list index out of range
301305
302See further tests upon PPA upload notifications on306See further tests upon PPA upload notifications on
303archiveuploader/ftests/test_ppauploadprocessor.307archiveuploader/ftests/test_ppauploadprocessor.
@@ -305,234 +309,240 @@
305NEW binary upload to RELEASE pocket via 'sync' policy (we need to309NEW binary upload to RELEASE pocket via 'sync' policy (we need to
306override sync policy to allow binary uploads):310override sync policy to allow binary uploads):
307311
308 >>> from lp.archiveuploader.uploadpolicy import ArchiveUploadType312 >>> from lp.archiveuploader.uploadpolicy import ArchiveUploadType
309 >>> modified_sync_policy = getPolicy(313 >>> modified_sync_policy = getPolicy(
310 ... name='sync', distro='ubuntu', distroseries='hoary')314 ... name='sync', distro='ubuntu', distroseries='hoary')
311 >>> modified_sync_policy.accepted_type = ArchiveUploadType.BINARY_ONLY315 >>> modified_sync_policy.accepted_type = ArchiveUploadType.BINARY_ONLY
312316
313 >>> bar_src = NascentUpload.from_changesfile_path(317 >>> bar_src = NascentUpload.from_changesfile_path(
314 ... datadir('suite/bar_1.0-1_binary/bar_1.0-1_i386.changes'),318 ... datadir('suite/bar_1.0-1_binary/bar_1.0-1_i386.changes'),
315 ... modified_sync_policy, DevNullLogger())319 ... modified_sync_policy, DevNullLogger())
316 >>> bar_src.process()320 >>> bar_src.process()
317321
318 >>> bar_src.logger = FakeLogger()322 >>> bar_src.logger = FakeLogger()
319 >>> result = bar_src.do_accept()323 >>> result = bar_src.do_accept()
320 DEBUG Creating queue entry324 DEBUG Creating queue entry
321 DEBUG Not sending email, upload contains binaries.325 DEBUG Not sending email; upload is from a build.
322326
323We simply ignore messages generated at this step because they are not327We simply ignore messages generated at this step because they are not
324going to exist in production. We simply need this binary to be published328going to exist in production. We simply need this binary to be published
325in order to test other features in post-release pockets.329in order to test other features in post-release pockets.
326330
327 >>> ignore = pop_notifications()331 >>> ignore = pop_notifications()
328332
329Let's accept & publish bar binary in order to make the next uploads of333Let's accept & publish bar binary in order to make the next uploads of
330this series *known* in hoary:334this series *known* in hoary:
331335
332 >>> bar_src.queue_root.setAccepted()336 >>> bar_src.queue_root.setAccepted()
333 >>> pub_records = bar_src.queue_root.realiseUpload()337 >>> pub_records = bar_src.queue_root.realiseUpload()
334338
335339
336NEW source uploads for 'translations' section via sync policy:340NEW source uploads for 'translations' section via sync policy:
337341
338 >>> modified_sync_policy = getPolicy(342 >>> modified_sync_policy = getPolicy(
339 ... name='sync', distro='ubuntu', distroseries='hoary')343 ... name='sync', distro='ubuntu', distroseries='hoary')
340344
341 >>> lang_pack = NascentUpload.from_changesfile_path(345 >>> lang_pack = NascentUpload.from_changesfile_path(
342 ... datadir('language-packs/language-pack-pt_1.0-1_source.changes'),346 ... datadir('language-packs/language-pack-pt_1.0-1_source.changes'),
343 ... modified_sync_policy, DevNullLogger())347 ... modified_sync_policy, DevNullLogger())
344 >>> lang_pack.process()348 >>> lang_pack.process()
345349
346 >>> lang_pack.logger = FakeLogger()350 >>> lang_pack.logger = FakeLogger()
347 >>> result = lang_pack.do_accept()351 >>> result = lang_pack.do_accept()
348 DEBUG Creating queue entry352 DEBUG Creating queue entry
349 DEBUG Skipping acceptance and announcement, it is a language-package upload.353 DEBUG Skipping acceptance and announcement, it is a language-package
350354 upload.
351 >>> lang_pack.queue_root.status.name355
352 'NEW'356 >>> lang_pack.queue_root.status.name
357 'NEW'
353358
354No messages were generated since this upload is targeted for the359No messages were generated since this upload is targeted for the
355'translation' section:360'translation' section:
356361
357 >>> transaction.commit()362 >>> transaction.commit()
358 >>> len(stub.test_emails)363 >>> len(stub.test_emails)
359 0364 0
360365
361Accept and publish this series:366Accept and publish this series:
362367
363 >>> lang_pack.queue_root.setAccepted()368 >>> lang_pack.queue_root.setAccepted()
364 >>> pub_records = lang_pack.queue_root.realiseUpload()369 >>> pub_records = lang_pack.queue_root.realiseUpload()
365370
366371
367AUTO_APPROVED source uploads for 'translations' section:372AUTO_APPROVED source uploads for 'translations' section:
368373
369 >>> modified_sync_policy = getPolicy(374 >>> modified_sync_policy = getPolicy(
370 ... name='sync', distro='ubuntu', distroseries='hoary')375 ... name='sync', distro='ubuntu', distroseries='hoary')
371376
372 >>> lang_pack = NascentUpload.from_changesfile_path(377 >>> lang_pack = NascentUpload.from_changesfile_path(
373 ... datadir('language-packs/language-pack-pt_1.0-2_source.changes'),378 ... datadir('language-packs/language-pack-pt_1.0-2_source.changes'),
374 ... modified_sync_policy, DevNullLogger())379 ... modified_sync_policy, DevNullLogger())
375 >>> lang_pack.process()380 >>> lang_pack.process()
376381
377 >>> lang_pack.logger = FakeLogger()382 >>> lang_pack.logger = FakeLogger()
378 >>> result = lang_pack.do_accept()383 >>> result = lang_pack.do_accept()
379 DEBUG Creating queue entry384 DEBUG Creating queue entry
380 ...385 ...
381 DEBUG Skipping acceptance and announcement, it is a language-package upload.386 DEBUG Skipping acceptance and announcement, it is a language-package
382387 upload.
383 >>> lang_pack.queue_root.status.name388
384 'DONE'389 >>> lang_pack.queue_root.status.name
390 'DONE'
385391
386Again, no messages were generated since this upload is targeted for392Again, no messages were generated since this upload is targeted for
387'translation' section:393'translation' section:
388394
389 >>> transaction.commit()395 >>> transaction.commit()
390 >>> len(stub.test_emails)396 >>> len(stub.test_emails)
391 0397 0
392398
393Release hoary, enable uploads to post-release pockets:399Release hoary, enable uploads to post-release pockets:
394400
395 >>> hoary.status = SeriesStatus.CURRENT401 >>> hoary.status = SeriesStatus.CURRENT
396402
397UNAPPROVED source uploads for 'translations' section via insecure:403UNAPPROVED source uploads for 'translations' section via insecure:
398404
399 >>> insecure_policy = getPolicy(405 >>> insecure_policy = getPolicy(
400 ... name='insecure', distro='ubuntu', distroseries=None)406 ... name='insecure', distro='ubuntu', distroseries=None)
401 >>> insecure_policy.setDistroSeriesAndPocket('hoary-updates')407 >>> insecure_policy.setDistroSeriesAndPocket('hoary-updates')
402408
403 >>> lang_pack = NascentUpload.from_changesfile_path(409 >>> lang_pack = NascentUpload.from_changesfile_path(
404 ... datadir('language-packs/language-pack-pt_1.0-3_source.changes'),410 ... datadir('language-packs/language-pack-pt_1.0-3_source.changes'),
405 ... insecure_policy, DevNullLogger())411 ... insecure_policy, DevNullLogger())
406 >>> lang_pack.process()412 >>> lang_pack.process()
407 >>> lang_pack.logger = FakeLogger()413 >>> lang_pack.logger = FakeLogger()
408 >>> result = lang_pack.do_accept()414 >>> result = lang_pack.do_accept()
409 DEBUG Creating queue entry415 DEBUG Creating queue entry
410 DEBUG language-pack-pt diff from 1.0-2 to 1.0-3 requested416 DEBUG language-pack-pt diff from 1.0-2 to 1.0-3 requested
411 DEBUG Setting it to UNAPPROVED417 DEBUG Setting it to UNAPPROVED
412 DEBUG Skipping acceptance and announcement, it is a language-package upload.418 DEBUG Skipping acceptance and announcement, it is a language-package
413419 upload.
414 >>> lang_pack.queue_root.status.name420
415 'UNAPPROVED'421 >>> lang_pack.queue_root.status.name
422 'UNAPPROVED'
416423
417UNAPPROVED message was also skipped for an upload targeted to424UNAPPROVED message was also skipped for an upload targeted to
418'translation' section:425'translation' section:
419426
420 >>> transaction.commit()427 >>> transaction.commit()
421 >>> len(stub.test_emails)428 >>> len(stub.test_emails)
422 0429 0
423430
424431
425An UNAPPROVED binary upload via insecure will send one email saying that432An UNAPPROVED binary upload via insecure will send one email saying that
426the upload is waiting for approval:433the upload is waiting for approval:
427434
428 >>> bar_src = NascentUpload.from_changesfile_path(435 >>> bar_src = NascentUpload.from_changesfile_path(
429 ... datadir('suite/bar_1.0-2/bar_1.0-2_source.changes'),436 ... datadir('suite/bar_1.0-2/bar_1.0-2_source.changes'),
430 ... insecure_policy, DevNullLogger())437 ... insecure_policy, DevNullLogger())
431 >>> bar_src.process()438 >>> bar_src.process()
432439
433 >>> bar_src.logger = FakeLogger()440 >>> bar_src.logger = FakeLogger()
434 >>> result = bar_src.do_accept()441 >>> result = bar_src.do_accept()
435 DEBUG Creating queue entry442 DEBUG Creating queue entry
436 ...443 ...
437444
438 >>> [notification] = pop_notifications()445 >>> [notification] = pop_notifications()
439446
440 >>> notification['X-Katie']447 >>> notification['X-Katie']
441 'Launchpad actually'448 'Launchpad actually'
442449
443 >>> print_addrlist(notification['To'])450 >>> print_addrlist(notification['To'])
444 Foo Bar <foo.bar@canonical.com>451 Foo Bar <foo.bar@canonical.com>
445 Daniel Silverstone <daniel.silverstone@canonical.com>452 Daniel Silverstone <daniel.silverstone@canonical.com>
446453
447 >>> notification['Subject']454 >>> notification['Subject']
448 '[ubuntu/hoary-updates] bar 1.0-2 (Waiting for approval)'455 '[ubuntu/hoary-updates] bar 1.0-2 (Waiting for approval)'
449456
450457
451UNAPPROVED upload to BACKPORTS via insecure policy will send a notification458UNAPPROVED upload to BACKPORTS via insecure policy will send a notification
452saying they are waiting for approval:459saying they are waiting for approval:
453460
454 >>> unapproved_backports_policy = getPolicy(461 >>> unapproved_backports_policy = getPolicy(
455 ... name='insecure', distro='ubuntu', distroseries=None)462 ... name='insecure', distro='ubuntu', distroseries=None)
456 >>> unapproved_backports_policy.setDistroSeriesAndPocket('hoary-backports')463 >>> unapproved_backports_policy.setDistroSeriesAndPocket(
457 >>> bar_src = NascentUpload.from_changesfile_path(464 ... 'hoary-backports')
458 ... datadir('suite/bar_1.0-3_valid/bar_1.0-3_source.changes'),465 >>> bar_src = NascentUpload.from_changesfile_path(
459 ... unapproved_backports_policy, DevNullLogger())466 ... datadir('suite/bar_1.0-3_valid/bar_1.0-3_source.changes'),
460 >>> bar_src.process()467 ... unapproved_backports_policy, DevNullLogger())
461 >>> bar_src.logger = FakeLogger()468 >>> bar_src.process()
462 >>> result = bar_src.do_accept()469 >>> bar_src.logger = FakeLogger()
463 DEBUG Creating queue entry470 >>> result = bar_src.do_accept()
464 DEBUG bar diff from 1.0-1 to 1.0-3 requested471 DEBUG Creating queue entry
465 DEBUG Setting it to UNAPPROVED472 DEBUG bar diff from 1.0-1 to 1.0-3 requested
466 ...473 DEBUG Setting it to UNAPPROVED
467474 ...
468 >>> [notification] = pop_notifications()475
469476 >>> [notification] = pop_notifications()
470 >>> notification['X-Katie']477
471 'Launchpad actually'478 >>> notification['X-Katie']
472479 'Launchpad actually'
473 >>> print_addrlist(notification['To'])480
474 Foo Bar <foo.bar@canonical.com>481 >>> print_addrlist(notification['To'])
475 Daniel Silverstone <daniel.silverstone@canonical.com>482 Foo Bar <foo.bar@canonical.com>
476483 Daniel Silverstone <daniel.silverstone@canonical.com>
477 >>> notification['Subject']484
478 '[ubuntu/hoary-backports] bar 1.0-3 (Waiting for approval)'485 >>> notification['Subject']
486 '[ubuntu/hoary-backports] bar 1.0-3 (Waiting for approval)'
479487
480AUTO-APPROVED upload to BACKPORTS pocket via 'sync' policy:488AUTO-APPROVED upload to BACKPORTS pocket via 'sync' policy:
481489
482 >>> modified_sync_policy = getPolicy(490 >>> modified_sync_policy = getPolicy(
483 ... name='sync', distro='ubuntu', distroseries=None)491 ... name='sync', distro='ubuntu', distroseries=None)
484 >>> modified_sync_policy.setDistroSeriesAndPocket('hoary-backports')492 >>> modified_sync_policy.setDistroSeriesAndPocket('hoary-backports')
485493
486 >>> bar_src = NascentUpload.from_changesfile_path(494 >>> bar_src = NascentUpload.from_changesfile_path(
487 ... datadir('suite/bar_1.0-4/bar_1.0-4_source.changes'),495 ... datadir('suite/bar_1.0-4/bar_1.0-4_source.changes'),
488 ... modified_sync_policy, DevNullLogger())496 ... modified_sync_policy, DevNullLogger())
489 >>> bar_src.process()497 >>> bar_src.process()
490498
491 >>> bar_src.logger = FakeLogger()499 >>> bar_src.logger = FakeLogger()
492 >>> result = bar_src.do_accept()500 >>> result = bar_src.do_accept()
493 DEBUG Creating queue entry501 DEBUG Creating queue entry
494 ...502 ...
495 DEBUG Sent a mail:503 DEBUG Sent a mail:
496 DEBUG Subject: [ubuntu/hoary-backports] bar 1.0-4 (Accepted)504 DEBUG Subject: [ubuntu/hoary-backports] bar 1.0-4 (Accepted)
497 DEBUG Recipients: Celso Providelo <celso.providelo@canonical.com>505 DEBUG Recipients: Celso Providelo <celso.providelo@canonical.com>
498 DEBUG Body:506 DEBUG Body:
499 DEBUG bar (1.0-4) breezy; urgency=low507 DEBUG bar (1.0-4) breezy; urgency=low
500 DEBUG508 DEBUG
501 DEBUG * Changer using non-preferred email509 DEBUG * Changer using non-preferred email
502 DEBUG510 DEBUG
503 DEBUG Date: Tue, 25 Apr 2006 10:36:14 -0300511 DEBUG Date: Tue, 25 Apr 2006 10:36:14 -0300
504 DEBUG Changed-By: Celso R. Providelo <cprov@ubuntu.com>512 DEBUG Changed-By: Celso R. Providelo <cprov@ubuntu.com>
505 DEBUG Maintainer: Launchpad team <launchpad@lists.canonical.com>513 DEBUG Maintainer: Launchpad team <launchpad@lists.canonical.com>
506 DEBUG http://launchpad.dev/ubuntu/hoary/+source/bar/1.0-4514 DEBUG http://launchpad.dev/ubuntu/hoary/+source/bar/1.0-4
507 DEBUG515 DEBUG
508 DEBUG ==516 DEBUG ==
509 DEBUG517 DEBUG
510 DEBUG Announcing to hoary-announce@lists.ubuntu.com518 DEBUG Announcing to hoary-announce@lists.ubuntu.com
511 DEBUG519 DEBUG
512 DEBUG Thank you for your contribution to Ubuntu Linux.520 DEBUG Thank you for your contribution to Ubuntu Linux.
513 DEBUG521 DEBUG
514 DEBUG --522 DEBUG --
515 DEBUG You are receiving this email because you are the uploader, maintainer or523 DEBUG You are receiving this email because you are the uploader,
516 DEBUG signer of the above package.524 maintainer or
525 DEBUG signer of the above package.
517526
518There is one email generated:527There is one email generated:
519528
520 >>> [notification] = pop_notifications()529 >>> [notification] = pop_notifications()
521530
522 >>> notification['X-Katie']531 >>> notification['X-Katie']
523 'Launchpad actually'532 'Launchpad actually'
524533
525 >>> print_addrlist(notification['To'])534 >>> print_addrlist(notification['To'])
526 Celso Providelo <celso.providelo@canonical.com>535 Celso Providelo <celso.providelo@canonical.com>
527536
528 >>> notification['Subject']537 >>> notification['Subject']
529 '[ubuntu/hoary-backports] bar 1.0-4 (Accepted)'538 '[ubuntu/hoary-backports] bar 1.0-4 (Accepted)'
530539
531Remove orig.tar.gz pumped from librarian to disk during the upload540Remove orig.tar.gz pumped from librarian to disk during the upload
532checks:541checks:
533542
534 >>> import os543 >>> import os
535 >>> os.remove(os.path.join(datadir('suite/bar_1.0-4'), 'bar_1.0.orig.tar.gz'))544 >>> upload_data = datadir('suite/bar_1.0-4')
545 >>> os.remove(os.path.join(upload_data, 'bar_1.0.orig.tar.gz'))
536546
537DEBIAN SYNC upload of a source via the 'sync' policy.547DEBIAN SYNC upload of a source via the 'sync' policy.
538These uploads do not generate any announcement emails for auto-accepted548These uploads do not generate any announcement emails for auto-accepted
@@ -541,88 +551,88 @@
541Make hoary developmental again, as syncs only happen at that stage of a551Make hoary developmental again, as syncs only happen at that stage of a
542distroseries.552distroseries.
543553
544 >>> hoary.status = SeriesStatus.DEVELOPMENT554 >>> hoary.status = SeriesStatus.DEVELOPMENT
545555
546 >>> bar_src = NascentUpload.from_changesfile_path(556 >>> bar_src = NascentUpload.from_changesfile_path(
547 ... datadir(557 ... datadir(
548 ... 'suite/bar_1.0-5_debian_auto_sync/bar_1.0-5_source.changes'),558 ... 'suite/bar_1.0-5_debian_auto_sync/bar_1.0-5_source.changes'),
549 ... sync_policy, DevNullLogger())559 ... sync_policy, DevNullLogger())
550 >>> bar_src.process()560 >>> bar_src.process()
551561
552 >>> bar_src.logger = FakeLogger()562 >>> bar_src.logger = FakeLogger()
553 >>> result = bar_src.do_accept()563 >>> result = bar_src.do_accept()
554 DEBUG Creating queue entry564 DEBUG Creating queue entry
555 ...565 ...
556566
557One email generated:567One email generated:
558568
559 >>> [notification] = pop_notifications()569 >>> [notification] = pop_notifications()
560 >>> notification['Subject']570 >>> notification['Subject']
561 '[ubuntu/hoary] bar 1.0-5 (Accepted)'571 '[ubuntu/hoary] bar 1.0-5 (Accepted)'
562572
563573
564In contrast, manual sync uploads do generate the announcement:574In contrast, manual sync uploads do generate the announcement:
565575
566 >>> bar_src = NascentUpload.from_changesfile_path(576 >>> bar_src = NascentUpload.from_changesfile_path(
567 ... datadir(577 ... datadir(
568 ... 'suite/bar_1.0-6/bar_1.0-6_source.changes'),578 ... 'suite/bar_1.0-6/bar_1.0-6_source.changes'),
569 ... sync_policy, DevNullLogger())579 ... sync_policy, DevNullLogger())
570 >>> bar_src.process()580 >>> bar_src.process()
571581
572 >>> bar_src.logger = FakeLogger()582 >>> bar_src.logger = FakeLogger()
573 >>> result = bar_src.do_accept()583 >>> result = bar_src.do_accept()
574 DEBUG Creating queue entry584 DEBUG Creating queue entry
575 ...585 ...
576586
577Two emails generated:587Two emails generated:
578588
579 >>> import operator589 >>> import operator
580 >>> msgs = pop_notifications(sort_key=operator.itemgetter('To'))590 >>> msgs = pop_notifications(sort_key=operator.itemgetter('To'))
581 >>> len(msgs)591 >>> len(msgs)
582 2592 2
583593
584 >>> [message['To'] for message in msgs]594 >>> [message['To'] for message in msgs]
585 ['Celso Providelo <celso.providelo@canonical.com>',595 ['Celso Providelo <celso.providelo@canonical.com>',
586 'hoary-announce@lists.ubuntu.com']596 'hoary-announce@lists.ubuntu.com']
587597
588 >>> [message['Subject'] for message in msgs]598 >>> [message['Subject'] for message in msgs]
589 ['[ubuntu/hoary] bar 1.0-6 (Accepted)',599 ['[ubuntu/hoary] bar 1.0-6 (Accepted)',
590 '[ubuntu/hoary] bar 1.0-6 (Accepted)']600 '[ubuntu/hoary] bar 1.0-6 (Accepted)']
591601
592Reset hoary back to released and remove disk files created during processing:602Reset hoary back to released and remove disk files created during processing:
593603
594 >>> hoary.status = SeriesStatus.CURRENT604 >>> hoary.status = SeriesStatus.CURRENT
595 >>> os.remove(os.path.join(datadir('suite/bar_1.0-5_debian_auto_sync'),605 >>> os.remove(os.path.join(datadir('suite/bar_1.0-5_debian_auto_sync'),
596 ... 'bar_1.0.orig.tar.gz'))606 ... 'bar_1.0.orig.tar.gz'))
597 >>> os.remove(os.path.join(datadir('suite/bar_1.0-6'),607 >>> os.remove(os.path.join(datadir('suite/bar_1.0-6'),
598 ... 'bar_1.0.orig.tar.gz'))608 ... 'bar_1.0.orig.tar.gz'))
599609
600Dry run uploads should not generate any emails. Call do_accept with610Dry run uploads should not generate any emails. Call do_accept with
601notify=False:611notify=False:
602612
603 >>> sync_policy = getPolicy(613 >>> sync_policy = getPolicy(
604 ... name='sync', distro='ubuntu', distroseries='hoary')614 ... name='sync', distro='ubuntu', distroseries='hoary')
605615
606 >>> bar_src = NascentUpload.from_changesfile_path(616 >>> bar_src = NascentUpload.from_changesfile_path(
607 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),617 ... datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),
608 ... sync_policy, DevNullLogger())618 ... sync_policy, DevNullLogger())
609 >>> bar_src.process()619 >>> bar_src.process()
610620
611 >>> bar_src.logger = FakeLogger()621 >>> bar_src.logger = FakeLogger()
612 >>> result = bar_src.do_accept(notify=False)622 >>> result = bar_src.do_accept(notify=False)
613623
614No emails generated:624No emails generated:
615625
616 >>> msgs = pop_notifications()626 >>> msgs = pop_notifications()
617 >>> len(msgs)627 >>> len(msgs)
618 0628 0
619629
620Rejections with notify=False will also not generate any emails.630Rejections with notify=False will also not generate any emails.
621631
622 >>> result = bar_src.do_reject(notify=False)632 >>> result = bar_src.do_reject(notify=False)
623 >>> msgs = pop_notifications()633 >>> msgs = pop_notifications()
624 >>> len(msgs)634 >>> len(msgs)
625 0635 0
626636
627The PackageUpload record created by NascentUpload will be in the NEW637The PackageUpload record created by NascentUpload will be in the NEW
628state if the package was not previously uploaded. When accepted by an638state if the package was not previously uploaded. When accepted by an
@@ -631,199 +641,205 @@
631dry_run, which when True will not send any emails. It will also log at641dry_run, which when True will not send any emails. It will also log at
632the INFO level what it /would/ have sent.642the INFO level what it /would/ have sent.
633643
634 >>> random_package_upload = hoary.getQueueItems()[0]644 >>> random_package_upload = hoary.getQueueItems()[0]
635 >>> random_package_upload.notify(dry_run=True, logger=FakeLogger())645 >>> random_package_upload.notify(dry_run=True, logger=FakeLogger())
636 DEBUG Building recipients list.646 DEBUG Building recipients list.
637 ...647 ...
638 INFO Would have sent a mail:648 INFO Would have sent a mail:
639 INFO Subject: [ubuntu/hoary] bar 1.0-6 (Accepted)649 INFO Subject: [ubuntu/hoary] bar 1.0-6 (Accepted)
640 ...650 ...
641 INFO Recipients: Celso Providelo <celso.providelo@canonical.com>651 INFO Recipients: Celso Providelo <celso.providelo@canonical.com>
642 ...652 ...
643 INFO bar (1.0-6) breezy; urgency=low653 INFO bar (1.0-6) breezy; urgency=low
644 ...654 ...
645 INFO No announcement sent655 INFO No announcement sent
646 ...656 ...
647 INFO You are receiving this email because you are the uploader, maintainer or657 INFO You are receiving this email because you are the uploader, maintainer
648 INFO signer of the above package.658 or
659 INFO signer of the above package.
649660
650No emails generated:661No emails generated:
651662
652 >>> msgs = pop_notifications()663 >>> msgs = pop_notifications()
653 >>> len(msgs)664 >>> len(msgs)
654 0665 0
655666
656Uploads with UTF-8 characters in email addresses in the changes file are667Uploads with UTF-8 characters in email addresses in the changes file are
657permitted, but converted to ASCII, which is a limitation of the mailer.668permitted, but converted to ASCII, which is a limitation of the mailer.
658However, UTF-8 in the mail content is preserved.669However, UTF-8 in the mail content is preserved.
659670
660 >>> hoary.status = SeriesStatus.DEVELOPMENT671 >>> hoary.status = SeriesStatus.DEVELOPMENT
661 >>> anything_policy = getPolicy(672 >>> anything_policy = getPolicy(
662 ... name='anything', distro='ubuntu', distroseries='hoary')673 ... name='anything', distro='ubuntu', distroseries='hoary')
663 >>> bar_upload = NascentUpload.from_changesfile_path(674 >>> bar_upload = NascentUpload.from_changesfile_path(
664 ... datadir(675 ... datadir(
665 ... 'suite/bar_1.0-10_utf8_changesfile/bar_1.0-10_source.changes'),676 ... 'suite/bar_1.0-10_utf8_changesfile/'
666 ... anything_policy, DevNullLogger())677 ... 'bar_1.0-10_source.changes'),
667 >>> bar_upload.process()678 ... anything_policy, DevNullLogger())
668679 >>> bar_upload.process()
669 >>> bar_upload.logger = FakeLogger()680
670 >>> result = bar_upload.do_accept()681 >>> bar_upload.logger = FakeLogger()
671 DEBUG Creating queue entry682 >>> result = bar_upload.do_accept()
672 ...683 DEBUG Creating queue entry
673684 ...
674 >>> msgs = pop_notifications(sort_key=operator.itemgetter('To'))685
675 >>> len(msgs)686 >>> msgs = pop_notifications(sort_key=operator.itemgetter('To'))
676 2687 >>> len(msgs)
688 2
677689
678"Cihar" should actually be "ÄŒihaÅ™" but the mailer will convert to ASCII.690"Cihar" should actually be "ÄŒihaÅ™" but the mailer will convert to ASCII.
679691
680 >>> [message['From'] for message in msgs]692 >>> [message['From'] for message in msgs]
681 ['Root <root@localhost>', 'Non-ascii changed-by Cihar <daniel.silverstone@canonical.com>']693 ['Root <root@localhost>', 'Non-ascii changed-by Cihar
694 <daniel.silverstone@canonical.com>']
682695
683UTF-8 text in the changes file that is sent on the email is preserved696UTF-8 text in the changes file that is sent on the email is preserved
684in the MIME encoding. Please note also that the person that signed the697in the MIME encoding. Please note also that the person that signed the
685changes file is mentioned toward the end of the email.698changes file is mentioned toward the end of the email.
686699
687 >>> announcement_email = msgs[0]700 >>> announcement_email = msgs[0]
688 >>> announcement_email.is_multipart()701 >>> announcement_email.is_multipart()
689 True702 True
690703
691 >>> body = announcement_email.get_payload()[0]704 >>> body = announcement_email.get_payload()[0]
692 >>> print body.as_string() # doctest: -NORMALIZE_WHITESPACE705 >>> print body.as_string() # doctest: -NORMALIZE_WHITESPACE
693 Content-Type: text/plain; charset="utf-8"706 Content-Type: text/plain; charset="utf-8"
694 MIME-Version: 1.0707 MIME-Version: 1.0
695 Content-Transfer-Encoding: quoted-printable708 Content-Transfer-Encoding: quoted-printable
696 <BLANKLINE>709 <BLANKLINE>
697 bar (1.0-10) breezy; urgency=3Dlow710 bar (1.0-10) breezy; urgency=3Dlow
698 <BLANKLINE>711 <BLANKLINE>
699 * Changes file that contains UTF-8712 * Changes file that contains UTF-8
700 <BLANKLINE>713 <BLANKLINE>
701 * Non-ascii text: =C4=8Ciha=C5=99714 * Non-ascii text: =C4=8Ciha=C5=99
702 <BLANKLINE>715 <BLANKLINE>
703 <BLANKLINE>716 <BLANKLINE>
704 Date: Thu, 30 Mar 2006 01:36:14 +0100717 Date: Thu, 30 Mar 2006 01:36:14 +0100
705 Changed-By: Non-ascii changed-by =C4=8Ciha=C5=99 <daniel.silverstone@canoni=718 Changed-By: Non-ascii changed-by =C4=8Ciha=C5=99 <daniel.silverstone@canoni=
706 cal.com>719 cal.com>
707 Maintainer: Non-ascii maintainer =C4=8Ciha=C5=99 <launchpad@lists.canonical=720 Maintainer: Non-ascii maintainer =C4=8Ciha=C5=99 <launchpad@lists.canonical=
708 .com>721 .com>
709 Signed-By: Foo Bar <foo.bar@canonical.com>722 Signed-By: Foo Bar <foo.bar@canonical.com>
710 http://launchpad.dev/ubuntu/hoary/+source/bar/1.0-10723 http://launchpad.dev/ubuntu/hoary/+source/bar/1.0-10
711 <BLANKLINE>724 <BLANKLINE>
712 =3D=3D725 =3D=3D
713 <BLANKLINE>726 <BLANKLINE>
714 Announcing to hoary-announce@lists.ubuntu.com727 Announcing to hoary-announce@lists.ubuntu.com
715 <BLANKLINE>728 <BLANKLINE>
716 Thank you for your contribution to Ubuntu Linux.729 Thank you for your contribution to Ubuntu Linux.
717 <BLANKLINE>730 <BLANKLINE>
718 -- =731 -- =
719 <BLANKLINE>732 <BLANKLINE>
720 You are receiving this email because you are the uploader, maintainer or733 You are receiving this email because you are the uploader, maintainer or
721 signer of the above package.734 signer of the above package.
722 <BLANKLINE>735 <BLANKLINE>
723736
724In order to facilitate scripts that parse announcement emails, the changes737In order to facilitate scripts that parse announcement emails, the changes
725file is enclosed as an attachment.738file is enclosed as an attachment.
726739
727 >>> attachment = announcement_email.get_payload()[1]740 >>> attachment = announcement_email.get_payload()[1]
728741
729Here's the attachment metadata.742Here's the attachment metadata.
730743
731 >>> attachment['Content-Disposition']744 >>> attachment['Content-Disposition']
732 'attachment; filename="changesfile"'745 'attachment; filename="changesfile"'
733746
734And what follows is the content of the attachment.747And what follows is the content of the attachment.
735748
736 >>> print attachment.as_string() # doctest: -NORMALIZE_WHITESPACE749 >>> print attachment.as_string() # doctest: -NORMALIZE_WHITESPACE
737 Content-Type: text/plain; charset="utf-8"750 Content-Type: text/plain; charset="utf-8"
738 MIME-Version: 1.0751 MIME-Version: 1.0
739 Content-Transfer-Encoding: quoted-printable752 Content-Transfer-Encoding: quoted-printable
740 Content-Disposition: attachment; filename="changesfile"753 Content-Disposition: attachment; filename="changesfile"
741 <BLANKLINE>754 <BLANKLINE>
742 -----BEGIN PGP SIGNED MESSAGE-----755 -----BEGIN PGP SIGNED MESSAGE-----
743 Hash: SHA1756 Hash: SHA1
744 <BLANKLINE>757 <BLANKLINE>
745 Format: 1.7758 Format: 1.7
746 Date: Thu, 30 Mar 2006 01:36:14 +0100759 Date: Thu, 30 Mar 2006 01:36:14 +0100
747 Source: bar760 Source: bar
748 Binary: bar761 Binary: bar
749 Architecture: source762 Architecture: source
750 Version: 1.0-10763 Version: 1.0-10
751 Distribution: breezy764 Distribution: breezy
752 Urgency: low765 Urgency: low
753 Maintainer: Non-ascii maintainer =C4=8Ciha=C5=99 <launchpad@lists.canonical=766 Maintainer: Non-ascii maintainer =C4=8Ciha=C5=99 <launchpad@lists.canonical=
754 .com>767 .com>
755 Changed-By: Non-ascii changed-by =C4=8Ciha=C5=99 <daniel.silverstone@canoni=768 Changed-By: Non-ascii changed-by =C4=8Ciha=C5=99 <daniel.silverstone@canoni=
756 cal.com>769 cal.com>
757 Description: =770 Description: =
758 <BLANKLINE>771 <BLANKLINE>
759 bar - Stuff for testing772 bar - Stuff for testing
760 Changes: =773 Changes: =
761 <BLANKLINE>774 <BLANKLINE>
762 bar (1.0-10) breezy; urgency=3Dlow775 bar (1.0-10) breezy; urgency=3Dlow
763 .776 .
764 * Changes file that contains UTF-8777 * Changes file that contains UTF-8
765 .778 .
766 * Non-ascii text: =C4=8Ciha=C5=99779 * Non-ascii text: =C4=8Ciha=C5=99
767 .780 .
768 Files: =781 Files: =
769 <BLANKLINE>782 <BLANKLINE>
770 a4932aa84fdb62819b49f3dda163fc0d 514 devel optional bar_1.0-10.dsc783 a4932aa84fdb62819b49f3dda163fc0d 514 devel optional bar_1.0-10.dsc
771 ac6b4efe44e31f47ec9f0d0fac6935f4 622 devel optional bar_1.0-10.diff.gz784 ac6b4efe44e31f47ec9f0d0fac6935f4 622 devel optional bar_1.0-10.diff.gz
772 <BLANKLINE>785 <BLANKLINE>
773 -----BEGIN PGP SIGNATURE-----786 -----BEGIN PGP SIGNATURE-----
774 Version: GnuPG v1.4.6 (GNU/Linux)787 Version: GnuPG v1.4.6 (GNU/Linux)
775 <BLANKLINE>788 <BLANKLINE>
776 iD8DBQFGjOzNjn63CGxkqMURAlDZAJ9CusNQWwbTJr7JHYV4Ka1JbWwmJACeLbs5789 iD8DBQFGjOzNjn63CGxkqMURAlDZAJ9CusNQWwbTJr7JHYV4Ka1JbWwmJACeLbs5
777 Hzkb2pxxwYzKs7uyCiFONlo=3D790 Hzkb2pxxwYzKs7uyCiFONlo=3D
778 =3D9dL/791 =3D9dL/
779 -----END PGP SIGNATURE-----792 -----END PGP SIGNATURE-----
780 <BLANKLINE>793 <BLANKLINE>
781794
782The attempt to upload a package with a malformed changes file name will795The attempt to upload a package with a malformed changes file name will
783result in a rejection email.796result in a rejection email.
784797
785We first create a misnamed copy of the changes file.798We first create a misnamed copy of the changes file.
786799
787 >>> import os, shutil800 >>> import os, shutil
788 >>> originalp = datadir('suite/bar_1.0-1/bar_1.0-1_source.changes')801 >>> originalp = datadir('suite/bar_1.0-1/bar_1.0-1_source.changes')
789 >>> copyp = datadir('suite/bar_1.0-1/z-z_0.4.12-2~ppa2.changes')802 >>> copyp = datadir('suite/bar_1.0-1/z-z_0.4.12-2~ppa2.changes')
790 >>> shutil.copyfile(originalp, copyp)803 >>> shutil.copyfile(originalp, copyp)
791804
792And then try to upload using the changes file with the malformed name.805And then try to upload using the changes file with the malformed name.
793806
794 >>> bar_src = NascentUpload.from_changesfile_path(807 >>> bar_src = NascentUpload.from_changesfile_path(
795 ... copyp, sync_policy, DevNullLogger())808 ... copyp, sync_policy, DevNullLogger())
796 >>> bar_src.process()809 >>> bar_src.process()
797 Traceback (most recent call last):810 Traceback (most recent call last):
798 ...811 ...
799 EarlyReturnUploadError: An error occurred that prevented further processing.812 EarlyReturnUploadError: An error occurred that prevented further
800813 processing.
801 >>> bar_src.logger = FakeLogger()814
802 >>> result = bar_src.do_accept()815 >>> bar_src.logger = FakeLogger()
803 DEBUG Building recipients list.816 >>> result = bar_src.do_accept()
804 ...817 DEBUG Building recipients list.
805 DEBUG Sending rejection email.818 ...
806 DEBUG Sent a mail:819 DEBUG Sending rejection email.
807 ...820 DEBUG Sent a mail:
808 DEBUG Rejected:821 ...
809 DEBUG z-z_0.4.12-2~ppa2.changes -> inappropriate changesfile name, should follow "<pkg>_<version>_<arch>.changes" format822 DEBUG Rejected:
810 ...823 DEBUG z-z_0.4.12-2~ppa2.changes -> inappropriate changesfile name, should
811 DEBUG If you don't understand why your files were rejected, or if the824 follow "<pkg>_<version>_<arch>.changes" format
812 DEBUG override file requires editing, please go to:825 ...
813 DEBUG http://answers.launchpad.net/soyuz826 DEBUG If you don't understand why your files were rejected, or if the
814 DEBUG827 DEBUG override file requires editing, please go to:
815 DEBUG --828 DEBUG http://answers.launchpad.net/soyuz
816 DEBUG You are receiving this email because you are the uploader, maintainer or829 DEBUG
817 DEBUG signer of the above package.830 DEBUG --
818831 DEBUG You are receiving this email because you are the uploader,
819 >>> [notification] = pop_notifications()832 maintainer or
820833 DEBUG signer of the above package.
821 >>> notification['X-Katie']834
822 'Launchpad actually'835 >>> [notification] = pop_notifications()
823836
824 >>> print_addrlist(notification['To'])837 >>> notification['X-Katie']
825 Daniel Silverstone <daniel.silverstone@canonical.com>838 'Launchpad actually'
839
840 >>> print_addrlist(notification['To'])
841 Daniel Silverstone <daniel.silverstone@canonical.com>
826842
827Remove the misnamed changes file copy used for testing.843Remove the misnamed changes file copy used for testing.
828844
829 >>> os.unlink(copyp)845 >>> os.unlink(copyp)
830846
=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
--- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-01-27 17:17:50 +0000
+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-01-27 17:17:51 +0000
@@ -1950,10 +1950,7 @@
1950 # Upon full build the upload log is unset.1950 # Upon full build the upload log is unset.
1951 self.assertIs(None, build.upload_log)1951 self.assertIs(None, build.upload_log)
19521952
1953 def testSourcePackageRecipeBuild(self):1953 def doSuccessRecipeBuild(self):
1954 # Properly uploaded source packages should result in the
1955 # build status changing to FULLYBUILT.
1956
1957 # Upload a source package1954 # Upload a source package
1958 archive = self.factory.makeArchive()1955 archive = self.factory.makeArchive()
1959 archive.require_virtualized = False1956 archive.require_virtualized = False
@@ -1981,14 +1978,27 @@
1981 BuildUploadHandler(self.uploadprocessor, self.incoming_folder,1978 BuildUploadHandler(self.uploadprocessor, self.incoming_folder,
1982 leaf_name).process()1979 leaf_name).process()
1983 self.layer.txn.commit()1980 self.layer.txn.commit()
1981 return build
19841982
1983 def testSourcePackageRecipeBuild(self):
1984 # Properly uploaded source packages should result in the
1985 # build status changing to FULLYBUILT.
1986 build = self.doSuccessRecipeBuild()
1985 self.assertEquals(BuildStatus.FULLYBUILT, build.status)1987 self.assertEquals(BuildStatus.FULLYBUILT, build.status)
1986 self.assertEquals(None, build.builder)1988 self.assertEquals(None, build.builder)
1987 self.assertIsNot(None, build.duration)1989 self.assertIsNot(None, build.duration)
1988 # Upon full build the upload log is unset.1990 # Upon full build the upload log is unset.
1989 self.assertIs(None, build.upload_log)1991 self.assertIs(None, build.upload_log)
19901992
1991 def testSourcePackageRecipeBuild_fail(self):1993 def testSourcePackageRecipeBuild_success_mail(self):
1994 # When a source package recipe build succeeds, it sends a build-style
1995 # email, not user-upload-style one.
1996 self.doSuccessRecipeBuild()
1997 (mail,) = pop_notifications()
1998 subject = mail['Subject'].replace('\n\t', ' ')
1999 self.assertIn('Successfully built', subject)
2000
2001 def doFailureRecipeBuild(self):
1992 # A source package recipe build will fail if no files are present.2002 # A source package recipe build will fail if no files are present.
19932003
1994 # Upload a source package2004 # Upload a source package
@@ -2012,11 +2022,24 @@
2012 BuildUploadHandler(self.uploadprocessor, self.incoming_folder,2022 BuildUploadHandler(self.uploadprocessor, self.incoming_folder,
2013 leaf_name).process()2023 leaf_name).process()
2014 self.layer.txn.commit()2024 self.layer.txn.commit()
2025 return build
2026
2027 def testSourcePackageRecipeBuild_fail(self):
2028 build = self.doFailureRecipeBuild()
2015 self.assertEquals(BuildStatus.FAILEDTOUPLOAD, build.status)2029 self.assertEquals(BuildStatus.FAILEDTOUPLOAD, build.status)
2016 self.assertEquals(None, build.builder)2030 self.assertEquals(None, build.builder)
2017 self.assertIsNot(None, build.duration)2031 self.assertIsNot(None, build.duration)
2018 self.assertIsNot(None, build.upload_log)2032 self.assertIsNot(None, build.upload_log)
20192033
2034 def testSourcePackageRecipeBuild_fail_mail(self):
2035 # Failures should generate a message that includes the upload log URL.
2036 self.doFailureRecipeBuild()
2037 (mail,) = pop_notifications()
2038 subject = mail['Subject'].replace('\n\t', ' ')
2039 self.assertIn('Failed to upload', subject)
2040 body = mail.get_payload(decode=True)
2041 self.assertIn('Upload Log: http', body)
2042
2020 def testBuildWithInvalidStatus(self):2043 def testBuildWithInvalidStatus(self):
2021 # Builds with an invalid (non-UPLOADING) status should trigger2044 # Builds with an invalid (non-UPLOADING) status should trigger
2022 # a warning but be left alone.2045 # a warning but be left alone.
20232046
=== modified file 'lib/lp/archiveuploader/uploadprocessor.py'
--- lib/lp/archiveuploader/uploadprocessor.py 2011-01-27 17:17:50 +0000
+++ lib/lp/archiveuploader/uploadprocessor.py 2011-01-27 17:17:51 +0000
@@ -660,13 +660,15 @@
660 error_utility.raising(info, request)660 error_utility.raising(info, request)
661 logger.error('%s (%s)' % (message, request.oopsid))661 logger.error('%s (%s)' % (message, request.oopsid))
662 result = UploadStatusEnum.FAILED662 result = UploadStatusEnum.FAILED
663 if not (result == UploadStatusEnum.ACCEPTED and663 if (result != UploadStatusEnum.ACCEPTED or
664 self.build.verifySuccessfulUpload() and664 not self.build.verifySuccessfulUpload()):
665 self.build.status == BuildStatus.FULLYBUILT):
666 self.build.status = BuildStatus.FAILEDTOUPLOAD665 self.build.status = BuildStatus.FAILEDTOUPLOAD
666 if self.build.status != BuildStatus.FULLYBUILT:
667 self.build.storeUploadLog(logger.getLogBuffer())
667 self.build.notify(extra_info="Uploading build %s failed." %668 self.build.notify(extra_info="Uploading build %s failed." %
668 self.upload)669 self.upload)
669 self.build.storeUploadLog(logger.getLogBuffer())670 else:
671 self.build.notify()
670 self.processor.ztm.commit()672 self.processor.ztm.commit()
671 self.moveProcessedUpload(result, logger)673 self.moveProcessedUpload(result, logger)
672674
673675
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2011-01-24 20:57:37 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2011-01-27 17:17:51 +0000
@@ -621,6 +621,8 @@
621621
622 if not config.builddmaster.send_build_notification:622 if not config.builddmaster.send_build_notification:
623 return623 return
624 if self.status == BuildStatus.FULLYBUILT:
625 return
624626
625 recipients = set()627 recipients = set()
626628
627629
=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py 2011-01-14 14:03:28 +0000
+++ lib/lp/soyuz/model/queue.py 2011-01-27 17:17:51 +0000
@@ -72,7 +72,6 @@
72 )72 )
73from lp.services.propertycache import cachedproperty73from lp.services.propertycache import cachedproperty
74from lp.soyuz.enums import (74from lp.soyuz.enums import (
75 BinaryPackageFormat,
76 PackageUploadCustomFormat,75 PackageUploadCustomFormat,
77 PackageUploadStatus,76 PackageUploadStatus,
78 )77 )
@@ -192,9 +191,23 @@
192 # PackageUploadSource objects which are related.191 # PackageUploadSource objects which are related.
193 sources = SQLMultipleJoin('PackageUploadSource',192 sources = SQLMultipleJoin('PackageUploadSource',
194 joinColumn='packageupload')193 joinColumn='packageupload')
194 # Does not include source builds.
195 builds = SQLMultipleJoin('PackageUploadBuild',195 builds = SQLMultipleJoin('PackageUploadBuild',
196 joinColumn='packageupload')196 joinColumn='packageupload')
197197
198 def getSourceBuild(self):
199 #avoid circular import
200 from lp.code.model.sourcepackagerecipebuild import (
201 SourcePackageRecipeBuild)
202 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
203 return Store.of(self).find(
204 SourcePackageRecipeBuild,
205 SourcePackageRecipeBuild.id ==
206 SourcePackageRelease.source_package_recipe_build_id,
207 SourcePackageRelease.id ==
208 PackageUploadSource.sourcepackagereleaseID,
209 PackageUploadSource.packageupload == self.id).one()
210
198 # Also the custom files associated with the build.211 # Also the custom files associated with the build.
199 customfiles = SQLMultipleJoin('PackageUploadCustom',212 customfiles = SQLMultipleJoin('PackageUploadCustom',
200 joinColumn='packageupload')213 joinColumn='packageupload')
@@ -488,6 +501,10 @@
488 """See `IPackageUpload`."""501 """See `IPackageUpload`."""
489 return self.builds502 return self.builds
490503
504 @cachedproperty
505 def from_build(self):
506 return bool(self.builds) or self.getSourceBuild()
507
491 def isAutoSyncUpload(self, changed_by_email):508 def isAutoSyncUpload(self, changed_by_email):
492 """See `IPackageUpload`."""509 """See `IPackageUpload`."""
493 katie = getUtility(ILaunchpadCelebrities).katie510 katie = getUtility(ILaunchpadCelebrities).katie
@@ -1070,10 +1087,10 @@
10701087
1071 # If this is a binary or mixed upload, we don't send *any* emails1088 # If this is a binary or mixed upload, we don't send *any* emails
1072 # provided it's not a rejection or a security upload:1089 # provided it's not a rejection or a security upload:
1073 if(self.contains_build and1090 if(self.from_build and
1074 self.status != PackageUploadStatus.REJECTED and1091 self.status != PackageUploadStatus.REJECTED and
1075 self.pocket != PackagePublishingPocket.SECURITY):1092 self.pocket != PackagePublishingPocket.SECURITY):
1076 debug(self.logger, "Not sending email, upload contains binaries.")1093 debug(self.logger, "Not sending email; upload is from a build.")
1077 return1094 return
10781095
1079 # XXX julian 2007-05-11:1096 # XXX julian 2007-05-11:
10801097
=== modified file 'lib/lp/soyuz/tests/test_build_notify.py'
--- lib/lp/soyuz/tests/test_build_notify.py 2011-01-20 20:31:07 +0000
+++ lib/lp/soyuz/tests/test_build_notify.py 2011-01-27 17:17:51 +0000
@@ -114,7 +114,7 @@
114 duration = 'not finished'114 duration = 'not finished'
115 build_log = 'see builder page'115 build_log = 'see builder page'
116 elif (116 elif (
117 build.status == BuildStatus.SUPERSEDED or 117 build.status == BuildStatus.SUPERSEDED or
118 build.status == BuildStatus.NEEDSBUILD):118 build.status == BuildStatus.NEEDSBUILD):
119 duration = 'not available'119 duration = 'not available'
120 build_log = 'not available'120 build_log = 'not available'
@@ -203,20 +203,11 @@
203 self._assert_mail_is_correct(build, notification, ppa=True)203 self._assert_mail_is_correct(build, notification, ppa=True)
204204
205 def test_notify_successfully_built(self):205 def test_notify_successfully_built(self):
206 # We can notify the creator when the build is sucessful.206 # Successful builds don't notify anyone.
207 self.create_builds(self.archive)207 self.create_builds(self.archive)
208 build = self.builds[BuildStatus.FULLYBUILT.value]208 build = self.builds[BuildStatus.FULLYBUILT.value]
209 build.notify()209 build.notify()
210 notification = pop_notifications()[1]210 self.assertEqual([], pop_notifications())
211 self._assert_mail_is_correct(build, notification)
212
213 def test_notify_successfully_built_ppa(self):
214 # We can notify the creator when the build is sucessful.
215 self.create_builds(self.ppa)
216 build = self.builds[BuildStatus.FULLYBUILT.value]
217 build.notify()
218 notification = pop_notifications()[1]
219 self._assert_mail_is_correct(build, notification, ppa=True)
220211
221 def test_notify_dependency_wait(self):212 def test_notify_dependency_wait(self):
222 # We can notify the creator when the build can't find a dependency.213 # We can notify the creator when the build can't find a dependency.
@@ -321,7 +312,7 @@
321 # When the 'notify_owner' config option is False, we don't send mail312 # When the 'notify_owner' config option is False, we don't send mail
322 # to the owner of the SPR.313 # to the owner of the SPR.
323 self.create_builds(self.archive)314 self.create_builds(self.archive)
324 build = self.builds[BuildStatus.FULLYBUILT.value]315 build = self.builds[BuildStatus.FAILEDTOBUILD.value]
325 notify_owner = dedent("""316 notify_owner = dedent("""
326 [builddmaster]317 [builddmaster]
327 send_build_notification: True318 send_build_notification: True
@@ -357,13 +348,13 @@
357 sponsor = self.factory.makePerson('sponsor@example.com')348 sponsor = self.factory.makePerson('sponsor@example.com')
358 key = self.factory.makeGPGKey(owner=sponsor)349 key = self.factory.makeGPGKey(owner=sponsor)
359 self.create_builds(self.archive)350 self.create_builds(self.archive)
360 build = self.builds[BuildStatus.FULLYBUILT.value]351 build = self.builds[BuildStatus.FAILEDTOBUILD.value]
361 spr = build.current_source_publication.sourcepackagerelease352 spr = build.current_source_publication.sourcepackagerelease
362 # Push past the security proxy353 # Push past the security proxy
363 removeSecurityProxy(spr).dscsigningkey = key354 removeSecurityProxy(spr).dscsigningkey = key
364 build.notify()355 build.notify()
365 notifications = pop_notifications()356 notifications = pop_notifications()
366 expected_emails = self.buildd_admins_email + [357 expected_emails = self.buildd_admins_email + [
367 'sponsor@example.com', 'test@example.com'] 358 'sponsor@example.com', 'test@example.com']
368 actual_emails = [n['To'] for n in notifications]359 actual_emails = [n['To'] for n in notifications]
369 self.assertEquals(expected_emails, actual_emails)360 self.assertEquals(expected_emails, actual_emails)