예제 #1
0
    def reload_changelog(self):
        """Reloads debian/changelog and updates the version.

        Returns true if the changelog was reloaded successfully. Returns false
        if the user wants to correct a broken changelog.
        """

        # Check the changelog
        self._changelog = debian.changelog.Changelog()
        try:
            self._changelog.parse_changelog(open("debian/changelog"),
                                            max_blocks=1, strict=True)
        except debian.changelog.ChangelogParseError as error:
            Logger.error("The changelog entry doesn't validate: %s", str(error))
            ask_for_manual_fixing()
            return False

        # Get new version of package
        try:
            self._version = self._changelog.get_version()
        except IndexError:
            Logger.error("Debian package version could not be determined. "
                         "debian/changelog is probably malformed.")
            ask_for_manual_fixing()
            return False

        return True
예제 #2
0
 def apply(self, task):
     """Applies the patch in the current directory."""
     assert self._changed_files is not None, "You forgot to download the patch."
     edit = False
     if self.is_debdiff():
         cmd = [
             "patch", "--merge", "--force", "-p",
             str(self.get_strip_level()), "-i", self._full_path
         ]
         Logger.command(cmd)
         if subprocess.call(cmd) != 0:
             Logger.error("Failed to apply debdiff %s to %s %s.",
                          self._patch_file, task.package,
                          task.get_version())
             if not edit:
                 ask_for_manual_fixing()
                 edit = True
     else:
         cmd = ["add-patch", self._full_path]
         Logger.command(cmd)
         if subprocess.call(cmd) != 0:
             Logger.error("Failed to apply diff %s to %s %s.",
                          self._patch_file, task.package,
                          task.get_version())
             if not edit:
                 ask_for_manual_fixing()
                 edit = True
     return edit
예제 #3
0
    def _check_dsc(self, verify_signature=False):
        "Check that the dsc matches what we are expecting"
        assert self._dsc is not None
        self.source = self.dsc['Source']
        self.version = debian.debian_support.Version(self.dsc['Version'])

        valid = False
        message = None
        gpg_info = None
        try:
            gpg_info = self.dsc.get_gpg_info()
            valid = gpg_info.valid()
        except IOError:
            message = ('Signature on %s could not be verified, install '
                       'debian-keyring' % self.dsc_name)
        if message is None:
            if valid:
                message = 'Valid signature'
            else:
                message = ('Signature on %s could not be verified' %
                           self.dsc_name)
        if gpg_info is not None:
            if 'GOODSIG' in gpg_info:
                message = ('Good signature by %s (0x%s)' %
                           (gpg_info['GOODSIG'][1], gpg_info['GOODSIG'][0]))
            elif 'VALIDSIG' in gpg_info:
                message = 'Valid signature by 0x%s' % gpg_info['VALIDSIG'][0]
        if verify_signature:
            if valid:
                Logger.normal(message)
            else:
                Logger.error(message)
                raise DownloadError(message)
        else:
            Logger.info(message)
예제 #4
0
    def build_source(self, keyid, upload, previous_version):
        """Tries to build the source package.

        Returns true if the source package was built successfully. Returns false
        if the user wants to change something.
        """

        if self._branch:
            cmd = ['bzr', 'builddeb', '--builder=debuild', '-S',
                   '--', '--no-lintian', '-nc']
        else:
            cmd = ['debuild', '--no-lintian', '-nc', '-S']
        cmd.append("-v" + previous_version.full_version)
        if previous_version.upstream_version == \
           self._changelog.upstream_version and upload == "ubuntu":
            # FIXME: Add proper check that catches cases like changed
            # compression (.tar.gz -> tar.bz2) and multiple orig source tarballs
            cmd.append("-sd")
        else:
            cmd.append("-sa")
        if keyid is not None:
            cmd += ["-k" + keyid]
        env = os.environ
        if upload == 'ubuntu':
            env['DEB_VENDOR'] = 'Ubuntu'
        Logger.command(cmd)
        if subprocess.call(cmd, env=env) != 0:
            Logger.error("Failed to build source tarball.")
            # TODO: Add a "retry" option
            ask_for_manual_fixing()
            return False
        return True
