def _doUpload(self, type, version, policy, archive, distribution_name,
                  suite, logger, notify):
        """Upload a given version.

        Raises an error if the version couldn't be found or the upload
        was rejected.

        Build a upload policy with the given name and override it with
        archive, distribution_name and suite if passed.

        Return the corresponding `NascentUpload` object.
        """
        changesfile_path = self._getChangefilePathForVersion(version, type)
        assert changesfile_path is not None, (
            "Could not find a %s upload for version %s." % (type, version))

        if archive is not None:
            policy.archive = archive

        policy.distro = getUtility(IDistributionSet).getByName(
            distribution_name)

        if suite is not None:
            policy.setDistroSeriesAndPocket(suite)

        upload = NascentUpload.from_changesfile_path(
            changesfile_path, policy, logger)
        upload.process()

        return upload
 def makeNascentUpload(self, spph, maintainer, maintainer_address,
                       changer, changer_address):
     """Create a `NascentUpload` for `spph`."""
     changes = self.makeChangesFile(
         spph, maintainer, maintainer_address, changer, changer_address)
     upload = NascentUpload(
         changes, FakeUploadPolicy(spph), DevNullLogger())
     upload.queue_root = upload._createQueueEntry()
     das = self.factory.makeDistroArchSeries(
         distroseries=spph.distroseries)
     bpb = self.factory.makeBinaryPackageBuild(
         source_package_release=spph.sourcepackagerelease,
         archive=spph.archive, distroarchseries=das, pocket=spph.pocket,
         sourcepackagename=spph.sourcepackagename)
     upload.queue_root.addBuild(bpb)
     return upload
    def _doUpload(self, type, version, policy, archive, distribution_name,
                  suite, logger, notify):
        """Upload a given version.

        Raises an error if the version couldn't be found or the upload
        was rejected.

        Build a upload policy with the given name and override it with
        archive, distribution_name and suite if passed.

        Return the corresponding `NascentUpload` object.
        """
        changesfile_path = self._getChangefilePathForVersion(version, type)
        assert changesfile_path is not None, (
            "Could not find a %s upload for version %s." % (type, version))

        if archive is not None:
            policy.archive = archive

        policy.distro = getUtility(IDistributionSet).getByName(
            distribution_name)

        if suite is not None:
            policy.setDistroSeriesAndPocket(suite)

        upload = NascentUpload.from_changesfile_path(changesfile_path, policy,
                                                     logger)
        upload.process()

        return upload
