Merge lp:~cjwatson/launchpad/better-upload-error-notifications into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18472
Proposed branch: lp:~cjwatson/launchpad/better-upload-error-notifications
Merge into: lp:launchpad
Diff against target: 438 lines (+129/-76)
11 files modified
lib/lp/archiveuploader/changesfile.py (+16/-10)
lib/lp/archiveuploader/dscfile.py (+10/-6)
lib/lp/archiveuploader/nascentupload.py (+3/-1)
lib/lp/archiveuploader/tests/nascentupload.txt (+19/-23)
lib/lp/archiveuploader/tests/nascentuploadfile.txt (+4/-0)
lib/lp/archiveuploader/tests/test_changesfile.py (+19/-13)
lib/lp/archiveuploader/tests/test_nascentuploadfile.py (+3/-1)
lib/lp/archiveuploader/tests/test_sync_notification.py (+2/-1)
lib/lp/archiveuploader/tests/test_uploadprocessor.py (+40/-2)
lib/lp/archiveuploader/uploadprocessor.py (+12/-17)
lib/lp/soyuz/doc/distroseriesqueue-translations.txt (+1/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/better-upload-error-notifications
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+311179@code.launchpad.net

Commit message

Send proper email notifications about most failures to parse the .changes file.

Description of the change

We can now send email notifications to the signer as long as we have a signature, even if the signing key is deactivated. If the .changes file is sufficiently parseable then we'll notify other relevant people as well.

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
=== modified file 'lib/lp/archiveuploader/changesfile.py'
--- lib/lp/archiveuploader/changesfile.py 2017-03-29 09:28:09 +0000
+++ lib/lp/archiveuploader/changesfile.py 2017-09-17 15:23:26 +0000
@@ -84,6 +84,11 @@
84 files = None84 files = None
8585
86 def __init__(self, filepath, policy, logger):86 def __init__(self, filepath, policy, logger):
87 self.filepath = filepath
88 self.policy = policy
89 self.logger = logger
90
91 def parseChanges(self):
87 """Process the given changesfile.92 """Process the given changesfile.
8893
89 Does:94 Does:
@@ -93,24 +98,25 @@
93 * Checks name of changes file98 * Checks name of changes file
94 * Checks signature of changes file99 * Checks signature of changes file
95100
96 If any of these checks fail, UploadError is raised, and it's101 If any of these checks fail, UploadError is yielded, and it should
97 considered a fatal error (no subsequent processing of the upload102 be considered a fatal error (no subsequent processing of the upload
98 will be done).103 should be done).
99104
100 Logger and Policy are instances built in uploadprocessor.py passed105 Logger and Policy are instances built in uploadprocessor.py passed
101 via NascentUpload class.106 via NascentUpload class.
102 """107 """
103 self.filepath = filepath108 try:
104 self.policy = policy109 self.parse(verify_signature=not self.policy.unsigned_changes_ok)
105 self.logger = logger110 except UploadError as e:
106111 yield e
107 self.parse(verify_signature=not policy.unsigned_changes_ok)112 return
108113
109 for field in self.mandatory_fields:114 for field in self.mandatory_fields:
110 if field not in self._dict:115 if field not in self._dict:
111 raise UploadError(116 yield UploadError(
112 "Unable to find mandatory field '%s' in the changes "117 "Unable to find mandatory field '%s' in the changes "
113 "file." % field)118 "file." % field)
119 return
114120
115 try:121 try:
116 format = float(self._dict["Format"])122 format = float(self._dict["Format"])
@@ -119,7 +125,7 @@
119 format = 1.5125 format = 1.5
120126
121 if format < 1.5 or format > 2.0:127 if format < 1.5 or format > 2.0:
122 raise UploadError(128 yield UploadError(
123 "Format out of acceptable range for changes file. Range "129 "Format out of acceptable range for changes file. Range "
124 "1.5 - 2.0, format %g" % format)130 "1.5 - 2.0, format %g" % format)
125131
126132
=== modified file 'lib/lp/archiveuploader/dscfile.py'
--- lib/lp/archiveuploader/dscfile.py 2017-07-31 11:45:32 +0000
+++ lib/lp/archiveuploader/dscfile.py 2017-09-17 15:23:26 +0000
@@ -142,8 +142,16 @@
142 "Unable to read %s: %s" % (self.filename, error))142 "Unable to read %s: %s" % (self.filename, error))
143143
144 if verify_signature:144 if verify_signature:
145 self.signingkey, self.parsed_content = self.verifySignature(145 # We set self.signingkey regardless of whether the key is
146 # deactivated, since a deactivated key is still good enough for
147 # determining whom to notify, and raising UploadError is enough
148 # to prevent the upload being accepted.
149 self.signingkey, self.parsed_content = self._verifySignature(
146 self.raw_content, self.filepath)150 self.raw_content, self.filepath)
151 if not self.signingkey.active:
152 raise UploadError("File %s is signed with a deactivated key %s"
153 % (self.filepath,
154 self.signingkey.fingerprint))
147 else:155 else:
148 self.logger.debug("%s can be unsigned." % self.filename)156 self.logger.debug("%s can be unsigned." % self.filename)
149 self.parsed_content = self.raw_content157 self.parsed_content = self.raw_content
@@ -154,7 +162,7 @@
154 raise UploadError(162 raise UploadError(
155 "Unable to parse %s: %s" % (self.filename, error))163 "Unable to parse %s: %s" % (self.filename, error))
156164
157 def verifySignature(self, content, filename):165 def _verifySignature(self, content, filename):
158 """Verify the signature on the file content.166 """Verify the signature on the file content.
159167
160 Raise UploadError if the signing key cannot be found in launchpad168 Raise UploadError if the signing key cannot be found in launchpad
@@ -179,10 +187,6 @@
179 raise UploadError("Signing key %s not registered in launchpad."187 raise UploadError("Signing key %s not registered in launchpad."
180 % sig.fingerprint)188 % sig.fingerprint)
181189
182 if key.active == False:
183 raise UploadError("File %s is signed with a deactivated key %s"
184 % (filename, key.fingerprint))
185
186 return (key, sig.plain_data)190 return (key, sig.plain_data)
187191
188 def parseAddress(self, addr, fieldname="Maintainer"):192 def parseAddress(self, addr, fieldname="Maintainer"):
189193
=== modified file 'lib/lp/archiveuploader/nascentupload.py'
--- lib/lp/archiveuploader/nascentupload.py 2017-04-21 15:21:27 +0000
+++ lib/lp/archiveuploader/nascentupload.py 2017-09-17 15:23:26 +0000
@@ -142,6 +142,8 @@
142 policy = self.policy142 policy = self.policy
143 self.logger.debug("Beginning processing.")143 self.logger.debug("Beginning processing.")
144144
145 self.run_and_reject_on_error(self.changes.parseChanges)
146
145 try:147 try:
146 policy.setDistroSeriesAndPocket(self.changes.suite_name)148 policy.setDistroSeriesAndPocket(self.changes.suite_name)
147 except NotFoundError:149 except NotFoundError:
@@ -780,7 +782,7 @@
780 IDistributionSet)['ubuntu'].currentseries782 IDistributionSet)['ubuntu'].currentseries
781 return distroseries.createQueueEntry(783 return distroseries.createQueueEntry(
782 PackagePublishingPocket.RELEASE,784 PackagePublishingPocket.RELEASE,
783 distroseries.main_archive, self.changes.filename,785 self.policy.archive, self.changes.filename,
784 self.changes.raw_content, signing_key=self.changes.signingkey)786 self.changes.raw_content, signing_key=self.changes.signingkey)
785 else:787 else:
786 return distroseries.createQueueEntry(788 return distroseries.createQueueEntry(
787789
=== modified file 'lib/lp/archiveuploader/tests/nascentupload.txt'
--- lib/lp/archiveuploader/tests/nascentupload.txt 2016-01-26 15:47:37 +0000
+++ lib/lp/archiveuploader/tests/nascentupload.txt 2017-09-17 15:23:26 +0000
@@ -39,38 +39,34 @@
39 ... name='anything', distro='ubuntu', distroseries='hoary')39 ... name='anything', distro='ubuntu', distroseries='hoary')
4040
4141
42Constructing a NascentUpload object42NascentUpload Processing
43-----------------------------------43------------------------
4444
45Constructing a NascentUpload instance verifies that the changes file45Processing a NascentUpload consists of building files objects for each
46specified exists and tries to build a ChangesFile (see46specified file in the upload, executing all their specific checks and
47doc/nascentuploadfile.txt) object based on that. If anything goes47collect all errors that may be generated. (see doc/nascentuploadfile.txt)
48wrong during this process UploadError is raised:48
49First, NascentUpload verifies that the changes file specified exist, and
50tries to build a ChangesFile (see doc/nascentuploadfile.txt) object based
51on that.
4952
50 >>> from lp.services.log.logger import DevNullLogger, FakeLogger53 >>> from lp.services.log.logger import DevNullLogger, FakeLogger
51 >>> NascentUpload.from_changesfile_path(54
55 >>> nonexistent = NascentUpload.from_changesfile_path(
52 ... datadir("DOES-NOT-EXIST"), buildd_policy, FakeLogger())56 ... datadir("DOES-NOT-EXIST"), buildd_policy, FakeLogger())
57 >>> nonexistent.process()
53 Traceback (most recent call last):58 Traceback (most recent call last):
54 ...59 ...
55 UploadError:...60 EarlyReturnUploadError: An error occurred that prevented further
5661 processing.
57Otherwise a ChangesFile object is ready to use.62 >>> nonexistent.is_rejected
63 True
64 >>> print nonexistent.rejection_message
65 Unable to read DOES-NOT-EXIST: ...
5866
59 >>> quodlibet = NascentUpload.from_changesfile_path(67 >>> quodlibet = NascentUpload.from_changesfile_path(
60 ... datadir("quodlibet_0.13.1-1_i386.changes"),68 ... datadir("quodlibet_0.13.1-1_i386.changes"),
61 ... anything_policy, DevNullLogger())69 ... anything_policy, DevNullLogger())
62
63 >>> quodlibet.changes
64 <lp.archiveuploader.changesfile.ChangesFile ...>
65
66
67NascentUpload Processing
68------------------------
69
70Processing a NascentUpload consists of building files objects for each
71specified file in the upload, executing all their specific checks and
72collect all errors that may be generated. (see doc/nascentuploadfile.txt)
73
74 >>> quodlibet.process()70 >>> quodlibet.process()
75 >>> for f in quodlibet.changes.files:71 >>> for f in quodlibet.changes.files:
76 ... print f.filename, f72 ... print f.filename, f
7773
=== modified file 'lib/lp/archiveuploader/tests/nascentuploadfile.txt'
--- lib/lp/archiveuploader/tests/nascentuploadfile.txt 2017-08-03 14:26:40 +0000
+++ lib/lp/archiveuploader/tests/nascentuploadfile.txt 2017-09-17 15:23:26 +0000
@@ -72,10 +72,14 @@
72 >>> ed_binary_changes = ChangesFile(72 >>> ed_binary_changes = ChangesFile(
73 ... datadir('ed_0.2-20_i386.changes.binary-only'),73 ... datadir('ed_0.2-20_i386.changes.binary-only'),
74 ... modified_insecure_policy, DevNullLogger())74 ... modified_insecure_policy, DevNullLogger())
75 >>> len(list(ed_binary_changes.parseChanges()))
76 0
7577
76 >>> ed_source_changes = ChangesFile(78 >>> ed_source_changes = ChangesFile(
77 ... datadir('ed_0.2-20_source.changes'),79 ... datadir('ed_0.2-20_source.changes'),
78 ... modified_insecure_policy, DevNullLogger())80 ... modified_insecure_policy, DevNullLogger())
81 >>> len(list(ed_source_changes.parseChanges()))
82 0
7983
80Make sure we are not getting any exceptions due to a malformed changes84Make sure we are not getting any exceptions due to a malformed changes
81file name.85file name.
8286
=== modified file 'lib/lp/archiveuploader/tests/test_changesfile.py'
--- lib/lp/archiveuploader/tests/test_changesfile.py 2017-03-29 09:28:09 +0000
+++ lib/lp/archiveuploader/tests/test_changesfile.py 2017-09-17 15:23:26 +0000
@@ -182,7 +182,10 @@
182 changes.dump(changes_fd)182 changes.dump(changes_fd)
183 finally:183 finally:
184 changes_fd.close()184 changes_fd.close()
185 return ChangesFile(path, self.policy, self.logger)185 changesfile = ChangesFile(path, self.policy, self.logger)
186 for error in changesfile.parseChanges():
187 raise error
188 return changesfile
186189
187 def getBaseChanges(self):190 def getBaseChanges(self):
188 contents = Changes()191 contents = Changes()
@@ -364,36 +367,39 @@
364 import_public_test_keys()367 import_public_test_keys()
365368
366 def test_valid_signature_accepted(self):369 def test_valid_signature_accepted(self):
367 # A correctly signed changes file is excepted, and all its370 # A correctly signed changes file is accepted, and all its
368 # content is parsed.371 # content is parsed.
369 path = datadir('signatures/signed.changes')372 path = datadir('signatures/signed.changes')
370 parsed = ChangesFile(path, InsecureUploadPolicy(), BufferLogger())373 changesfile = ChangesFile(path, InsecureUploadPolicy(), BufferLogger())
374 self.assertEqual([], list(changesfile.parseChanges()))
371 self.assertEqual(375 self.assertEqual(
372 getUtility(IPersonSet).getByEmail('foo.bar@canonical.com'),376 getUtility(IPersonSet).getByEmail('foo.bar@canonical.com'),
373 parsed.signer)377 changesfile.signer)
374 expected = "\AFormat: 1.7\n.*foo_1.0-1.diff.gz\Z"378 expected = "\AFormat: 1.7\n.*foo_1.0-1.diff.gz\Z"
375 self.assertTextMatchesExpressionIgnoreWhitespace(379 self.assertTextMatchesExpressionIgnoreWhitespace(
376 expected,380 expected,
377 parsed.parsed_content)381 changesfile.parsed_content)
378382
379 def test_no_signature_rejected(self):383 def test_no_signature_rejected(self):
380 # An unsigned changes file is rejected.384 # An unsigned changes file is rejected.
381 path = datadir('signatures/unsigned.changes')385 path = datadir('signatures/unsigned.changes')
382 self.assertRaises(386 changesfile = ChangesFile(path, InsecureUploadPolicy(), BufferLogger())
383 UploadError,387 errors = list(changesfile.parseChanges())
384 ChangesFile, path, InsecureUploadPolicy(), BufferLogger())388 self.assertIsInstance(errors[0], UploadError)
389 self.assertEqual(1, len(errors))
385390
386 def test_prefix_ignored(self):391 def test_prefix_ignored(self):
387 # A signed changes file with an unsigned prefix has only the392 # A signed changes file with an unsigned prefix has only the
388 # signed part parsed.393 # signed part parsed.
389 path = datadir('signatures/prefixed.changes')394 path = datadir('signatures/prefixed.changes')
390 parsed = ChangesFile(path, InsecureUploadPolicy(), BufferLogger())395 changesfile = ChangesFile(path, InsecureUploadPolicy(), BufferLogger())
396 self.assertEqual([], list(changesfile.parseChanges()))
391 self.assertEqual(397 self.assertEqual(
392 getUtility(IPersonSet).getByEmail('foo.bar@canonical.com'),398 getUtility(IPersonSet).getByEmail('foo.bar@canonical.com'),
393 parsed.signer)399 changesfile.signer)
394 expected = "\AFormat: 1.7\n.*foo_1.0-1.diff.gz\Z"400 expected = "\AFormat: 1.7\n.*foo_1.0-1.diff.gz\Z"
395 self.assertTextMatchesExpressionIgnoreWhitespace(401 self.assertTextMatchesExpressionIgnoreWhitespace(
396 expected,402 expected,
397 parsed.parsed_content)403 changesfile.parsed_content)
398 self.assertEqual("breezy", parsed.suite_name)404 self.assertEqual("breezy", changesfile.suite_name)
399 self.assertNotIn("evil", parsed.changes_comment)405 self.assertNotIn("evil", changesfile.changes_comment)
400406
=== modified file 'lib/lp/archiveuploader/tests/test_nascentuploadfile.py'
--- lib/lp/archiveuploader/tests/test_nascentuploadfile.py 2016-11-08 20:22:59 +0000
+++ lib/lp/archiveuploader/tests/test_nascentuploadfile.py 2017-09-17 15:23:26 +0000
@@ -208,7 +208,9 @@
208 path = os.path.join(tempdir, filename)208 path = os.path.join(tempdir, filename)
209 with open(path, "w") as changes_fd:209 with open(path, "w") as changes_fd:
210 changes.dump(changes_fd)210 changes.dump(changes_fd)
211 return ChangesFile(path, self.policy, self.logger)211 changesfile = ChangesFile(path, self.policy, self.logger)
212 self.assertEqual([], list(changesfile.parseChanges()))
213 return changesfile
212214
213215
214class DSCFileTests(PackageUploadFileTestCase):216class DSCFileTests(PackageUploadFileTestCase):
215217
=== modified file 'lib/lp/archiveuploader/tests/test_sync_notification.py'
--- lib/lp/archiveuploader/tests/test_sync_notification.py 2015-08-26 13:41:21 +0000
+++ lib/lp/archiveuploader/tests/test_sync_notification.py 2017-09-17 15:23:26 +0000
@@ -1,4 +1,4 @@
1# Copyright 2012-2015 Canonical Ltd. This software is licensed under the1# Copyright 2012-2016 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test notification behaviour for cross-distro package syncs."""4"""Test notification behaviour for cross-distro package syncs."""
@@ -55,6 +55,7 @@
55 self.raw_content = open(file_path).read()55 self.raw_content = open(file_path).read()
56 self.signingkey = None56 self.signingkey = None
5757
58 parseChanges = FakeMethod([])
58 checkFileName = FakeMethod([])59 checkFileName = FakeMethod([])
59 processAddresses = FakeMethod([])60 processAddresses = FakeMethod([])
60 processFiles = FakeMethod([])61 processFiles = FakeMethod([])
6162
=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
--- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2017-04-21 15:21:27 +0000
+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2017-09-17 15:23:26 +0000
@@ -51,6 +51,7 @@
51 IBuildFarmJobBehaviour,51 IBuildFarmJobBehaviour,
52 )52 )
53from lp.registry.interfaces.distribution import IDistributionSet53from lp.registry.interfaces.distribution import IDistributionSet
54from lp.registry.interfaces.gpg import IGPGKeySet
54from lp.registry.interfaces.person import IPersonSet55from lp.registry.interfaces.person import IPersonSet
55from lp.registry.interfaces.pocket import PackagePublishingPocket56from lp.registry.interfaces.pocket import PackagePublishingPocket
56from lp.registry.interfaces.series import SeriesStatus57from lp.registry.interfaces.series import SeriesStatus
@@ -240,7 +241,7 @@
240 excName = excClass.__name__241 excName = excClass.__name__
241 else:242 else:
242 excName = str(excClass)243 excName = str(excClass)
243 raise self.failureException, "%s not raised" % excName244 raise self.failureException("%s not raised" % excName)
244245
245 def setupBreezy(self, name="breezy", permitted_formats=None):246 def setupBreezy(self, name="breezy", permitted_formats=None):
246 """Create a fresh distroseries in ubuntu.247 """Create a fresh distroseries in ubuntu.
@@ -1955,10 +1956,47 @@
19551956
1956 self.assertEqual(UploadStatusEnum.REJECTED, result)1957 self.assertEqual(UploadStatusEnum.REJECTED, result)
1957 self.assertLogContains(1958 self.assertLogContains(
1958 "INFO Failed to parse changes file")1959 "INFO Not sending rejection notice without a signing key.")
1959 self.assertEmailQueueLength(0)1960 self.assertEmailQueueLength(0)
1960 self.assertEqual([], self.oopses)1961 self.assertEqual([], self.oopses)
19611962
1963 def test_deactivated_key_upload_sends_mail(self):
1964 # An upload signed with a deactivated key does not OOPS and sends a
1965 # rejection email.
1966 self.switchToAdmin()
1967 fingerprint = "340CA3BB270E2716C9EE0B768E7EB7086C64A8C5"
1968 gpgkeyset = getUtility(IGPGKeySet)
1969 gpgkeyset.deactivate(gpgkeyset.getByFingerprint(fingerprint))
1970 self.switchToUploader()
1971
1972 uploadprocessor = self.setupBreezyAndGetUploadProcessor()
1973 upload_dir = self.queueUpload("netapplet_1.0-1-signed")
1974
1975 [result] = self.processUpload(uploadprocessor, upload_dir)
1976
1977 self.assertEqual(UploadStatusEnum.REJECTED, result)
1978 base_contents = [
1979 "Subject: [ubuntu] netapplet_1.0-1_source.changes (Rejected)",
1980 "File %s/netapplet_1.0-1-signed/netapplet_1.0-1_source.changes "
1981 "is signed with a deactivated key %s" % (
1982 self.incoming_folder, fingerprint),
1983 ]
1984 expected = []
1985 expected.append({
1986 "contents": base_contents + [
1987 "You are receiving this email because you are the most "
1988 "recent person",
1989 "listed in this package's changelog."],
1990 "recipient": "daniel.silverstone@canonical.com",
1991 })
1992 expected.append({
1993 "contents": base_contents + [
1994 "You are receiving this email because you made this upload."],
1995 "recipient": "foo.bar@canonical.com",
1996 })
1997 self.assertEmails(expected)
1998 self.assertEqual([], self.oopses)
1999
1962 def test_ddeb_upload_overrides(self):2000 def test_ddeb_upload_overrides(self):
1963 # DDEBs should always be overridden to the same values as their2001 # DDEBs should always be overridden to the same values as their
1964 # counterpart DEB's.2002 # counterpart DEB's.
19652003
=== modified file 'lib/lp/archiveuploader/uploadprocessor.py'
--- lib/lp/archiveuploader/uploadprocessor.py 2017-01-10 15:18:48 +0000
+++ lib/lp/archiveuploader/uploadprocessor.py 2017-09-17 15:23:26 +0000
@@ -338,22 +338,8 @@
338 # The path we want for NascentUpload is the path to the folder338 # The path we want for NascentUpload is the path to the folder
339 # containing the changes file (and the other files referenced by it).339 # containing the changes file (and the other files referenced by it).
340 changesfile_path = os.path.join(self.upload_path, changes_file)340 changesfile_path = os.path.join(self.upload_path, changes_file)
341 try:341 upload = NascentUpload.from_changesfile_path(
342 upload = NascentUpload.from_changesfile_path(342 changesfile_path, policy, self.processor.log)
343 changesfile_path, policy, self.processor.log)
344 except UploadError as e:
345 # We failed to parse the changes file, so we have no key or
346 # Changed-By to notify of the rejection. Just log it and
347 # move on.
348 # XXX wgrant 2011-09-29 bug=499438: With some refactoring we
349 # could do better here: if we have a signature then we have
350 # somebody to email, even if the rest of the file is
351 # corrupt.
352 logger.info(
353 "Failed to parse changes file '%s': %s" % (
354 os.path.join(self.upload_path, changes_file),
355 str(e)))
356 return UploadStatusEnum.REJECTED
357343
358 # Reject source upload to buildd upload paths.344 # Reject source upload to buildd upload paths.
359 first_path = relative_path.split(os.path.sep)[0]345 first_path = relative_path.split(os.path.sep)[0]
@@ -411,7 +397,16 @@
411 notify = False397 notify = False
412 if upload.is_rejected:398 if upload.is_rejected:
413 result = UploadStatusEnum.REJECTED399 result = UploadStatusEnum.REJECTED
414 upload.do_reject(notify)400 if upload.changes.parsed_content is not None:
401 # We got past the point of checking any required
402 # signature, so we can do a proper rejection.
403 upload.do_reject(notify)
404 else:
405 # The upload required a signature and either didn't have
406 # one or we failed to verify it, so we have nobody to
407 # notify. Just log it and move on.
408 logger.info(
409 "Not sending rejection notice without a signing key.")
415 self.processor.ztm.abort()410 self.processor.ztm.abort()
416 else:411 else:
417 successful = self._acceptUpload(upload, notify)412 successful = self._acceptUpload(upload, notify)
418413
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-translations.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-translations.txt 2015-09-04 12:19:07 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-translations.txt 2017-09-17 15:23:26 +0000
@@ -87,10 +87,9 @@
87 >>> pmount_upload = NascentUpload.from_changesfile_path(87 >>> pmount_upload = NascentUpload.from_changesfile_path(
88 ... datadir('pmount_0.9.7-2ubuntu2_amd64.changes'),88 ... datadir('pmount_0.9.7-2ubuntu2_amd64.changes'),
89 ... buildd_policy, FakeLogger())89 ... buildd_policy, FakeLogger())
90 DEBUG pmount_0.9.7-2ubuntu2_amd64.changes can be unsigned.
91
92 >>> pmount_upload.process(build=build)90 >>> pmount_upload.process(build=build)
93 DEBUG Beginning processing.91 DEBUG Beginning processing.
92 DEBUG pmount_0.9.7-2ubuntu2_amd64.changes can be unsigned.
94 DEBUG Verifying the changes file.93 DEBUG Verifying the changes file.
95 DEBUG Verifying files in upload.94 DEBUG Verifying files in upload.
96 DEBUG Verifying binary pmount_0.9.7-2ubuntu2_amd64.deb95 DEBUG Verifying binary pmount_0.9.7-2ubuntu2_amd64.deb