예제 #5
0
def get_patch_or_branch(bug):
    patch = None
    branch = None
    if not is_sync(bug):
        attached_patches = [a for a in bug.attachments if a.type == "Patch"]
        linked_branches = [b.branch for b in bug.linked_branches]
        if len(attached_patches) == 0 and len(linked_branches) == 0:
            if len(bug.attachments) == 0:
                Logger.error(
                    "No attachment and no linked branch found on "
                    "bug #%i. Add the tag sync to the bug if it is "
                    "a sync request.", bug.id)
            else:
                Logger.error(
                    "No attached patch and no linked branch found. "
                    "Go to https://launchpad.net/bugs/%i and mark an "
                    "attachment as patch.", bug.id)
            sys.exit(1)
        elif len(attached_patches) == 1 and len(linked_branches) == 0:
            patch = Patch(attached_patches[0])
        elif len(attached_patches) == 0 and len(linked_branches) == 1:
            branch = linked_branches[0].bzr_identity
        else:
            patch, branch = ask_for_patch_or_branch(bug, attached_patches,
                                                    linked_branches)
    return (patch, branch)
예제 #6
0
def rmadison(url, package, suite=None, arch=None):
    "Call rmadison and parse the result"
    cmd = ['rmadison', '-u', url]
    if suite:
        cmd += ['-s', suite]
    if arch:
        cmd += ['-a', arch]
    cmd.append(package)
    process = subprocess.Popen(cmd,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               close_fds=True)
    output, error_output = process.communicate()
    if process.wait() != 0:
        if error_output:
            Logger.error('rmadison failed with: %s', error_output)
        else:
            Logger.error('rmadison failed')
        sys.exit(1)

    # rmadison uses some shorthand
    if suite:
        suite = suite.replace('-proposed-updates', '-p-u')

    # pylint bug: http://www.logilab.org/ticket/46273
    # pylint: disable=E1103
    for line in output.strip().splitlines():
        # pylint: enable=E1103
        pkg, ver, dist, archs = [x.strip() for x in line.split('|')]
        comp = 'main'
        if '/' in dist:
            dist, comp = dist.split('/')
        archs = set(x.strip() for x in archs.split(','))

        # rmadison returns some results outside the requested set.
        # It'll include backports, and when given an unknown suite,
        # it ignores that argument
        #
        # some versions (2.14.1ubuntu0.1) of rmadison return 'sid' when
        # asked about 'unstable'.  Others return 'unstable'.  Accept either.
        if (suite and dist != suite
                and not (suite == 'sid' and dist == 'unstable')):
            continue

        if 'source' in archs:
            yield {
                'source': pkg,
                'version': ver,
                'suite': dist,
                'component': comp,
            }
        archs.discard('source')
        if archs:
            yield {
                'binary': pkg,
                'version': ver,
                'suite': dist,
                'component': comp,
                'architectures': archs,
            }
예제 #7
0
    def snapshot_list(self):
        "Return a filename -> hash dictionary from snapshot.debian.org"
        if self._snapshot_list is None:
            try:
                import json
            except ImportError:
                import simplejson as json
            except ImportError:
                Logger.error("Please install python-simplejson.")
                raise DownloadError("Unable to dowload from "
                                    "snapshot.debian.org without "
                                    "python-simplejson")

            try:
                data = self.url_opener.open(
                    'http://snapshot.debian.org/mr/package/%s/%s/srcfiles?fileinfo=1'
                    % (self.source, self.version.full_version))
                reader = codecs.getreader('utf-8')
                srcfiles = json.load(reader(data))

            except HTTPError:
                Logger.error(
                    'Version %s of %s not found on '
                    'snapshot.debian.org', self.version.full_version,
                    self.source)
                self._snapshot_list = False
                return False
            self._snapshot_list = dict(
                (info[0]['name'], hash_)
                for hash_, info in srcfiles['fileinfo'].items())
        return self._snapshot_list