Beispiel #4
0
 def makeNascentUpload(self, spph, maintainer, maintainer_address, changer,
                       changer_address):
     """Create a `NascentUpload` for `spph`."""
     changes = self.makeChangesFile(spph, maintainer, maintainer_address,
                                    changer, changer_address)
     upload = NascentUpload(changes, FakeUploadPolicy(spph),
                            DevNullLogger())
     upload.queue_root = upload._createQueueEntry()
     das = self.factory.makeDistroArchSeries(distroseries=spph.distroseries)
     bpb = self.factory.makeBinaryPackageBuild(
         source_package_release=spph.sourcepackagerelease,
         archive=spph.archive,
         distroarchseries=das,
         pocket=spph.pocket,
         sourcepackagename=spph.sourcepackagename)
     upload.queue_root.addBuild(bpb)
     return upload
 def uploadTestData(self, version):
     upload = NascentUpload.from_changesfile_path(
         datadir("dist-upgrader/dist-upgrader_%s_all.changes" % version),
         self.anything_policy, self.logger)
     upload.process()
     self.assertFalse(upload.is_rejected)
     self.assertTrue(upload.do_accept())
     self.assertFalse(upload.rejection_message)
     return upload
 def uploadTestData(self, version):
     upload = NascentUpload.from_changesfile_path(
         datadir("ddtp-tarball/translations-main_%s_all.changes" % version), self.anything_policy, self.logger
     )
     upload.process()
     self.assertFalse(upload.is_rejected)
     self.assertTrue(upload.do_accept())
     self.assertFalse(upload.rejection_message)
     return upload
    def uploadToPPA(self):
        # Setup PPA owner and archive
        self.name16 = getUtility(IPersonSet).getByName('name16')
        name16_archive = self.factory.makeArchive(
            distribution=self.breezy_autotest.distribution,
            owner=self.name16,
            name="ppa")

        policy = self.absolutely_anything_policy
        policy.archive = name16_archive

        upload = NascentUpload.from_changesfile_path(
            datadir("rosetta-translations/%s" % self.source_changes_file),
            policy, self.logger)
        upload.process()

        self.assertFalse(upload.is_rejected)
        self.assertTrue(upload.do_accept())

        self.assertEqual(upload.queue_root.status, PackageUploadStatus.DONE)
        spph = self.name16.archive.getPublishedSources(name="pmount").one()
        self.assertIsNotNone(spph)
        transaction.commit()

        policy.accepted_type = ArchiveUploadType.BINARY_ONLY

        bin_upload = NascentUpload.from_changesfile_path(
            datadir("rosetta-translations/%s" % self.bin_changes_file), policy,
            self.logger)
        bin_upload.process()

        self.assertFalse(bin_upload.is_rejected)
        self.assertTrue(bin_upload.do_accept())
        self.assertEqual(bin_upload.queue_root.status,
                         PackageUploadStatus.ACCEPTED)

        bin_upload.queue_root.realiseUpload()
        self.assertEqual(bin_upload.queue_root.status,
                         PackageUploadStatus.DONE)
        self.assertEqual(bin_upload.queue_root.builds[0].build.status.name,
                         "FULLYBUILT")
        transaction.commit()

        return upload, spph, bin_upload
