1688 lines
55 KiB
Python
1688 lines
55 KiB
Python
# -*- coding:utf-8 -*-
|
|
#
|
|
# Copyright (C) 2005 - 2007, TUBITAK/UEKAE
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free
|
|
# Software Foundation; either version 2 of the License, or (at your option)
|
|
# any later version.
|
|
#
|
|
# Please read the COPYING file.
|
|
#
|
|
|
|
import sys
|
|
from optparse import OptionParser, OptionGroup, HelpFormatter
|
|
|
|
import gettext
|
|
__trans = gettext.translation('pisi', fallback=True)
|
|
_ = __trans.ugettext
|
|
|
|
import pisi
|
|
import pisi.cli
|
|
import pisi.context as ctx
|
|
from pisi.uri import URI
|
|
import pisi.util as util
|
|
from colors import colorize
|
|
|
|
class Error(pisi.Error):
|
|
pass
|
|
|
|
class PisiHelpFormatter(HelpFormatter):
|
|
def __init__(self,
|
|
indent_increment=1,
|
|
max_help_position=32,
|
|
width=None,
|
|
short_first=1):
|
|
HelpFormatter.__init__(
|
|
self, indent_increment, max_help_position, width, short_first)
|
|
|
|
self._short_opt_fmt = "%s"
|
|
self._long_opt_fmt = "%s"
|
|
|
|
def format_usage(self, usage):
|
|
return _("usage: %s\n") % usage
|
|
|
|
def format_heading(self, heading):
|
|
return "%*s%s:\n" % (self.current_indent, "", heading)
|
|
|
|
def format_option_strings(self, option):
|
|
"""Return a comma-separated list of option strings & metavariables."""
|
|
if option.takes_value():
|
|
short_opts = [self._short_opt_fmt % sopt
|
|
for sopt in option._short_opts]
|
|
long_opts = [self._long_opt_fmt % lopt
|
|
for lopt in option._long_opts]
|
|
else:
|
|
short_opts = option._short_opts
|
|
long_opts = option._long_opts
|
|
|
|
if long_opts and short_opts:
|
|
opt = "%s [%s]" % (short_opts[0], long_opts[0])
|
|
else:
|
|
opt = long_opts[0] or short_opts[0]
|
|
|
|
if option.takes_value():
|
|
opt += " arg"
|
|
|
|
return opt
|
|
|
|
def format_option(self, option):
|
|
import textwrap
|
|
result = []
|
|
opts = self.option_strings[option]
|
|
opt_width = self.help_position - self.current_indent - 2
|
|
if len(opts) > opt_width:
|
|
opts = "%*s%s\n" % (self.current_indent, "", opts)
|
|
indent_first = self.help_position
|
|
else: # start help on same line as opts
|
|
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
|
|
indent_first = 0
|
|
result.append(opts)
|
|
if option.help:
|
|
help_text = self.expand_default(option)
|
|
help_lines = textwrap.wrap(help_text, self.help_width)
|
|
result.append(": %*s%s\n" % (indent_first, "", help_lines[0]))
|
|
result.extend([" %*s%s\n" % (self.help_position, "", line)
|
|
for line in help_lines[1:]])
|
|
elif opts[-1] != "\n":
|
|
result.append("\n")
|
|
return "".join(result)
|
|
|
|
|
|
class Command(object):
|
|
"""generic help string for any command"""
|
|
|
|
# class variables
|
|
|
|
cmd = []
|
|
cmd_dict = {}
|
|
|
|
@staticmethod
|
|
def commands_string():
|
|
s = ''
|
|
list = [x.name[0] for x in Command.cmd]
|
|
list.sort()
|
|
for name in list:
|
|
commandcls = Command.cmd_dict[name]
|
|
trans = gettext.translation('pisi', fallback=True)
|
|
summary = trans.ugettext(commandcls.__doc__).split('\n')[0]
|
|
name = commandcls.name[0]
|
|
if commandcls.name[1]:
|
|
name += ' (%s)' % commandcls.name[1]
|
|
s += '%21s - %s\n' % (name, summary)
|
|
return s
|
|
|
|
@staticmethod
|
|
def get_command(cmd, fail=False, args=None):
|
|
|
|
if Command.cmd_dict.has_key(cmd):
|
|
return Command.cmd_dict[cmd](args)
|
|
|
|
if fail:
|
|
raise Error(_("Unrecognized command: %s") % cmd)
|
|
else:
|
|
return None
|
|
|
|
# instance variabes
|
|
|
|
def __init__(self, args = None):
|
|
# now for the real parser
|
|
import pisi
|
|
self.comar = False
|
|
self.parser = OptionParser(usage=getattr(self, "__doc__"),
|
|
version="%prog " + pisi.__version__,
|
|
formatter=PisiHelpFormatter())
|
|
self.options()
|
|
self.commonopts()
|
|
(self.options, self.args) = self.parser.parse_args(args)
|
|
if self.args:
|
|
self.args.pop(0) # exclude command arg
|
|
|
|
self.process_opts()
|
|
|
|
def commonopts(self):
|
|
'''common options'''
|
|
p = self.parser
|
|
|
|
group = OptionGroup(self.parser, _("general options"))
|
|
|
|
group.add_option("-D", "--destdir", action="store", default = None,
|
|
help = _("Change the system root for PiSi commands"))
|
|
group.add_option("-y", "--yes-all", action="store_true",
|
|
default=False, help = _("Assume yes in all yes/no queries"))
|
|
group.add_option("-u", "--username", action="store")
|
|
group.add_option("-p", "--password", action="store")
|
|
group.add_option("-v", "--verbose", action="store_true",
|
|
dest="verbose", default=False,
|
|
help=_("Detailed output"))
|
|
group.add_option("-d", "--debug", action="store_true",
|
|
default=False, help=_("Show debugging information"))
|
|
group.add_option("-N", "--no-color", action="store_true", default=False,
|
|
help = _("Suppresses all coloring of PiSi's output"))
|
|
|
|
p.add_option_group(group)
|
|
|
|
return p
|
|
|
|
def options(self):
|
|
"""This is a fall back function. If the implementer module provides an
|
|
options function it will be called"""
|
|
pass
|
|
|
|
def process_opts(self):
|
|
self.check_auth_info()
|
|
|
|
# make destdir absolute
|
|
if self.options.destdir:
|
|
dir = str(self.options.destdir)
|
|
import os.path
|
|
if not os.path.exists(dir):
|
|
pisi.cli.printu(_('Destination directory %s does not exist. Creating directory.\n') % dir)
|
|
os.makedirs(dir)
|
|
self.options.destdir = os.path.realpath(dir)
|
|
|
|
def check_auth_info(self):
|
|
username = self.options.username
|
|
password = self.options.password
|
|
|
|
# TODO: We'll get the username, password pair from a configuration
|
|
# file from users home directory. Currently we need user to
|
|
# give it from the user interface.
|
|
# if not username and not password:
|
|
# if someauthconfig.username and someauthconfig.password:
|
|
# self.authInfo = (someauthconfig.username,
|
|
# someauthconfig.password)
|
|
# return
|
|
if username and password:
|
|
self.options.authinfo = (username, password)
|
|
return
|
|
|
|
if username and not password:
|
|
from getpass import getpass
|
|
password = getpass(_("Password: "))
|
|
self.options.authinfo = (username, password)
|
|
else:
|
|
self.options.authinfo = None
|
|
|
|
def init(self, database = True, write = True):
|
|
"""initialize PiSi components"""
|
|
|
|
# NB: command imports here or in the command class run fxns
|
|
import pisi.api
|
|
pisi.api.init(database = database, write = write, options = self.options,
|
|
comar = self.comar)
|
|
|
|
def finalize(self):
|
|
"""do cleanup work for PiSi components"""
|
|
pisi.api.finalize()
|
|
|
|
def get_name(self):
|
|
return self.__class__.name
|
|
|
|
def format_name(self):
|
|
(name, shortname) = self.get_name()
|
|
if shortname:
|
|
return "%s (%s)" % (name, shortname)
|
|
else:
|
|
return name
|
|
|
|
def help(self):
|
|
"""print help for the command"""
|
|
trans = gettext.translation('pisi', fallback=True)
|
|
print "%s: %s\n" % (self.format_name(), trans.ugettext(self.__doc__))
|
|
print self.parser.format_option_help()
|
|
|
|
def die(self):
|
|
"""exit program"""
|
|
#FIXME: not called from anywhere?
|
|
ctx.ui.error(_('Command terminated abnormally.'))
|
|
sys.exit(-1)
|
|
|
|
|
|
class autocommand(type):
|
|
def __init__(cls, name, bases, dict):
|
|
super(autocommand, cls).__init__(name, bases, dict)
|
|
Command.cmd.append(cls)
|
|
name = getattr(cls, 'name', None)
|
|
if name is None:
|
|
raise pisi.cli.Error(_('Command lacks name'))
|
|
longname, shortname = name
|
|
def add_cmd(cmd):
|
|
if Command.cmd_dict.has_key(cmd):
|
|
raise pisi.cli.Error(_('Duplicate command %s') % cmd)
|
|
else:
|
|
Command.cmd_dict[cmd] = cls
|
|
add_cmd(longname)
|
|
if shortname:
|
|
add_cmd(shortname)
|
|
|
|
|
|
class Help(Command):
|
|
"""Prints help for given commands
|
|
|
|
Usage: help [ <command1> <command2> ... <commandn> ]
|
|
|
|
If run without parameters, it prints the general help."""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args = None):
|
|
#TODO? Discard Help's own usage doc in favor of general usage doc
|
|
#self.__doc__ = usage_text
|
|
#self.__doc__ += commands_string()
|
|
super(Help, self).__init__(args)
|
|
|
|
name = ("help", "?")
|
|
|
|
def run(self):
|
|
|
|
if not self.args:
|
|
self.parser.set_usage(usage_text)
|
|
pisi.cli.printu(self.parser.format_help())
|
|
return
|
|
|
|
self.init(database = False, write = False)
|
|
|
|
for arg in self.args:
|
|
obj = Command.get_command(arg, True)
|
|
obj.help()
|
|
ctx.ui.info('')
|
|
|
|
self.finalize()
|
|
|
|
|
|
class Clean(Command):
|
|
"""Clean stale locks
|
|
|
|
Usage: clean
|
|
|
|
PiSi uses filesystem locks for managing database access.
|
|
This command deletes unused locks from the database directory."""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args=None):
|
|
super(Clean, self).__init__(args)
|
|
|
|
name = ("clean", None)
|
|
|
|
def run(self):
|
|
self.init()
|
|
self.finalize()
|
|
|
|
class DeleteCache(Command):
|
|
"""Delete cache files
|
|
|
|
Usage: delete-cache
|
|
|
|
Sources, packages and temporary files are stored
|
|
under /var directory. Since these accumulate they can
|
|
consume a lot of disk space."""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args=None):
|
|
super(DeleteCache, self).__init__(args)
|
|
|
|
name = ("delete-cache", "dc")
|
|
|
|
def run(self):
|
|
self.init(database=False, write=True)
|
|
pisi.api.delete_cache()
|
|
|
|
|
|
class Graph(Command):
|
|
"""Graph package relations
|
|
|
|
Usage: graph [<package1> <package2> ...]
|
|
|
|
Write a graph of package relations, tracking dependency and
|
|
conflicts relations starting from given packages. By default
|
|
shows the package relations among repository packages, and writes
|
|
the package in graphviz format to 'pgraph.dot'.
|
|
"""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args=None):
|
|
super(Graph, self).__init__(args)
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("graph options"))
|
|
|
|
group.add_option("-r", "--repository", action="store",
|
|
default=None,
|
|
help=_("Specify a particular repository"))
|
|
group.add_option("-i", "--installed", action="store_true",
|
|
default=False,
|
|
help=_("Graph of installed packages"))
|
|
group.add_option("--ignore-installed", action="store_true",
|
|
default=False,
|
|
help=_("Do not show installed packages"))
|
|
group.add_option("-o", "--output", action="store",
|
|
default='pgraph.dot',
|
|
help=_("Dot output file"))
|
|
|
|
self.parser.add_option_group(group)
|
|
|
|
name = ("graph", None)
|
|
|
|
def run(self):
|
|
self.init(write=False)
|
|
if not ctx.get_option('installed'):
|
|
if ctx.get_option('repository'):
|
|
repo = ctx.get_option('repository')
|
|
ctx.ui.info(_('Plotting packages in repository %s') % repo)
|
|
else:
|
|
repo = pisi.db.itembyrepodb.repos
|
|
if self.args:
|
|
a = self.args
|
|
else:
|
|
ctx.ui.info(_('Plotting a graph of relations among all repository packages'))
|
|
a = ctx.packagedb.list_packages(repo)
|
|
else:
|
|
if self.args:
|
|
a = self.args
|
|
else:
|
|
# if A is empty, then graph all packages
|
|
ctx.ui.info(_('Plotting a graph of relations among all installed packages'))
|
|
a = ctx.installdb.list_installed()
|
|
repo = pisi.db.itembyrepodb.installed
|
|
g = pisi.api.package_graph(a, repo = repo,
|
|
ignore_installed = ctx.get_option('ignore_installed'))
|
|
g.write_graphviz(file(ctx.get_option('output'), 'w'))
|
|
self.finalize()
|
|
|
|
# option mixins
|
|
def buildno_opts(self, group):
|
|
group.add_option("--ignore-build-no", action="store_true",
|
|
default=False,
|
|
help=_("Do not take build no into account."))
|
|
|
|
def ignoredep_opt(self, group):
|
|
group.add_option("--ignore-dependency", action="store_true",
|
|
default=False,
|
|
help=_("Do not take dependency information into account"))
|
|
|
|
|
|
class Build(Command):
|
|
"""Build PiSi packages
|
|
|
|
Usage: build [<pspec.xml> | <sourcename>] ...
|
|
|
|
You can give a URI of the pspec.xml file. PiSi will
|
|
fetch all necessary files and build the package for you.
|
|
|
|
Alternatively, you can give the name of a source package
|
|
to be downloaded from a repository containing sources.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Build, self).__init__(args)
|
|
self.comar = True
|
|
|
|
name = ("build", "bi")
|
|
|
|
package_formats = ('1.0', '1.1')
|
|
|
|
def options(self):
|
|
|
|
self.add_steps_options()
|
|
group = OptionGroup(self.parser, _("build options"))
|
|
self.add_options(group)
|
|
self.parser.add_option_group(group)
|
|
|
|
def add_options(self, group):
|
|
buildno_opts(self, group)
|
|
ignoredep_opt(self, group)
|
|
group.add_option("-O", "--output-dir", action="store", default=None,
|
|
help=_("Output directory for produced packages"))
|
|
group.add_option("--ignore-action-errors",
|
|
action="store_true", default=False,
|
|
help=_("Bypass errors from ActionsAPI"))
|
|
group.add_option("--ignore-safety", action="store_true",
|
|
default=False, help=_("Bypass safety switch"))
|
|
group.add_option("--ignore-check", action="store_true",
|
|
default=False, help=_("Bypass testing step"))
|
|
group.add_option("--create-static", action="store_true",
|
|
default=False, help=_("Create a static package with ar files"))
|
|
group.add_option("--no-install", action="store_true",
|
|
default=False, help=_("Do not install build dependencies, fail if a build dependency is present"))
|
|
group.add_option("-F", "--package-format", action="store", default='1.1',
|
|
help=_("PiSi binary package formats: '1.0', '1.1' (default)"))
|
|
|
|
def add_steps_options(self):
|
|
group = OptionGroup(self.parser, _("build steps"))
|
|
group.add_option("--fetch", dest="until", action="store_const",
|
|
const="fetch", help=_("Break build after fetching the source archive"))
|
|
group.add_option("--unpack", dest="until", action="store_const",
|
|
const="unpack", help=_("Break build after unpacking the source archive, checking sha1sum and applying patches"))
|
|
group.add_option("--setup", dest="until", action="store_const",
|
|
const="setup", help=_("Break build after running configure step"))
|
|
group.add_option("--build", dest="until", action="store_const",
|
|
const="build", help=_("Break build after running compile step"))
|
|
group.add_option("--check", dest="until", action="store_const",
|
|
const="check", help=_("Break build after running check step"))
|
|
group.add_option("--install", dest="until", action="store_const",
|
|
const="install", help=_("Break build after running install step"))
|
|
group.add_option("--package", dest="until", action="store_const",
|
|
const="package", help=_("create PiSi package"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
if not self.args:
|
|
self.help()
|
|
return
|
|
|
|
if self.options.no_install:
|
|
self.init(database=True, write=False)
|
|
else:
|
|
self.init()
|
|
|
|
if ctx.get_option('package_format') not in Build.package_formats:
|
|
raise Error(_('package_format must be one of %s ') % pisi.util.strlist(Build.package_formats))
|
|
|
|
if ctx.get_option('output_dir'):
|
|
ctx.ui.info(_('Output directory: %s') % ctx.config.options.output_dir)
|
|
else:
|
|
ctx.ui.info(_('Outputting packages in the working directory.'))
|
|
ctx.config.options.output_dir = '.'
|
|
|
|
for x in self.args:
|
|
if ctx.get_option('until'):
|
|
pisi.api.build_until(x, ctx.get_option('until'))
|
|
else:
|
|
pisi.api.build(x)
|
|
self.finalize()
|
|
|
|
class Delta(Command):
|
|
"""Creates delta PiSi packages
|
|
|
|
Usage: delta oldpackage newpackage
|
|
|
|
Delta command finds the changed files between the given packages by comparing the sha1sum of the files
|
|
and creates a delta pisi package with the changed files between two releases.
|
|
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Delta, self).__init__(args)
|
|
|
|
name = ("delta", "dt")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("delta options"))
|
|
self.add_options(group)
|
|
self.parser.add_option_group(group)
|
|
|
|
def add_options(self, group):
|
|
group.add_option("-O", "--output-dir", action="store", default=None,
|
|
help=_("Output directory for produced packages"))
|
|
|
|
def run(self):
|
|
|
|
from pisi.delta import create_delta_package
|
|
|
|
self.init(database=False, write=False)
|
|
|
|
if len(self.args) is not 2:
|
|
self.help()
|
|
return
|
|
|
|
if ctx.get_option('output_dir'):
|
|
ctx.ui.info(_('Output directory: %s') % ctx.config.options.output_dir)
|
|
else:
|
|
ctx.ui.info(_('Outputting packages in the working directory.'))
|
|
ctx.config.options.output_dir = '.'
|
|
|
|
oldpackage = self.args[0]
|
|
newpackage = self.args[1]
|
|
|
|
create_delta_package(oldpackage, newpackage)
|
|
|
|
self.finalize()
|
|
|
|
class Emerge(Build):
|
|
"""Build and install PiSi source packages from repository
|
|
|
|
Usage: emerge <sourcename> ...
|
|
|
|
You should give the name of a source package to be
|
|
downloaded from a repository containing sources.
|
|
|
|
You can also give the name of a component.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Emerge, self).__init__(args)
|
|
self.comar = True
|
|
|
|
name = ("emerge", "em")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("emerge options"))
|
|
super(Emerge, self).add_options(group)
|
|
group.add_option("--ignore-file-conflicts", action="store_true",
|
|
default=False, help=_("Ignore file conflicts"))
|
|
group.add_option("--ignore-package-conflicts", action="store_true",
|
|
default=False, help=_("Ignore package conflicts"))
|
|
group.add_option("--ignore-comar", action="store_true",
|
|
default=False, help=_("Bypass comar configuration agent"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
if not self.args:
|
|
self.help()
|
|
return
|
|
|
|
self.init(database = True)
|
|
if ctx.get_option('output_dir'):
|
|
ctx.ui.info(_('Output directory: %s') % ctx.config.options.output_dir)
|
|
else:
|
|
ctx.ui.info(_('Outputting binary packages in the package cache.'))
|
|
ctx.config.options.output_dir = ctx.config.packages_dir()
|
|
|
|
pisi.api.emerge(self.args)
|
|
self.finalize()
|
|
|
|
|
|
class PackageOp(Command):
|
|
"""Abstract package operation command"""
|
|
|
|
def __init__(self, args):
|
|
super(PackageOp, self).__init__(args)
|
|
self.comar = True
|
|
|
|
def options(self, group):
|
|
ignoredep_opt(self, group)
|
|
group.add_option("--ignore-comar", action="store_true",
|
|
default=False, help=_("Bypass comar configuration agent"))
|
|
group.add_option("--ignore-safety", action="store_true",
|
|
default=False, help=_("Bypass safety switch"))
|
|
group.add_option("-n", "--dry-run", action="store_true", default=False,
|
|
help = _("Do not perform any action, just show what would be done"))
|
|
|
|
def init(self, database=True, write=True):
|
|
super(PackageOp, self).init(database, write)
|
|
|
|
def finalize(self):
|
|
#self.finalize_db()
|
|
pass
|
|
|
|
|
|
class Install(PackageOp):
|
|
"""Install PiSi packages
|
|
|
|
Usage: install <package1> <package2> ... <packagen>
|
|
|
|
You may use filenames, URI's or package names for packages. If you have
|
|
specified a package name, it should exist in a specified repository.
|
|
|
|
You can also specify components instead of package names, which will be
|
|
expanded to package names.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Install, self).__init__(args)
|
|
|
|
name = "install", "it"
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("install options"))
|
|
|
|
super(Install, self).options(group)
|
|
buildno_opts(self, group)
|
|
group.add_option("--reinstall", action="store_true",
|
|
default=False, help=_("Reinstall already installed packages"))
|
|
group.add_option("--ignore-file-conflicts", action="store_true",
|
|
default=False, help=_("Ignore file conflicts"))
|
|
group.add_option("--ignore-package-conflicts", action="store_true",
|
|
default=False, help=_("Ignore package conflicts"))
|
|
group.add_option("-c", "--component", action="append",
|
|
default=None, help=_("Install component's and recursive components' packages"))
|
|
group.add_option("-f", "--fetch-only", action="store_true",
|
|
default=False, help=_("Fetch upgrades but do not install."))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
if self.options.fetch_only:
|
|
self.init(database=True, write=False)
|
|
else:
|
|
self.init()
|
|
|
|
components = ctx.get_option('component')
|
|
if not components and not self.args:
|
|
self.help()
|
|
return
|
|
|
|
packages = []
|
|
if components:
|
|
for name in components:
|
|
if ctx.componentdb.has_component(name):
|
|
packages.extend(ctx.componentdb.get_union_packages(name, walk=True))
|
|
packages.extend(self.args)
|
|
|
|
pisi.api.install(packages, ctx.get_option('reinstall'))
|
|
self.finalize()
|
|
|
|
|
|
class Upgrade(PackageOp):
|
|
"""Upgrade PiSi packages
|
|
|
|
Usage: Upgrade [<package1> <package2> ... <packagen>]
|
|
|
|
<packagei>: package name
|
|
|
|
Upgrades the entire system if no package names are given
|
|
|
|
You may use only package names to specify packages because
|
|
the package upgrade operation is defined only with respect
|
|
to repositories. If you have specified a package name, it
|
|
should exist in the package repositories. If you just want to
|
|
reinstall a package from a PiSi file, use the install command.
|
|
|
|
You can also specify components instead of package names, which will be
|
|
expanded to package names.
|
|
"""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Upgrade, self).__init__(args)
|
|
|
|
name = ("upgrade", "up")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("upgrade options"))
|
|
|
|
super(Upgrade, self).options(group)
|
|
buildno_opts(self, group)
|
|
group.add_option("--security-only", action="store_true",
|
|
default=False, help=_("Security related package upgrades only"))
|
|
group.add_option("-r", "--bypass-update-repo", action="store_true",
|
|
default=False, help=_("Do not update repositories"))
|
|
group.add_option("--ignore-file-conflicts", action="store_true",
|
|
default=False, help=_("Ignore file conflicts"))
|
|
group.add_option("--ignore-package-conflicts", action="store_true",
|
|
default=False, help=_("Ignore package conflicts"))
|
|
group.add_option("-c", "--component", action="append",
|
|
default=None, help=_("Upgrade component's and recursive components' packages"))
|
|
group.add_option("-f", "--fetch-only", action="store_true",
|
|
default=False, help=_("Fetch upgrades but do not install."))
|
|
group.add_option("-x", "--exclude", action="append",
|
|
default=None, help=_("When upgrading system, ignore packages and components whose basenames match pattern."))
|
|
group.add_option("--exclude-from", action="store",
|
|
default=None, help=_("When upgrading system, ignore packages and components whose basenames \
|
|
match any pattern contained in file."))
|
|
|
|
self.parser.add_option_group(group)
|
|
|
|
def exclude_from(self, packages):
|
|
import os
|
|
|
|
patterns = []
|
|
file = ctx.get_option('exclude_from')
|
|
if os.path.exists(file):
|
|
for line in open(file, "r").readlines():
|
|
if not line.startswith('#') and not line == '\n':
|
|
patterns.append(line.strip())
|
|
if patterns:
|
|
return self.exclude(packages, patterns)
|
|
|
|
return packages
|
|
|
|
def exclude(self, packages, patterns):
|
|
from sets import Set as set
|
|
import fnmatch
|
|
|
|
packages = set(packages)
|
|
for pattern in patterns:
|
|
# match pattern in package names
|
|
match = fnmatch.filter(packages, pattern)
|
|
packages = packages - set(match)
|
|
|
|
if not match:
|
|
# match pattern in component names
|
|
for cmp in fnmatch.filter(ctx.componentdb.list_components(), pattern):
|
|
packages = packages - set(ctx.componentdb.get_union_packages(cmp, walk=True))
|
|
|
|
return list(packages)
|
|
|
|
def run(self):
|
|
|
|
if self.options.fetch_only:
|
|
self.init(database=True, write=False)
|
|
else:
|
|
self.init()
|
|
|
|
if not ctx.get_option('bypass_update_repo'):
|
|
ctx.ui.info(_('Updating repositories'))
|
|
repos = ctx.repodb.list()
|
|
for repo in repos:
|
|
pisi.api.update_repo(repo)
|
|
else:
|
|
ctx.ui.info(_('Will not update repositories'))
|
|
|
|
components = ctx.get_option('component')
|
|
packages = []
|
|
if components:
|
|
for name in components:
|
|
if ctx.componentdb.has_component(name):
|
|
packages.extend(ctx.componentdb.get_union_packages(name, walk=True))
|
|
packages.extend(self.args)
|
|
|
|
if packages == []:
|
|
packages = ctx.installdb.list_installed()
|
|
|
|
if ctx.get_option('exclude_from'):
|
|
packages = self.exclude_from(packages)
|
|
|
|
if ctx.get_option('exclude'):
|
|
patterns = ctx.get_option('exclude')
|
|
packages = self.exclude(packages, patterns)
|
|
|
|
pisi.api.upgrade(packages)
|
|
self.finalize()
|
|
|
|
|
|
class Remove(PackageOp):
|
|
"""Remove PiSi packages
|
|
|
|
Usage: remove <package1> <package2> ... <packagen>
|
|
|
|
Remove package(s) from your system. Just give the package names to remove.
|
|
|
|
You can also specify components instead of package names, which will be
|
|
expanded to package names.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Remove, self).__init__(args)
|
|
|
|
name = ("remove", "rm")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("remove options"))
|
|
super(Remove, self).options(group)
|
|
group.add_option("-c", "--component", action="append",
|
|
default=None, help=_("Remove component's and recursive components' packages"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
self.init()
|
|
|
|
components = ctx.get_option('component')
|
|
if not components and not self.args:
|
|
self.help()
|
|
return
|
|
|
|
packages = []
|
|
if components:
|
|
for name in components:
|
|
if ctx.componentdb.has_component(name):
|
|
packages.extend(ctx.componentdb.get_union_packages(name, walk=True))
|
|
packages.extend(self.args)
|
|
|
|
pisi.api.remove(packages)
|
|
self.finalize()
|
|
|
|
class ConfigurePending(PackageOp):
|
|
"""Configure pending packages
|
|
|
|
If COMAR configuration of some packages were not
|
|
done at installation time, they are added to a list
|
|
of packages waiting to be configured. This command
|
|
configures those packages.
|
|
"""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ConfigurePending, self).__init__(args)
|
|
|
|
name = ("configure-pending", "cp")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("configure-pending options"))
|
|
super(ConfigurePending, self).options(group)
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
self.init()
|
|
pisi.api.configure_pending()
|
|
self.finalize()
|
|
|
|
|
|
class Info(Command):
|
|
"""Display package information
|
|
|
|
Usage: info <package1> <package2> ... <packagen>
|
|
|
|
<packagei> is either a package name or a .pisi file,
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Info, self).__init__(args)
|
|
|
|
name = ("info", None)
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("info options"))
|
|
self.add_options(group)
|
|
self.parser.add_option_group(group)
|
|
|
|
def add_options(self, group):
|
|
group.add_option("-f", "--files", action="store_true",
|
|
default=False,
|
|
help=_("Show a list of package files."))
|
|
group.add_option("-c", "--component", action="append",
|
|
default=None, help=_("Info about the given component"))
|
|
group.add_option("-F", "--files-path", action="store_true",
|
|
default=False,
|
|
help=_("Show only paths."))
|
|
group.add_option("-s", "--short", action="store_true",
|
|
default=False, help=_("Do not show details"))
|
|
group.add_option("--xml", action="store_true",
|
|
default=False, help=_("Output in xml format"))
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
|
|
components = ctx.get_option('component')
|
|
if not components and not self.args:
|
|
self.help()
|
|
return
|
|
|
|
index = pisi.index.Index()
|
|
index.distribution = None
|
|
|
|
# info of components
|
|
if components:
|
|
for name in components:
|
|
if ctx.componentdb.has_component(name):
|
|
component = ctx.componentdb.get_union_comp(name)
|
|
if self.options.xml:
|
|
index.add_component(component)
|
|
else:
|
|
if not self.options.short:
|
|
ctx.ui.info(unicode(component))
|
|
else:
|
|
ctx.ui.info("%s - %s" % (component.name, component.summary))
|
|
|
|
# info of packages
|
|
for arg in self.args:
|
|
if self.options.xml:
|
|
index.packages.append(pisi.api.info(arg)[0].package)
|
|
else:
|
|
self.info_package(arg)
|
|
|
|
if self.options.xml:
|
|
errs = []
|
|
index.newDocument()
|
|
index.encode(index.rootNode(), errs)
|
|
index.writexmlfile(sys.stdout)
|
|
sys.stdout.write('\n')
|
|
self.finalize()
|
|
|
|
|
|
def info_package(self, arg):
|
|
if arg.endswith(ctx.const.package_suffix):
|
|
metadata, files = pisi.api.info_file(arg)
|
|
ctx.ui.info(_('Package file: %s') % arg)
|
|
self.print_pkginfo(metadata, files)
|
|
else:
|
|
if ctx.installdb.is_installed(arg):
|
|
metadata, files = pisi.api.info_name(arg, True)
|
|
if self.options.short:
|
|
ctx.ui.info(_('[inst] '), noln=True)
|
|
else:
|
|
ctx.ui.info(_('Installed package:'))
|
|
self.print_pkginfo(metadata, files,pisi.db.itembyrepodb.installed)
|
|
|
|
if ctx.packagedb.has_package(arg):
|
|
metadata, files = pisi.api.info_name(arg, False)
|
|
if self.options.short:
|
|
ctx.ui.info(_('[repo] '), noln=True)
|
|
else:
|
|
ctx.ui.info(_('Package found in repository:'))
|
|
self.print_pkginfo(metadata, files, pisi.db.itembyrepodb.repos)
|
|
|
|
if not ctx.packagedb.has_package(arg):
|
|
ctx.ui.info(_("%s is not found in repositories") % arg)
|
|
|
|
if not ctx.installdb.is_installed(arg):
|
|
ctx.ui.info(_("%s is not installed") % arg)
|
|
|
|
def print_pkginfo(self, metadata, files, repo = None):
|
|
import os.path
|
|
|
|
if ctx.get_option('short'):
|
|
pkg = metadata.package
|
|
ctx.ui.info('%15s - %s' % (pkg.name, unicode(pkg.summary)))
|
|
else:
|
|
ctx.ui.info(unicode(metadata.package))
|
|
if repo:
|
|
revdeps = [x[0] for x in
|
|
ctx.packagedb.get_rev_deps(metadata.package.name, repo)]
|
|
print _('Reverse Dependencies:'), util.strlist(revdeps)
|
|
if self.options.files or self.options.files_path:
|
|
if files:
|
|
print _('\nFiles:')
|
|
files.list.sort(key = lambda x:x.path)
|
|
for fileinfo in files.list:
|
|
if self.options.files:
|
|
print fileinfo
|
|
else:
|
|
print "/" + fileinfo.path
|
|
else:
|
|
ctx.ui.warning(_('File information not available'))
|
|
if not self.options.short:
|
|
print
|
|
|
|
|
|
class Check(Command):
|
|
"""Verify installation
|
|
|
|
Usage: check [<package1> <package2> ... <packagen>]
|
|
|
|
<packagei>: package name
|
|
|
|
A cryptographic checksum is stored for each installed
|
|
file. Check command uses the checksums to verify a package.
|
|
Just give the names of packages.
|
|
|
|
If no packages are given, checks all installed packages.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Check, self).__init__(args)
|
|
|
|
name = ("check", None)
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("check options"))
|
|
group.add_option("-c", "--component", action="store",
|
|
default=None, help=_("Check installed packages under given component"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
self.init(database = True, write = False)
|
|
|
|
component = ctx.get_option('component')
|
|
if component:
|
|
#FIXME: pisi api is insufficient to do this
|
|
from sets import Set as set
|
|
installed = ctx.installdb.list_installed()
|
|
component_pkgs = ctx.componentdb.get_union_packages(component, walk=True)
|
|
pkgs = list(set(installed) & set(component_pkgs))
|
|
elif self.args:
|
|
pkgs = self.args
|
|
else:
|
|
ctx.ui.info(_('Checking all installed packages'))
|
|
pkgs = ctx.installdb.list_installed()
|
|
|
|
for pkg in pkgs:
|
|
ctx.ui.info(_('* Checking %s... ') % pkg, noln=True)
|
|
if ctx.installdb.is_installed(pkg):
|
|
corrupt = pisi.api.check(pkg)
|
|
if corrupt:
|
|
ctx.ui.info(_('\nPackage %s is corrupt.') % pkg)
|
|
else:
|
|
ctx.ui.info(_("OK"), verbose=False)
|
|
else:
|
|
ctx.ui.info(_('Package %s not installed') % pkg)
|
|
self.finalize()
|
|
|
|
|
|
class Index(Command):
|
|
"""Index PiSi files in a given directory
|
|
|
|
Usage: index <directory> ...
|
|
|
|
This command searches for all PiSi files in a directory, collects PiSi
|
|
tags from them and accumulates the information in an output XML file,
|
|
named by default 'pisi-index.xml'. In particular, it indexes both
|
|
source and binary packages.
|
|
|
|
If you give multiple directories, the command still works, but puts
|
|
everything in a single index file.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Index, self).__init__(args)
|
|
|
|
name = ("index", "ix")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("index options"))
|
|
|
|
group.add_option("-a", "--absolute-urls", action="store_true",
|
|
default=False,
|
|
help=_("Store absolute links for indexed files."))
|
|
group.add_option("-o", "--output", action="store",
|
|
default='pisi-index.xml',
|
|
help=_("Index output file"))
|
|
group.add_option("--skip-sources", action="store_true",
|
|
default=False,
|
|
help=_("Do not index PiSi spec files."))
|
|
group.add_option("--skip-signing", action="store_true",
|
|
default=False,
|
|
help=_("Do not sign index."))
|
|
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
from pisi.api import index
|
|
if len(self.args)>0:
|
|
index(self.args, ctx.get_option('output'),
|
|
skip_sources = ctx.get_option('skip_sources'),
|
|
skip_signing = ctx.get_option('skip_signing'))
|
|
elif len(self.args)==0:
|
|
ctx.ui.info(_('Indexing current directory.'))
|
|
index(['.'], ctx.get_option('output'),
|
|
skip_sources = ctx.get_option('skip_sources'),
|
|
skip_signing = ctx.get_option('skip_signing'))
|
|
self.finalize()
|
|
|
|
|
|
class ListInstalled(Command):
|
|
"""Print the list of all installed packages
|
|
|
|
Usage: list-installed
|
|
"""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ListInstalled, self).__init__(args)
|
|
|
|
name = ("list-installed", "li")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("list-installed options"))
|
|
|
|
group.add_option("-l", "--long", action="store_true",
|
|
default=False, help=_("Show in long format"))
|
|
group.add_option("-c", "--component", action="store",
|
|
default=None, help=_("List installed packages under given component"))
|
|
group.add_option("-i", "--install-info", action="store_true",
|
|
default=False, help=_("Show detailed install info"))
|
|
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
self.init(database = True, write = False)
|
|
installed = ctx.installdb.list_installed()
|
|
|
|
component = ctx.get_option('component')
|
|
if component:
|
|
#FIXME: pisi api is insufficient to do this
|
|
from sets import Set as set
|
|
component_pkgs = ctx.componentdb.get_union_packages(component, walk=True)
|
|
installed = list(set(installed) & set(component_pkgs))
|
|
|
|
installed.sort()
|
|
if self.options.install_info:
|
|
ctx.ui.info(_('Package Name |St| Version| Rel.| Build| Distro| Date'))
|
|
print '========================================================================'
|
|
for pkg in installed:
|
|
package = pisi.api.get_installed_package(pkg)
|
|
inst_info = ctx.installdb.get_info(pkg)
|
|
if self.options.long:
|
|
ctx.ui.info(unicode(package))
|
|
ctx.ui.info(unicode(inst_info))
|
|
elif self.options.install_info:
|
|
ctx.ui.info('%-15s |%s' % (package.name, inst_info.one_liner()))
|
|
else:
|
|
ctx.ui.info('%15s - %s' % (package.name, unicode(package.summary)))
|
|
self.finalize()
|
|
|
|
class RebuildDb(Command):
|
|
"""Rebuild Databases
|
|
|
|
Usage: rebuilddb [ <package1> <package2> ... <packagen> ]
|
|
|
|
Rebuilds the PiSi databases
|
|
|
|
If package specs are given, they should be the names of package
|
|
dirs under /var/lib/pisi
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(RebuildDb, self).__init__(args)
|
|
|
|
name = ("rebuild-db", "rdb")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("rebuild-db options"))
|
|
|
|
group.add_option("-f", "--files", action="store_true",
|
|
default=False, help=_("Rebuild files database"))
|
|
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
if self.args:
|
|
self.init(database=True)
|
|
for package_fn in self.args:
|
|
pisi.api.resurrect_package(package_fn, ctx.get_option('files`'))
|
|
else:
|
|
self.init(database=False)
|
|
if ctx.ui.confirm(_('Rebuild PiSi databases?')):
|
|
pisi.api.rebuild_db(ctx.get_option('files'))
|
|
|
|
self.finalize()
|
|
|
|
|
|
class UpdateRepo(Command):
|
|
"""Update repository databases
|
|
|
|
Usage: update-repo [<repo1> <repo2> ... <repon>]
|
|
|
|
<repoi>: repository name
|
|
|
|
Synchronizes the PiSi databases with the current repository.
|
|
If no repository is given, all repositories are updated.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self,args):
|
|
super(UpdateRepo, self).__init__(args)
|
|
|
|
name = ("update-repo", "ur")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("update-repo options"))
|
|
|
|
group.add_option("-f", "--force", action="store_true",
|
|
default=False,
|
|
help=_("Update database in any case"))
|
|
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
self.init(database = True)
|
|
|
|
if self.args:
|
|
repos = self.args
|
|
else:
|
|
repos = ctx.repodb.list()
|
|
|
|
for repo in repos:
|
|
pisi.api.update_repo(repo, ctx.get_option('force'))
|
|
self.finalize()
|
|
|
|
|
|
class AddRepo(Command):
|
|
"""Add a repository
|
|
|
|
Usage: add-repo <repo> <indexuri>
|
|
|
|
<repo>: name of repository to add
|
|
<indexuri>: URI of index file
|
|
|
|
If no repo is given, add-repo pardus-devel repo is added by default
|
|
|
|
NB: We support only local files (e.g., /a/b/c) and http:// URIs at the moment
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(AddRepo, self).__init__(args)
|
|
|
|
name = ("add-repo", "ar")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("add-repo options"))
|
|
group.add_option("--at", action="store",
|
|
type="int", default=None,
|
|
help=_("Add repository at given position (0 is first)"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
if len(self.args)==2 or len(self.args)==0:
|
|
self.init()
|
|
if len(self.args)==2:
|
|
name = self.args[0]
|
|
indexuri = self.args[1]
|
|
else:
|
|
name = 'pardus-2007'
|
|
indexuri = 'http://paketler.pardus.org.tr/pardus-2007/pisi-index.xml.bz2'
|
|
pisi.api.add_repository(name, indexuri, ctx.get_option('at'))
|
|
if ctx.ui.confirm(_('Update PiSi database for repository %s?') % name):
|
|
try:
|
|
pisi.api.update_repo(name)
|
|
except pisi.fetcher.FetchError:
|
|
ctx.ui.warning(_("%s repository could not be reached. Removing %s from system.") % (name, name))
|
|
pisi.api.remove_repo(name)
|
|
self.finalize()
|
|
else:
|
|
self.help()
|
|
return
|
|
|
|
|
|
class RemoveRepo(Command):
|
|
"""Remove repositories
|
|
|
|
Usage: remove-repo <repo1> <repo2> ... <repon>
|
|
|
|
Remove all repository information from the system.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self,args):
|
|
super(RemoveRepo, self).__init__(args)
|
|
|
|
name = ("remove-repo", "rr")
|
|
|
|
def run(self):
|
|
|
|
if len(self.args)>=1:
|
|
self.init()
|
|
for repo in self.args:
|
|
pisi.api.remove_repo(repo)
|
|
self.finalize()
|
|
else:
|
|
self.help()
|
|
return
|
|
|
|
|
|
class ListRepo(Command):
|
|
"""List repositories
|
|
|
|
Usage: list-repo
|
|
|
|
Lists currently tracked repositories.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ListRepo, self).__init__(args)
|
|
|
|
name = ("list-repo", "lr")
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
for repo in ctx.repodb.list():
|
|
ctx.ui.info(repo)
|
|
print ' ', ctx.repodb.get_repo(repo).indexuri.get_uri()
|
|
self.finalize()
|
|
|
|
|
|
class ListAvailable(Command):
|
|
"""List available packages in the repositories
|
|
|
|
Usage: list-available [ <repo1> <repo2> ... repon ]
|
|
|
|
Gives a brief list of PiSi packages published in the specified
|
|
repositories. If no repository is specified, we list packages in
|
|
all repositories.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ListAvailable, self).__init__(args)
|
|
|
|
name = ("list-available", "la")
|
|
|
|
def options(self):
|
|
|
|
group = OptionGroup(self.parser, _("list-available options"))
|
|
group.add_option("-l", "--long", action="store_true",
|
|
default=False, help=_("Show in long format"))
|
|
group.add_option("-c", "--component", action="store",
|
|
default=None, help=_("List available packages under given component"))
|
|
group.add_option("-U", "--uninstalled", action="store_true",
|
|
default=False, help=_("Show uninstalled packages only"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
|
|
if not (ctx.get_option('no_color') or ctx.config.get_option('uninstalled')):
|
|
ctx.ui.info(colorize(_('Installed packages are shown in this color'), 'green'))
|
|
|
|
if self.args:
|
|
for arg in self.args:
|
|
self.print_packages(arg)
|
|
else:
|
|
# print for all repos
|
|
for repo in ctx.repodb.list():
|
|
ctx.ui.info(_("Repository : %s\n") % repo)
|
|
self.print_packages(repo)
|
|
self.finalize()
|
|
|
|
def print_packages(self, repo):
|
|
|
|
component = ctx.get_option('component')
|
|
if component:
|
|
list = ctx.componentdb.get_packages(component, walk=True, repo=repo)
|
|
else:
|
|
list = ctx.packagedb.list_packages(repo)
|
|
installed_list = ctx.installdb.list_installed()
|
|
list.sort()
|
|
for p in list:
|
|
package = pisi.api.get_repo_package(p)
|
|
if self.options.long:
|
|
ctx.ui.info(unicode(package))
|
|
else:
|
|
lenp = len(p)
|
|
if p in installed_list:
|
|
if ctx.config.get_option('uninstalled'):
|
|
continue
|
|
p = colorize(p, 'green')
|
|
p = p + ' ' * max(0, 15 - lenp)
|
|
ctx.ui.info('%s - %s ' % (p, unicode(package.summary)))
|
|
|
|
class ListComponents(Command):
|
|
"""List available components
|
|
|
|
Usage: list-components
|
|
|
|
Gives a brief list of PiSi components published in the
|
|
repositories.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ListComponents, self).__init__(args)
|
|
|
|
name = ("list-components", "lc")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("list-components options"))
|
|
group.add_option("-l", "--long", action="store_true",
|
|
default=False, help=_("Show in long format"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
|
|
list = ctx.componentdb.list_components()
|
|
list.sort()
|
|
for p in list:
|
|
component = ctx.componentdb.get_component(p)
|
|
if self.options.long:
|
|
ctx.ui.info(unicode(component))
|
|
else:
|
|
lenp = len(p)
|
|
#if p in installed_list:
|
|
# p = colorize(p, 'cyan')
|
|
p = p + ' ' * max(0, 15 - lenp)
|
|
ctx.ui.info('%s - %s ' % (component.name, unicode(component.summary)))
|
|
self.finalize()
|
|
|
|
|
|
class ListSources(Command):
|
|
"""List available sources
|
|
|
|
Usage: list-sources
|
|
|
|
Gives a brief list of sources published in the repositories.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ListSources, self).__init__(args)
|
|
|
|
name = ("list-sources", "ls")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("list-sources options"))
|
|
group.add_option("-l", "--long", action="store_true",
|
|
default=False, help=_("Show in long format"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
|
|
list = ctx.sourcedb.list()
|
|
list.sort()
|
|
for p in list:
|
|
sf, repo = ctx.sourcedb.get_spec_repo(p)
|
|
if self.options.long:
|
|
ctx.ui.info('[Repository: ' + repo + ']')
|
|
ctx.ui.info(unicode(sf.source))
|
|
else:
|
|
lenp = len(p)
|
|
#if p in installed_list:
|
|
# p = colorize(p, 'cyan')
|
|
p = p + ' ' * max(0, 15 - lenp)
|
|
ctx.ui.info('%s - %s' % (sf.source.name, unicode(sf.source.summary)))
|
|
self.finalize()
|
|
|
|
class ListUpgrades(Command):
|
|
"""List packages to be upgraded
|
|
|
|
Usage: list-upgrades
|
|
|
|
Lists the packages that will be upgraded.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ListUpgrades, self).__init__(args)
|
|
|
|
name = ("list-upgrades", "lu")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("list-upgrades options"))
|
|
buildno_opts(self, group)
|
|
group.add_option("-l", "--long", action="store_true",
|
|
default=False, help=_("Show in long format"))
|
|
group.add_option("-c", "--component", action="store",
|
|
default=None, help=_("List upgradable packages under given component"))
|
|
group.add_option("-i", "--install-info", action="store_true",
|
|
default=False, help=_("Show detailed install info"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
self.init(database = True, write = False)
|
|
upgradable_pkgs = pisi.api.list_upgradable()
|
|
|
|
component = ctx.get_option('component')
|
|
if component:
|
|
#FIXME: PiSi api is insufficient to do this
|
|
from sets import Set as set
|
|
component_pkgs = ctx.componentdb.get_union_packages(component, walk=True)
|
|
upgradable_pkgs = list(set(upgradable_pkgs) & set(component_pkgs))
|
|
|
|
if not upgradable_pkgs:
|
|
ctx.ui.info(_('No packages to upgrade.'))
|
|
|
|
upgradable_pkgs.sort()
|
|
if self.options.install_info:
|
|
ctx.ui.info(_('Package Name |St| Version| Rel.| Build| Distro| Date'))
|
|
print '========================================================================'
|
|
for pkg in upgradable_pkgs:
|
|
package = pisi.api.get_installed_package(pkg)
|
|
inst_info = ctx.installdb.get_info(pkg)
|
|
if self.options.long:
|
|
ctx.ui.info(package)
|
|
print inst_info
|
|
elif self.options.install_info:
|
|
ctx.ui.info('%-15s | %s ' % (package.name, inst_info.one_liner()))
|
|
else:
|
|
ctx.ui.info('%15s - %s ' % (package.name, package.summary))
|
|
self.finalize()
|
|
|
|
|
|
class ListPending(Command):
|
|
"""List pending packages
|
|
|
|
Lists packages waiting to be configured.
|
|
"""
|
|
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(ListPending, self).__init__(args)
|
|
|
|
name = ("list-pending", "lp")
|
|
|
|
def run(self):
|
|
self.init(database = True, write = False)
|
|
|
|
A = ctx.installdb.list_pending()
|
|
order = api.generate_pending_order(A)
|
|
|
|
if len(order):
|
|
for p in order:
|
|
print p
|
|
else:
|
|
ctx.ui.info(_('There are no packages waiting to be configured'))
|
|
self.finalize()
|
|
|
|
|
|
class Search(Info):
|
|
"""Search packages
|
|
|
|
Usage: search <term1> <term2> ... <termn>
|
|
|
|
Finds a package containing specified search terms
|
|
in summary, description, and package name fields.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(Search, self).__init__(args)
|
|
|
|
name = ("search", "sr")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("search options"))
|
|
super(Search, self).add_options(group)
|
|
group.remove_option("--component")
|
|
group.remove_option("--short")
|
|
group.remove_option("--xml")
|
|
group.add_option("-l", "--long", action="store_true",
|
|
default=False, help=_("Show details"))
|
|
self.parser.add_option_group(group)
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
|
|
if not self.args:
|
|
self.help()
|
|
return
|
|
|
|
r = pisi.api.search_package_terms(self.args)
|
|
ctx.ui.info(_('%s packages found') % len(r))
|
|
|
|
ctx.config.options.short = not ctx.config.options.long
|
|
for pkg in r:
|
|
self.info_package(pkg)
|
|
|
|
self.finalize()
|
|
|
|
class SearchFile(Command):
|
|
"""Search for a file
|
|
|
|
Usage: search-file <path1> <path2> ... <pathn>
|
|
|
|
Finds the installed package which contains the specified file.
|
|
"""
|
|
__metaclass__ = autocommand
|
|
|
|
def __init__(self, args):
|
|
super(SearchFile, self).__init__(args)
|
|
|
|
name = ("search-file", "sf")
|
|
|
|
def options(self):
|
|
group = OptionGroup(self.parser, _("search-file options"))
|
|
group.add_option("-l", "--long", action="store_true",
|
|
default=False, help=_("Show in long format"))
|
|
group.add_option("-f", "--fuzzy", action="store_true",
|
|
default=False, help=_("Fuzzy search"))
|
|
group.add_option("-q", "--quiet", action="store_true",
|
|
default=False, help=_("Show only package name"))
|
|
self.parser.add_option_group(group)
|
|
|
|
# what does exact mean? -- exa
|
|
@staticmethod
|
|
def search_exact(path):
|
|
files = []
|
|
path = path.lstrip('/') #FIXME: this shouldn't be necessary :/
|
|
|
|
if not ctx.config.options.fuzzy:
|
|
if ctx.filesdb.has_file(path):
|
|
files.append(ctx.filesdb.get_file(path))
|
|
else:
|
|
#FIXME: this linear search thing is not working well -- exa
|
|
files = ctx.filesdb.match_files(path)
|
|
|
|
if files:
|
|
for (pkg_name, file_info) in files:
|
|
if ctx.config.options.quiet:
|
|
ctx.ui.info(pkg_name)
|
|
else:
|
|
ctx.ui.info(_("Package %s has file %s") % (pkg_name, file_info.path))
|
|
if ctx.config.options.long:
|
|
ctx.ui.info(_('Type: %s, Hash: %s') % (file_info.type,
|
|
file_info.hash))
|
|
else:
|
|
ctx.ui.error(_("Path '%s' does not belong to an installed package") % path)
|
|
|
|
def run(self):
|
|
|
|
self.init(database = True, write = False)
|
|
|
|
if not self.args:
|
|
self.help()
|
|
return
|
|
|
|
# search among existing files
|
|
for path in self.args:
|
|
if not ctx.config.options.quiet:
|
|
ctx.ui.info(_('Searching for %s') % path)
|
|
import os.path
|
|
if os.path.exists(path):
|
|
path = os.path.realpath(path)
|
|
self.search_exact(path)
|
|
|
|
self.finalize()
|
|
|
|
# texts
|
|
|
|
usage_text1 = _("""%prog [options] <command> [arguments]
|
|
|
|
where <command> is one of:
|
|
|
|
""")
|
|
|
|
usage_text2 = _("""
|
|
Use \"%prog help <command>\" for help on a specific command.
|
|
""")
|
|
|
|
usage_text = (usage_text1 + Command.commands_string() + usage_text2)
|