예제 #8
0
def extract_source(dsc_file, verbose=False):
    cmd = ["dpkg-source", "--no-preparation", "-x", dsc_file]
    if not verbose:
        cmd.insert(1, "-q")
    Logger.command(cmd)
    if subprocess.call(cmd) != 0:
        Logger.error("Extraction of %s failed." % (os.path.basename(dsc_file)))
        sys.exit(1)
예제 #9
0
def _update_maintainer_field():
    """Update the Maintainer field in debian/control."""
    Logger.command(["update-maintainer"])
    try:
        update_maintainer("debian", Logger.verbose)
    except MaintainerUpdateException as e:
        Logger.error("update-maintainer failed: %s", str(e))
        sys.exit(1)
예제 #10
0
def update_maintainer(debian_directory, verbose=False):
    """updates the Maintainer field of an Ubuntu package

    * No modifications are made if the Maintainer field contains an ubuntu.com
      email address. Otherwise, the Maintainer field will be set to
      Ubuntu Developers <*****@*****.**>
    * The old value will be saved in a field named XSBC-Original-Maintainer
      if the Maintainer field is modified.

    Policy: https://wiki.ubuntu.com/DebianMaintainerField
    """
    try:
        changelog_file, control_files = _find_files(debian_directory, verbose)
    except MaintainerUpdateException as e:
        Logger.error(str(e))
        raise

    distribution = _get_distribution(changelog_file)
    for control_file in control_files:
        control = Control(control_file)
        original_maintainer = control.get_maintainer()

        if original_maintainer is None:
            Logger.error("No Maintainer field found in %s.", control_file)
            raise MaintainerUpdateException("No Maintainer field found")

        if original_maintainer.strip().lower() in _PREVIOUS_UBUNTU_MAINTAINER:
            if verbose:
                print("The old maintainer was: %s" % original_maintainer)
                print("Resetting as: %s" % _UBUNTU_MAINTAINER)
            control.set_maintainer(_UBUNTU_MAINTAINER)
            control.save()
            continue

        if original_maintainer.strip().endswith("ubuntu.com>"):
            if verbose:
                print(
                    "The Maintainer email is set to an ubuntu.com address. Doing nothing."
                )
            continue

        if distribution in ("stable", "testing", "unstable", "experimental"):
            if verbose:
                print("The package targets Debian. Doing nothing.")
            return

        if control.get_original_maintainer() is not None:
            Logger.warn("Overwriting original maintainer: %s",
                        control.get_original_maintainer())

        if verbose:
            print("The original maintainer is: %s" % original_maintainer)
            print("Resetting as: %s" % _UBUNTU_MAINTAINER)
        control.set_original_maintainer(original_maintainer)
        control.set_maintainer(_UBUNTU_MAINTAINER)
        control.save()

    return
예제 #11
0
def merge_branch(branch):
    edit = False
    cmd = ["bzr", "merge", branch]
    Logger.command(cmd)
    if subprocess.call(cmd) != 0:
        Logger.error("Failed to merge branch %s." % (branch))
        ask_for_manual_fixing()
        edit = True
    return edit
예제 #12
0
 def unpack(self, destdir=None):
     "Unpack in workdir"
     cmd = ['dpkg-source', '-x', self.dsc_name]
     if destdir:
         cmd.append(destdir)
     Logger.command(cmd)
     if subprocess.call(cmd, cwd=self.workdir):
         Logger.error('Source unpack failed.')
         sys.exit(1)
예제 #13
0
def download_branch(branch):
    dir_name = os.path.basename(branch)
    if os.path.isdir(dir_name):
        shutil.rmtree(dir_name)
    cmd = ["bzr", "branch", branch]
    Logger.command(cmd)
    if subprocess.call(cmd) != 0:
        Logger.error("Failed to download branch %s." % (branch))
        sys.exit(1)
    return dir_name