Beispiel #8
0
 def uploadTestData(self):
     upload = NascentUpload.from_changesfile_path(
         datadir("debian-installer/"
                 "debian-installer_20070214ubuntu1_i386.changes"),
         self.anything_policy, self.logger)
     upload.process()
     self.assertFalse(upload.is_rejected)
     self.assertTrue(upload.do_accept())
     self.assertFalse(upload.rejection_message)
     return upload
 def uploadTestData(self):
     upload = NascentUpload.from_changesfile_path(
         datadir(
             "debian-installer/"
             "debian-installer_20070214ubuntu1_i386.changes"),
         self.anything_policy, self.logger)
     upload.process()
     self.assertFalse(upload.is_rejected)
     self.assertTrue(upload.do_accept())
     self.assertFalse(upload.rejection_message)
     return upload
 def test_hash_mismatch_rejects(self):
     # A hash mismatch for any uploaded file will cause the upload to
     # be rejected.
     policy = getPolicy(name="sync", distro="ubuntu", distroseries="hoary")
     policy.accepted_type = ArchiveUploadType.BINARY_ONLY
     upload = NascentUpload.from_changesfile_path(
         datadir("suite/badhash_1.0-1/badhash_1.0-1_i386.changes"), policy,
         DevNullLogger())
     upload.process()
     self.assertTrue(upload.is_rejected)
     self.assertEqual(
         'File badhash_1.0-1_i386.deb mentioned in the changes has a SHA1 '
         'mismatch. 2ca33cf32a45852c62b465aaf9063fb7deb31725 != '
         '91556113ad38eb35d2fe03d27ae646e0ed487a3d',
         upload.rejection_message)
 def test_hash_mismatch_rejects(self):
     # A hash mismatch for any uploaded file will cause the upload to
     # be rejected.
     policy = getPolicy(name="sync", distro="ubuntu", distroseries="hoary")
     policy.accepted_type = ArchiveUploadType.BINARY_ONLY
     upload = NascentUpload.from_changesfile_path(
         datadir("suite/badhash_1.0-1/badhash_1.0-1_i386.changes"), policy, DevNullLogger()
     )
     upload.process()
     self.assertTrue(upload.is_rejected)
     self.assertEqual(
         "File badhash_1.0-1_i386.deb mentioned in the changes has a SHA1 "
         "mismatch. 2ca33cf32a45852c62b465aaf9063fb7deb31725 != "
         "91556113ad38eb35d2fe03d27ae646e0ed487a3d",
         upload.rejection_message,
     )
 def test_ppa_publishing_location(self):
     # A PPA dist-upgrader upload is published to the right place.
     archive = self.factory.makeArchive(distribution=self.ubuntutest)
     self.anything_policy.archive = archive
     ppa_upload = self.uploadTestData("20060302.0120")
     ppa_upload = NascentUpload.from_changesfile_path(
         datadir("dist-upgrader/dist-upgrader_20060302.0120_all.changes"),
         self.anything_policy, self.logger)
     ppa_upload.process()
     self.assertTrue(ppa_upload.do_accept())
     transaction.commit()
     ppa_upload.queue_root.realiseUpload(self.logger)
     ppa_root = config.personalpackagearchive.root
     ppa_dir = os.path.join(ppa_root, archive.owner.name, archive.name)
     target_dir = os.path.join(
         ppa_dir, "ubuntutest/dists/breezy-autotest/main/dist-upgrader-all")
     self.assertContentEqual(
         ["20060302.0120", "current"], os.listdir(target_dir))
 def test_ppa_publishing_location(self):
     # A PPA dist-upgrader upload is published to the right place.
     archive = self.factory.makeArchive(distribution=self.ubuntutest)
     self.anything_policy.archive = archive
     ppa_upload = self.uploadTestData("20060302.0120")
     ppa_upload = NascentUpload.from_changesfile_path(
         datadir("dist-upgrader/dist-upgrader_20060302.0120_all.changes"),
         self.anything_policy, self.logger)
     ppa_upload.process()
     self.assertTrue(ppa_upload.do_accept())
     transaction.commit()
     ppa_upload.queue_root.realiseUpload(self.logger)
     ppa_root = config.personalpackagearchive.root
     ppa_dir = os.path.join(ppa_root, archive.owner.name, archive.name)
     target_dir = os.path.join(
         ppa_dir, "ubuntutest/dists/breezy-autotest/main/dist-upgrader-all")
     self.assertContentEqual(["20060302.0120", "current"],
                             os.listdir(target_dir))
    def uploadTestData(self, name=None, version=None):
        if name is None:
            name = self.package_name
        if version is None:
            version = self.version

        spph = self.getPubSource(sourcename=name,
                                 version=version,
                                 distroseries=self.breezy_autotest,
                                 status=PackagePublishingStatus.PUBLISHED)
        self.spr = spph.sourcepackagerelease
        upload = NascentUpload.from_changesfile_path(
            datadir("rosetta-translations/%s" % self.bin_changes_file),
            self.absolutely_anything_policy, self.logger)

        upload.process()

        self.assertFalse(upload.is_rejected)
        self.assertTrue(upload.do_accept())
        self.assertFalse(upload.rejection_message)
        # Accepting the queue entry because there's no ancestry, so not
        # auto-accepted
        upload.queue_root.setAccepted()
        return upload
 def test_rejects_misspelled_changesfile_name(self):
     upload = NascentUpload.from_changesfile_path(
         datadir("dist-upgrader/dist-upgrader_20060302.0120.changes"),
         self.absolutely_anything_policy, self.logger)
     self.assertRaises(EarlyReturnUploadError, upload.process)
class TestMatchDDEBs(TestCase):
    """Tests that NascentUpload correctly links DEBs to their DDEBs.

    Also verifies detection of DDEB-related error cases.
    """

    layer = LaunchpadZopelessLayer

    def setUp(self):
        super(TestMatchDDEBs, self).setUp()
        self.changes = FakeChangesFile()
        self.upload = NascentUpload(self.changes, None, DevNullLogger())

    def addFile(self,
                filename,
                comp_and_section='main/devel',
                priority='extra'):
        """Add a file of the right type to the upload."""
        package, cls = determine_file_class_and_name(filename)
        file = cls(filename, None, 100, comp_and_section, priority, package,
                   '666', self.changes, None, self.upload.logger)
        self.changes.files.append(file)
        return file

    def assertMatchDDEBErrors(self, error_list):
        self.assertEqual(error_list,
                         [str(e) for e in self.upload._matchDDEBs()])

    def testNoLinksWithNoBinaries(self):
        # No links will be made if there are no binaries whatsoever.
        self.addFile('something_1.0.diff.gz')
        self.assertMatchDDEBErrors([])

    def testNoLinksWithJustDEBs(self):
        # No links will be made if there are no DDEBs.
        self.addFile('blah_1.0_all.deb')
        self.addFile('libblah_1.0_i386.deb')
        self.assertMatchDDEBErrors([])
        for file in self.changes.files:
            self.assertIs(None, file.ddeb_file)

    def testLinksMatchingDDEBs(self):
        # DDEBs will be linked to their matching DEBs.
        self.addFile('blah_1.0_all.deb')
        self.addFile('libblah_1.0_i386.deb')
        self.addFile('libblah-dbgsym_1.0_i386.ddeb')
        self.addFile('libfooble_1.0_i386.udeb')
        self.addFile('libfooble-dbgsym_1.0_i386.ddeb')
        self.assertMatchDDEBErrors([])
        self.assertIs(None, self.changes.files[0].ddeb_file)
        self.assertIs(self.changes.files[2], self.changes.files[1].ddeb_file)
        self.assertIs(self.changes.files[1], self.changes.files[2].deb_file)
        self.assertIs(None, self.changes.files[2].ddeb_file)

    def testDuplicateDDEBsCauseErrors(self):
        # An error will be raised if a DEB has more than one matching
        # DDEB.
        self.addFile('libblah_1.0_i386.deb')
        self.addFile('libblah-dbgsym_1.0_i386.ddeb')
        self.addFile('libblah-dbgsym_1.0_i386.ddeb')
        self.assertMatchDDEBErrors(
            ['Duplicated debug packages: libblah-dbgsym 666 (i386)'])

    def testMismatchedDDEBsCauseErrors(self):
        # An error will be raised if a DDEB has no matching DEB.
        self.addFile('libblah_1.0_i386.deb')
        self.addFile('libblah-dbgsym_1.0_amd64.ddeb')
        self.assertMatchDDEBErrors(
            ['Orphaned debug packages: libblah-dbgsym 666 (amd64)'])
 def setUp(self):
     super(TestMatchDDEBs, self).setUp()
     self.changes = FakeChangesFile()
     self.upload = NascentUpload(self.changes, None, DevNullLogger())
class TestMatchDDEBs(TestCase):
    """Tests that NascentUpload correctly links DEBs to their DDEBs.

    Also verifies detection of DDEB-related error cases.
    """

    layer = LaunchpadZopelessLayer

    def setUp(self):
        super(TestMatchDDEBs, self).setUp()
        self.changes = FakeChangesFile()
        self.upload = NascentUpload(self.changes, None, DevNullLogger())

    def addFile(self, filename, comp_and_section="main/devel", priority="extra"):
        """Add a file of the right type to the upload."""
        package, cls = determine_file_class_and_name(filename)
        file = cls(
            filename, None, 100, comp_and_section, priority, package, "666", self.changes, None, self.upload.logger
        )
        self.changes.files.append(file)
        return file

    def assertMatchDDEBErrors(self, error_list):
        self.assertEquals(error_list, [str(e) for e in self.upload._matchDDEBs()])

    def testNoLinksWithNoBinaries(self):
        # No links will be made if there are no binaries whatsoever.
        self.addFile("something_1.0.diff.gz")
        self.assertMatchDDEBErrors([])

    def testNoLinksWithJustDEBs(self):
        # No links will be made if there are no DDEBs.
        self.addFile("blah_1.0_all.deb")
        self.addFile("libblah_1.0_i386.deb")
        self.assertMatchDDEBErrors([])
        for file in self.changes.files:
            self.assertIs(None, file.ddeb_file)

    def testLinksMatchingDDEBs(self):
        # DDEBs will be linked to their matching DEBs.
        self.addFile("blah_1.0_all.deb")
        self.addFile("libblah_1.0_i386.deb")
        self.addFile("libblah-dbgsym_1.0_i386.ddeb")
        self.addFile("libfooble_1.0_i386.udeb")
        self.addFile("libfooble-dbgsym_1.0_i386.ddeb")
        self.assertMatchDDEBErrors([])
        self.assertIs(None, self.changes.files[0].ddeb_file)
        self.assertIs(self.changes.files[2], self.changes.files[1].ddeb_file)
        self.assertIs(self.changes.files[1], self.changes.files[2].deb_file)
        self.assertIs(None, self.changes.files[2].ddeb_file)

    def testDuplicateDDEBsCauseErrors(self):
        # An error will be raised if a DEB has more than one matching
        # DDEB.
        self.addFile("libblah_1.0_i386.deb")
        self.addFile("libblah-dbgsym_1.0_i386.ddeb")
        self.addFile("libblah-dbgsym_1.0_i386.ddeb")
        self.assertMatchDDEBErrors(["Duplicated debug packages: libblah-dbgsym 666 (i386)"])

    def testMismatchedDDEBsCauseErrors(self):
        # An error will be raised if a DDEB has no matching DEB.
        self.addFile("libblah_1.0_i386.deb")
        self.addFile("libblah-dbgsym_1.0_amd64.ddeb")
        self.assertMatchDDEBErrors(["Orphaned debug packages: libblah-dbgsym 666 (amd64)"])