예제 #14
0
    def check_version(self, previous_version):
        """Check if the version of the package is greater than the given one.

        Return true if the version of the package is newer. Returns false
        if the user wants to change something.
        """

        if self._version <= previous_version:
            Logger.error("The version %s is not greater than the already "
                         "available %s.", self._version, previous_version)
            return ask_for_ignoring_or_fixing()
        return True
예제 #15
0
    def ack_sync(self, upload, task, launchpad):
        """Acknowledge a sync request and subscribe ubuntu-archive."""

        if upload == "ubuntu":
            self._print_logs()
            question = Question(["yes", "edit", "no"])
            answer = question.ask("Do you want to acknowledge the sync request",
                                  "no")
            if answer == "edit":
                return False
            elif answer == "no":
                user_abort()

            bug = task.bug
            task.status = "Confirmed"
            if task.importance == "Undecided":
                task.importance = "Wishlist"
            task.lp_save()
            Logger.info("Set bug #%i status to Confirmed.", bug.id)

            msg = "Sync request ACK'd."
            if self._build_log:
                msg = ("%s %s builds on %s. " + msg) % \
                      (self._package, self._version,
                       self._builder.get_architecture())
            bug.newMessage(content=msg, subject="sponsor-patch")
            Logger.info("Acknowledged sync request bug #%i.", bug.id)

            bug.subscribe(person=launchpad.people['ubuntu-archive'])
            Logger.info("Subscribed ubuntu-archive to bug #%i.", bug.id)

            bug.subscribe(person=launchpad.me)
            Logger.info("Subscribed me to bug #%i.", bug.id)

            sponsorsteam = launchpad.people['ubuntu-sponsors']
            for sub in bug.subscriptions:
                if sub.person == sponsorsteam and sub.canBeUnsubscribedByUser():
                    bug.unsubscribe(person=launchpad.people['ubuntu-sponsors'])
                    Logger.info("Unsubscribed ubuntu-sponsors from bug #%i.",
                                bug.id)
                elif sub.person == sponsorsteam:
                    Logger.info("Couldn't unsubscribe ubuntu-sponsors from "
                                "bug #%i.", bug.id)

            Logger.normal("Successfully acknowledged sync request bug #%i.",
                          bug.id)
        else:
            Logger.error("Sync requests can only be acknowledged when the "
                         "upload target is Ubuntu.")
            sys.exit(1)
        return True
예제 #16
0
def get_open_ubuntu_bug_task(launchpad, bug, branch=None):
    """Returns an open Ubuntu bug task for a given Launchpad bug.

    The bug task needs to be open (not complete) and target Ubuntu. The user
    will be ask to select one if multiple open Ubuntu bug task exits for the
    bug.
    """
    bug_tasks = [BugTask(x, launchpad) for x in bug.bug_tasks]
    ubuntu_tasks = [x for x in bug_tasks if x.is_ubuntu_task()]
    bug_id = bug.id
    if branch:
        branch = branch.split('/')
        # Non-production LP?
        if len(branch) > 5:
            branch = branch[3:]

    if len(ubuntu_tasks) == 0:
        Logger.error("No Ubuntu bug task found on bug #%i." % (bug_id))
        sys.exit(1)
    elif len(ubuntu_tasks) == 1:
        task = ubuntu_tasks[0]
    if len(ubuntu_tasks) > 1 and branch and branch[1] == 'ubuntu':
        tasks = [
            t for t in ubuntu_tasks
            if t.get_series() == branch[2] and t.package == branch[3]
        ]
        if len(tasks) > 1:
            # A bug targeted to the development series?
            tasks = [t for t in tasks if t.series is not None]
        assert len(tasks) == 1
        task = tasks[0]
    elif len(ubuntu_tasks) > 1:
        task_list = [t.get_short_info() for t in ubuntu_tasks]
        Logger.info("%i Ubuntu tasks exist for bug #%i.\n%s",
                    len(ubuntu_tasks), bug_id, "\n".join(task_list))
        open_ubuntu_tasks = [x for x in ubuntu_tasks if not x.is_complete()]
        if len(open_ubuntu_tasks) == 1:
            task = open_ubuntu_tasks[0]
        else:
            Logger.normal(
                "https://launchpad.net/bugs/%i has %i Ubuntu tasks:" %
                (bug_id, len(ubuntu_tasks)))
            for i in range(len(ubuntu_tasks)):
                print("%i) %s" %
                      (i + 1, ubuntu_tasks[i].get_package_and_series()))
            selected = input_number(
                "To which Ubuntu task does the patch belong", 1,
                len(ubuntu_tasks))
            task = ubuntu_tasks[selected - 1]
    Logger.info("Selected Ubuntu task: %s" % (task.get_short_info()))
    return task
예제 #17
0
def _create_and_change_into(workdir):
    """Create (if it does not exits) and change into given working directory."""

    if not os.path.isdir(workdir):
        try:
            os.makedirs(workdir)
        except os.error as error:
            Logger.error(
                "Failed to create the working directory %s [Errno %i]: %s." %
                (workdir, error.errno, error.strerror))
            sys.exit(1)
    if workdir != os.getcwd():
        Logger.command(["cd", workdir])
        os.chdir(workdir)
예제 #18
0
def edit_source():
    # Spawn shell to allow modifications
    cmd = [get_user_shell()]
    Logger.command(cmd)
    print("""An interactive shell was launched in
file://%s
Edit your files. When you are done, exit the shell. If you wish to abort the
process, exit the shell such that it returns an exit code other than zero.
""" % (os.getcwd()),
          end=' ')
    returncode = subprocess.call(cmd)
    if returncode != 0:
        Logger.error("Shell exited with exit value %i." % (returncode))
        sys.exit(1)
예제 #19
0
    def _download_file(self, url, filename):
        "Download url to filename in workdir."
        pathname = os.path.join(self.workdir, filename)
        if self.dsc.verify_file(pathname):
            Logger.debug('Using existing %s', filename)
            return True
        size = [
            entry['size'] for entry in self.dsc['Files']
            if entry['name'] == filename
        ]
        assert len(size) == 1
        size = int(size[0])
        parsed = urlparse(url)
        if not self.quiet:
            Logger.normal('Downloading %s from %s (%0.3f MiB)', filename,
                          parsed.hostname, size / 1024.0 / 1024)

        if parsed.scheme == 'file':
            in_ = open(parsed.path, 'rb')
        else:
            try:
                in_ = self.url_opener.open(url)
            except URLError:
                return False

        downloaded = 0
        bar_width = 60
        try:
            with open(pathname, 'wb') as out:
                while True:
                    block = in_.read(10240)
                    if block == b'':
                        break
                    downloaded += len(block)
                    out.write(block)
                    if not self.quiet:
                        percent = downloaded * 100 // size
                        bar = '=' * int(round(downloaded * bar_width / size))
                        bar = (bar + '>' + ' ' * bar_width)[:bar_width]
                        Logger.stdout.write('[%s] %#3i%%\r' % (bar, percent))
                        Logger.stdout.flush()
            in_.close()
        finally:
            if not self.quiet:
                Logger.stdout.write(' ' * (bar_width + 7) + '\r')
                Logger.stdout.flush()
        if not self.dsc.verify_file(pathname):
            Logger.error('Checksum for %s does not match.', filename)
            return False
        return True
예제 #20
0
def restore_maintainer(debian_directory, verbose=False):
    """Restore the original maintainer"""
    try:
        changelog_file, control_files = _find_files(debian_directory, verbose)
    except MaintainerUpdateException as e:
        Logger.error(str(e))
        raise

    for control_file in control_files:
        control = Control(control_file)
        orig_maintainer = control.get_original_maintainer()
        if not orig_maintainer:
            continue
        if verbose:
            print("Restoring original maintainer: %s" % orig_maintainer)
        control.set_maintainer(orig_maintainer)
        control.remove_original_maintainer()
        control.save()