def getUploadForBinary(upload_path):
    """Return a NascentUpload object for binaries."""
    policy = getPolicy(name='sync', distro='ubuntu', distroseries='hoary')
    policy.accepted_type = ArchiveUploadType.BINARY_ONLY
    return NascentUpload.from_changesfile_path(
        datadir(upload_path), policy, DevNullLogger())
def getPPAUploadForSource(upload_path, ppa):
    """Return a NascentUpload object for a PPA source."""
    policy = getPolicy(name='insecure', distro='ubuntu', distroseries='hoary')
    policy.archive = ppa
    return NascentUpload.from_changesfile_path(
        datadir(upload_path), policy, DevNullLogger())
def getUploadForSource(upload_path):
    """Return a NascentUpload object for a source."""
    policy = getPolicy(name='sync', distro='ubuntu', distroseries='hoary')
    return NascentUpload.from_changesfile_path(
        datadir(upload_path), policy, DevNullLogger())
 def setUp(self):
     super(TestMatchDDEBs, self).setUp()
     self.changes = FakeChangesFile()
     self.upload = NascentUpload(self.changes, None, DevNullLogger())
Beispiel #23
0
 def test_rejects_misspelled_changesfile_name(self):
     upload = NascentUpload.from_changesfile_path(
         datadir("ddtp-tarball/translations-main_20060728.changes"),
         self.absolutely_anything_policy, self.logger)
     self.assertRaises(EarlyReturnUploadError, upload.process)
 def test_rejects_misspelled_changesfile_name(self):
     upload = NascentUpload.from_changesfile_path(
         datadir("ddtp-tarball/translations-main_20060728.changes"), self.absolutely_anything_policy, self.logger
     )
     self.assertRaises(EarlyReturnUploadError, upload.process)
 def test_rejects_misspelled_changesfile_name(self):
     upload = NascentUpload.from_changesfile_path(
         datadir("dist-upgrader/dist-upgrader_20060302.0120.changes"),
         self.absolutely_anything_policy, self.logger)
     self.assertRaises(EarlyReturnUploadError, upload.process)