예제 #21
0
    def sync(self, upload, series, bug_number, requester):
        """Does a sync of the source package."""

        if upload == "ubuntu":
            cmd = ["syncpackage", self._package, "-b", str(bug_number), "-f",
                   "-s", requester, "-V", str(self._version),
                   "-d", series]
            Logger.command(cmd)
            if subprocess.call(cmd) != 0:
                Logger.error("Syncing of %s %s failed.", self._package,
                             str(self._version))
                sys.exit(1)
        else:
            # FIXME: Support this use case!
            Logger.error("Uploading a synced package other than to Ubuntu "
                         "is not supported yet!")
            sys.exit(1)
        return True
예제 #22
0
def get_ubuntu_delta_changelog(srcpkg):
    '''
    Download the Ubuntu changelog and extract the entries since the last sync
    from Debian.
    '''
    archive = Distribution('ubuntu').getArchive()
    spph = archive.getPublishedSources(source_name=srcpkg.getPackageName(),
                                       exact_match=True, pocket='Release')
    debian_info = DebianDistroInfo()
    topline = re.compile(r'^(\w%(name_chars)s*) \(([^\(\) \t]+)\)'
                         r'((\s+%(name_chars)s+)+)\;'
                         % {'name_chars': '[-+0-9a-z.]'},
                         re.IGNORECASE)
    delta = []
    for record in spph:
        changes_url = record.changesFileUrl()
        if changes_url is None:
            # Native sync
            break
        try:
            response, body = Http().request(changes_url)
        except HttpLib2Error as e:
            Logger.error(str(e))
            break
        if response.status != 200:
            Logger.error("%s: %s %s", changes_url, response.status,
                         response.reason)
            break

        changes = Changes(Http().request(changes_url)[1])
        for line in changes['Changes'].splitlines():
            line = line[1:]
            m = topline.match(line)
            if m:
                distribution = m.group(3).split()[0].split('-')[0]
                if debian_info.valid(distribution):
                    break
            if line.startswith(u'  '):
                delta.append(line)
        else:
            continue
        break

    return '\n'.join(delta)
예제 #23
0
 def debdiff(self, newpkg, diffstat=False):
     """Write a debdiff comparing this src pkg to a newer one.
     Optionally print diffstat.
     Return the debdiff filename.
     """
     cmd = ['debdiff', self.dsc_name, newpkg.dsc_name]
     difffn = newpkg.dsc_name[:-3] + 'debdiff'
     Logger.command(cmd + ['> %s' % difffn])
     with open(difffn, 'w') as f:
         if subprocess.call(cmd, stdout=f, cwd=self.workdir) > 2:
             Logger.error('Debdiff failed.')
             sys.exit(1)
     if diffstat:
         cmd = ('diffstat', '-p1', difffn)
         Logger.command(cmd)
         if subprocess.call(cmd):
             Logger.error('diffstat failed.')
             sys.exit(1)
     return os.path.abspath(difffn)
예제 #24
0
    def is_fixed(self, lp_bug):
        """Make sure that the given Launchpad bug is closed.

        Returns true if the bug is closed. Returns false if the user wants to
        change something.
        """

        assert os.path.isfile(self._changes_file), "%s does not exist." % (self._changes_file)
        changes = debian.deb822.Changes(open(self._changes_file))
        fixed_bugs = []
        if "Launchpad-Bugs-Fixed" in changes:
            fixed_bugs = changes["Launchpad-Bugs-Fixed"].split(" ")
        fixed_bugs = [int(bug) for bug in fixed_bugs]

        # Check if the given bug is marked as duplicate. In this case get the
        # master bug.
        while lp_bug.duplicate_of:
            lp_bug = lp_bug.duplicate_of

        if lp_bug.id not in fixed_bugs:
            Logger.error("Launchpad bug #%i is not closed by new version." % (lp_bug.id))
            return ask_for_ignoring_or_fixing()
        return True