Beispiel #26
0
    def processChangesFile(self, changes_file, logger=None):
        """Process a single changes file.

        This is done by obtaining the appropriate upload policy (according
        to command-line options and the value in the .distro file beside
        the upload, if present), creating a NascentUpload object and calling
        its process method.

        We obtain the context for this processing from the relative path,
        within the upload folder, of this changes file. This influences
        our creation both of upload policy and the NascentUpload object.

        See nascentupload.py for the gory details.

        Returns a value from UploadStatusEnum, or re-raises an exception
        from NascentUpload.

        :param changes_file: filename of the changes file to process.
        :param logger: logger to use for processing.
        :return: an `UploadStatusEnum` value
        """
        if logger is None:
            logger = self.processor.log
        # Calculate the distribution from the path within the upload
        # Reject the upload since we could not process the path,
        # Store the exception information as a rejection message.
        relative_path = os.path.dirname(changes_file)
        upload_path_error = None
        try:
            (distribution, suite_name,
             archive) = parse_upload_path(relative_path)
        except UploadPathError as e:
            # pick some defaults to create the NascentUpload() object.
            # We will be rejecting the upload so it doesn matter much.
            distribution = getUtility(IDistributionSet)['ubuntu']
            suite_name = None
            archive = distribution.main_archive
            upload_path_error = UPLOAD_PATH_ERROR_TEMPLATE % (dict(
                upload_path=relative_path,
                path_error=str(e),
                extra_info=("Please update your dput/dupload configuration "
                            "and then re-upload.")))
        except PPAUploadPathError as e:
            # Again, pick some defaults but leave a hint for the rejection
            # emailer that it was a PPA failure.
            distribution = getUtility(IDistributionSet)['ubuntu']
            suite_name = None
            # XXX cprov 20071212: using the first available PPA is not exactly
            # fine because it can confuse the code that sends rejection
            # messages if it relies only on archive.purpose (which should be
            # enough). On the other hand if we set an arbitrary owner it
            # will break nascentupload ACL calculations.
            archive = distribution.getAllPPAs()[0]
            upload_path_error = UPLOAD_PATH_ERROR_TEMPLATE % (dict(
                upload_path=relative_path,
                path_error=str(e),
                extra_info=(
                    "Please check the documentation at "
                    "https://help.launchpad.net/Packaging/PPA#Uploading "
                    "and update your configuration.")))
        logger.debug("Finding fresh policy")
        policy = self._getPolicyForDistro(distribution)
        policy.archive = archive

        # DistroSeries overriding respect the following precedence:
        #  1. process-upload.py command-line option (-r),
        #  2. upload path,
        #  3. changesfile 'Distribution' field.
        if suite_name is not None:
            policy.setDistroSeriesAndPocket(suite_name)

        # The path we want for NascentUpload is the path to the folder
        # containing the changes file (and the other files referenced by it).
        changesfile_path = os.path.join(self.upload_path, changes_file)
        try:
            upload = NascentUpload.from_changesfile_path(
                changesfile_path, policy, self.processor.log)
        except UploadError as e:
            # We failed to parse the changes file, so we have no key or
            # Changed-By to notify of the rejection. Just log it and
            # move on.
            # XXX wgrant 2011-09-29 bug=499438: With some refactoring we
            # could do better here: if we have a signature then we have
            # somebody to email, even if the rest of the file is
            # corrupt.
            logger.info("Failed to parse changes file '%s': %s" %
                        (os.path.join(self.upload_path, changes_file), str(e)))
            return UploadStatusEnum.REJECTED

        # Reject source upload to buildd upload paths.
        first_path = relative_path.split(os.path.sep)[0]
        if (first_path.isdigit()
                and policy.name != BuildDaemonUploadPolicy.name):
            error_message = ("Invalid upload path (%s) for this policy (%s)" %
                             (relative_path, policy.name))
            upload.reject(error_message)
            logger.error(error_message)

        # Reject upload with path processing errors.
        if upload_path_error is not None:
            upload.reject(upload_path_error)

        # Store processed NascentUpload instance, mostly used for tests.
        self.processor.last_processed_upload = upload

        try:
            logger.info("Processing upload %s" % upload.changes.filename)
            result = UploadStatusEnum.ACCEPTED

            try:
                self._processUpload(upload)
            except UploadPolicyError as e:
                upload.reject("UploadPolicyError escaped upload.process: "
                              "%s " % e)
                logger.debug("UploadPolicyError escaped upload.process",
                             exc_info=True)
            except (KeyboardInterrupt, SystemExit):
                raise
            except EarlyReturnUploadError:
                # An error occurred that prevented further error collection,
                # add this fact to the list of errors.
                upload.reject(
                    "Further error processing not possible because of "
                    "a critical previous error.")
            except Exception as e:
                # In case of unexpected unhandled exception, we'll
                # *try* to reject the upload. This may fail and cause
                # a further exception, depending on the state of the
                # nascentupload objects. In that case, we've lost nothing,
                # the new exception will be handled by the caller just like
                # the one we caught would have been, by failing the upload
                # with no email.
                logger.exception("Unhandled exception processing upload")
                upload.reject("Unhandled exception processing upload: %s" % e)

            # XXX julian 2007-05-25 bug=29744:
            # When bug #29744 is fixed (zopeless mails should only be sent
            # when transaction is committed) this will cause any emails sent
            # sent by do_reject to be lost.
            notify = True
            if self.processor.dry_run or self.processor.no_mails:
                notify = False
            if upload.is_rejected:
                result = UploadStatusEnum.REJECTED
                upload.do_reject(notify)
                self.processor.ztm.abort()
            else:
                successful = self._acceptUpload(upload, notify)
                if not successful:
                    result = UploadStatusEnum.REJECTED
                    logger.info(
                        "Rejection during accept. Aborting partial accept.")
                    self.processor.ztm.abort()

            if upload.is_rejected:
                logger.info("Upload was rejected:")
                for msg in upload.rejections:
                    logger.info("\t%s" % msg)

            if self.processor.dry_run:
                logger.info("Dry run, aborting transaction.")
                self.processor.ztm.abort()
            else:
                logger.info(
                    "Committing the transaction and any mails associated "
                    "with this upload.")
                self.processor.ztm.commit()
        except:
            self.processor.ztm.abort()
            raise

        return result
    def processChangesFile(self, changes_file, logger=None):
        """Process a single changes file.

        This is done by obtaining the appropriate upload policy (according
        to command-line options and the value in the .distro file beside
        the upload, if present), creating a NascentUpload object and calling
        its process method.

        We obtain the context for this processing from the relative path,
        within the upload folder, of this changes file. This influences
        our creation both of upload policy and the NascentUpload object.

        See nascentupload.py for the gory details.

        Returns a value from UploadStatusEnum, or re-raises an exception
        from NascentUpload.

        :param changes_file: filename of the changes file to process.
        :param logger: logger to use for processing.
        :return: an `UploadStatusEnum` value
        """
        if logger is None:
            logger = self.processor.log
        # Calculate the distribution from the path within the upload
        # Reject the upload since we could not process the path,
        # Store the exception information as a rejection message.
        relative_path = os.path.dirname(changes_file)
        upload_path_error = None
        try:
            (distribution, suite_name,
             archive) = parse_upload_path(relative_path)
        except UploadPathError as e:
            # pick some defaults to create the NascentUpload() object.
            # We will be rejecting the upload so it doesn matter much.
            distribution = getUtility(IDistributionSet)['ubuntu']
            suite_name = None
            archive = distribution.main_archive
            upload_path_error = UPLOAD_PATH_ERROR_TEMPLATE % (
                dict(upload_path=relative_path, path_error=str(e),
                     extra_info=(
                         "Please update your dput/dupload configuration "
                         "and then re-upload.")))
        except PPAUploadPathError as e:
            # Again, pick some defaults but leave a hint for the rejection
            # emailer that it was a PPA failure.
            distribution = getUtility(IDistributionSet)['ubuntu']
            suite_name = None
            # XXX cprov 20071212: using the first available PPA is not exactly
            # fine because it can confuse the code that sends rejection
            # messages if it relies only on archive.purpose (which should be
            # enough). On the other hand if we set an arbitrary owner it
            # will break nascentupload ACL calculations.
            archive = distribution.getAllPPAs()[0]
            upload_path_error = UPLOAD_PATH_ERROR_TEMPLATE % (
                dict(upload_path=relative_path, path_error=str(e),
                     extra_info=(
                         "Please check the documentation at "
                         "https://help.launchpad.net/Packaging/PPA#Uploading "
                         "and update your configuration.")))
        logger.debug("Finding fresh policy")
        policy = self._getPolicyForDistro(distribution)
        policy.archive = archive

        # DistroSeries overriding respect the following precedence:
        #  1. process-upload.py command-line option (-r),
        #  2. upload path,
        #  3. changesfile 'Distribution' field.
        if suite_name is not None:
            policy.setDistroSeriesAndPocket(suite_name)

        # The path we want for NascentUpload is the path to the folder
        # containing the changes file (and the other files referenced by it).
        changesfile_path = os.path.join(self.upload_path, changes_file)
        try:
            upload = NascentUpload.from_changesfile_path(
                changesfile_path, policy, self.processor.log)
        except UploadError as e:
            # We failed to parse the changes file, so we have no key or
            # Changed-By to notify of the rejection. Just log it and
            # move on.
            # XXX wgrant 2011-09-29 bug=499438: With some refactoring we
            # could do better here: if we have a signature then we have
            # somebody to email, even if the rest of the file is
            # corrupt.
            logger.info(
                "Failed to parse changes file '%s': %s" % (
                    os.path.join(self.upload_path, changes_file),
                    str(e)))
            return UploadStatusEnum.REJECTED

        # Reject source upload to buildd upload paths.
        first_path = relative_path.split(os.path.sep)[0]
        if (first_path.isdigit() and
            policy.name != BuildDaemonUploadPolicy.name):
            error_message = (
                "Invalid upload path (%s) for this policy (%s)" %
                (relative_path, policy.name))
            upload.reject(error_message)
            logger.error(error_message)

        # Reject upload with path processing errors.
        if upload_path_error is not None:
            upload.reject(upload_path_error)

        # Store processed NascentUpload instance, mostly used for tests.
        self.processor.last_processed_upload = upload

        try:
            logger.info("Processing upload %s" % upload.changes.filename)
            result = UploadStatusEnum.ACCEPTED

            try:
                self._processUpload(upload)
            except UploadPolicyError as e:
                upload.reject("UploadPolicyError escaped upload.process: "
                              "%s " % e)
                logger.debug(
                    "UploadPolicyError escaped upload.process", exc_info=True)
            except (KeyboardInterrupt, SystemExit):
                raise
            except EarlyReturnUploadError:
                # An error occurred that prevented further error collection,
                # add this fact to the list of errors.
                upload.reject(
                    "Further error processing not possible because of "
                    "a critical previous error.")
            except Exception as e:
                # In case of unexpected unhandled exception, we'll
                # *try* to reject the upload. This may fail and cause
                # a further exception, depending on the state of the
                # nascentupload objects. In that case, we've lost nothing,
                # the new exception will be handled by the caller just like
                # the one we caught would have been, by failing the upload
                # with no email.
                logger.exception("Unhandled exception processing upload")
                upload.reject("Unhandled exception processing upload: %s" % e)

            # XXX julian 2007-05-25 bug=29744:
            # When bug #29744 is fixed (zopeless mails should only be sent
            # when transaction is committed) this will cause any emails sent
            # sent by do_reject to be lost.
            notify = True
            if self.processor.dry_run or self.processor.no_mails:
                notify = False
            if upload.is_rejected:
                result = UploadStatusEnum.REJECTED
                upload.do_reject(notify)
                self.processor.ztm.abort()
            else:
                successful = self._acceptUpload(upload, notify)
                if not successful:
                    result = UploadStatusEnum.REJECTED
                    logger.info(
                        "Rejection during accept. Aborting partial accept.")
                    self.processor.ztm.abort()

            if upload.is_rejected:
                logger.info("Upload was rejected:")
                for msg in upload.rejections:
                    logger.info("\t%s" % msg)

            if self.processor.dry_run:
                logger.info("Dry run, aborting transaction.")
                self.processor.ztm.abort()
            else:
                logger.info(
                    "Committing the transaction and any mails associated "
                    "with this upload.")
                self.processor.ztm.commit()
        except:
            self.processor.ztm.abort()
            raise

        return result