예제 #25
0
    def check_target(self, upload, launchpad):
        """Make sure that the target is correct.

        Returns true if the target is correct. Returns false if the user
        wants to change something.
        """

        (devel_series, supported_series) = _get_series(launchpad)

        if upload == "ubuntu":
            allowed = supported_series + \
                      [s + "-proposed" for s in supported_series] + \
                      [devel_series]
            if self._changelog.distributions not in allowed:
                Logger.error("%s is not an allowed series. It needs to be one of %s." %
                             (self._changelog.distributions, ", ".join(allowed)))
                return ask_for_ignoring_or_fixing()
        elif upload and upload.startswith("ppa/"):
            allowed = supported_series + [devel_series]
            if self._changelog.distributions not in allowed:
                Logger.error("%s is not an allowed series. It needs to be one of %s." %
                             (self._changelog.distributions, ", ".join(allowed)))
                return ask_for_ignoring_or_fixing()
        return True
예제 #26
0
def get_builder(name):
    if name in _SUPPORTED_BUILDERS:
        builder = _SUPPORTED_BUILDERS[name]()
        if builder.exists_in_path():
            return builder
        Logger.error("Builder doesn't appear to be installed: %s", name)
    else:
        Logger.error("Unsupported builder specified: %s.", name)
        Logger.error("Supported builders: %s",
                     ", ".join(sorted(_SUPPORTED_BUILDERS.keys())))
예제 #27
0
    def ask_and_upload(self, upload):
        """Ask the user before uploading the source package.

        Returns true if the source package is uploaded successfully. Returns
        false if the user wants to change something.
        """

        # Upload package
        if upload:
            self._print_logs()
            if upload == "ubuntu":
                target = "the official Ubuntu archive"
            else:
                target = upload
            question = Question(["yes", "edit", "no"])
            answer = question.ask("Do you want to upload the package to %s" % target, "no")
            if answer == "edit":
                return False
            elif answer == "no":
                user_abort()
            cmd = ["dput", "--force", upload, self._changes_file]
            Logger.command(cmd)
            if subprocess.call(cmd) != 0:
                Logger.error("Upload of %s to %s failed." %
                             (os.path.basename(self._changes_file), upload))
                sys.exit(1)

            # Push the branch if the package is uploaded to the Ubuntu archive.
            if upload == "ubuntu" and self._branch:
                cmd = ['debcommit']
                Logger.command(cmd)
                if subprocess.call(cmd) != 0:
                    Logger.error('Bzr commit failed.')
                    sys.exit(1)
                cmd = ['bzr', 'mark-uploaded']
                Logger.command(cmd)
                if subprocess.call(cmd) != 0:
                    Logger.error('Bzr tagging failed.')
                    sys.exit(1)
                cmd = ['bzr', 'push', ':parent']
                Logger.command(cmd)
                if subprocess.call(cmd) != 0:
                    Logger.error('Bzr push failed.')
                    sys.exit(1)
        return True
예제 #28
0
 def _update_failure(self, returncode, dist):
     if returncode != 0:
         Logger.error("Failed to update %s chroot for %s." %
                      (dist, self.name))
     return returncode
예제 #29
0
 def _build_failure(self, returncode, dsc_file):
     if returncode != 0:
         Logger.error("Failed to build %s from source with %s." %
                      (os.path.basename(dsc_file), self.name))
     return returncode
예제 #30
0
def mail_bug(srcpkg, subscribe, status, bugtitle, bugtext, bug_mail_domain,
             keyid, myemailaddr, mailserver_host, mailserver_port,
             mailserver_user, mailserver_pass):
    '''
    Submit the sync request per email.
    '''

    to = 'new@' + bug_mail_domain

    # generate mailbody
    if srcpkg:
        mailbody = ' affects ubuntu/%s\n' % srcpkg
    else:
        mailbody = ' affects ubuntu\n'
    mailbody += '''\
 status %s
 importance wishlist
 subscribe %s
 done

%s''' % (status, subscribe, bugtext)

    # prepare sign command
    gpg_command = None
    for cmd in ('gnome-gpg', 'gpg2', 'gpg'):
        if os.access('/usr/bin/%s' % cmd, os.X_OK):
            gpg_command = [cmd]
            break

    if not gpg_command:
        Logger.error("Cannot locate gpg, please install the 'gnupg' package!")
        sys.exit(1)

    gpg_command.append('--clearsign')
    if keyid:
        gpg_command.extend(('-u', keyid))

    # sign the mail body
    gpg = subprocess.Popen(gpg_command,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE)
    signed_report = gpg.communicate(
        mailbody.encode('utf-8'))[0].decode('utf-8')
    if gpg.returncode != 0:
        Logger.error("%s failed.", gpg_command[0])
        sys.exit(1)

    # generate email
    mail = u'''\
From: %s
To: %s
Subject: %s
Content-Type: text/plain; charset=UTF-8

%s''' % (myemailaddr, to, bugtitle, signed_report)

    print('The final report is:\n%s' % mail)
    confirmation_prompt()

    # save mail in temporary file
    backup = tempfile.NamedTemporaryFile(
        mode='w',
        delete=False,
        prefix='requestsync-' +
        re.sub(r'[^a-zA-Z0-9_-]', '', bugtitle.replace(' ', '_')))
    with backup:
        backup.write(mail)

    Logger.normal(
        'The e-mail has been saved in %s and will be deleted '
        'after succesful transmission', backup.name)

    # connect to the server
    while True:
        try:
            Logger.normal('Connecting to %s:%s ...', mailserver_host,
                          mailserver_port)
            s = smtplib.SMTP(mailserver_host, mailserver_port)
            break
        except smtplib.SMTPConnectError as s:
            try:
                # py2 path
                # pylint: disable=unsubscriptable-object
                Logger.error('Could not connect to %s:%s: %s (%i)',
                             mailserver_host, mailserver_port, s[1], s[0])
            except TypeError:
                # pylint: disable=no-member
                Logger.error('Could not connect to %s:%s: %s (%i)',
                             mailserver_host, mailserver_port, s.strerror,
                             s.errno)
            if s.smtp_code == 421:
                confirmation_prompt(
                    message='This is a temporary error, press [Enter] '
                    'to retry. Press [Ctrl-C] to abort now.')
        except socket.error as s:
            try:
                # py2 path
                # pylint: disable=unsubscriptable-object
                Logger.error('Could not connect to %s:%s: %s (%i)',
                             mailserver_host, mailserver_port, s[1], s[0])
            except TypeError:
                # pylint: disable=no-member
                Logger.error('Could not connect to %s:%s: %s (%i)',
                             mailserver_host, mailserver_port, s.strerror,
                             s.errno)
            return

    if mailserver_user and mailserver_pass:
        try:
            s.login(mailserver_user, mailserver_pass)
        except smtplib.SMTPAuthenticationError:
            Logger.error('Error authenticating to the server: '
                         'invalid username and password.')
            s.quit()
            return
        except smtplib.SMTPException:
            Logger.error('Unknown SMTP error.')
            s.quit()
            return

    while True:
        try:
            s.sendmail(myemailaddr, to, mail.encode('utf-8'))
            s.quit()
            os.remove(backup.name)
            Logger.normal('Sync request mailed.')
            break
        except smtplib.SMTPRecipientsRefused as smtperror:
            smtp_code, smtp_message = smtperror.recipients[to]
            Logger.error('Error while sending: %i, %s', smtp_code,
                         smtp_message)
            if smtp_code == 450:
                confirmation_prompt(
                    message='This is a temporary error, press [Enter] '
                    'to retry. Press [Ctrl-C] to abort now.')
            else:
                return
        except smtplib.SMTPResponseException as e:
            Logger.error('Error while sending: %i, %s', e.smtp_code,
                         e.smtp_error)
            return
        except smtplib.SMTPServerDisconnected:
            Logger.error('Server disconnected while sending the mail.')
            return