Files
entropy/client/solo/commands/pkg.py
T
Sławomir Nizio 7cf7e1f6b7 convert solo imports, with needed changes to entropy_path_loader
The module entropy_path_loader (used only for running from within the
checkout; otherwise not even installed) is made to provide the _entropy
namespace.

(Other ideas instead of this entropy_path_loader change would be to
reorganise files layout; drop support for running from the checkout as
is - and perhaps require virtualenvs; require sourcing a script that
sets PYTHONPATH. However, as implemented, it is not intrusive, and the
good part is that it is quite isolated, not used in normal usage after
installation. Basically, it only does sys.path + provides _entropy
namespace.)
2018-11-26 20:15:36 +01:00

463 lines
16 KiB
Python

# -*- coding: utf-8 -*-
"""
@author: Fabio Erculiani <lxnay@sabayon.org>
@contact: lxnay@sabayon.org
@copyright: Fabio Erculiani
@license: GPL-2
B{Entropy Command Line Client}.
"""
import os
import errno
import sys
import argparse
import shutil
from entropy.const import etpConst, const_setup_directory, \
const_convert_to_unicode, const_convert_to_rawstring, const_mkstemp
from entropy.i18n import _
from entropy.output import darkgreen, teal, brown, purple, darkred, blue
import entropy.tools
import entropy.dep
from _entropy.solo.commands.descriptor import SoloCommandDescriptor
from _entropy.solo.commands.command import SoloCommand, sharedlock
class SoloPkg(SoloCommand):
"""
Main Solo Smart command.
"""
NAME = "pkg"
ALIASES = ["smart"]
ALLOW_UNPRIVILEGED = False
INTRODUCTION = """\
Execute advanced tasks on Entropy packages and the running system.
"""
SEE_ALSO = ""
def __init__(self, args):
SoloCommand.__init__(self, args)
self._nsargs = None
self._commands = {}
self._savedir = etpConst['entropyunpackdir']
self._ask = False
def man(self):
"""
Overridden from SoloCommand.
"""
return self._man()
def _get_parser(self):
"""
Overridden from SoloCommand.
"""
_commands = {}
descriptor = SoloCommandDescriptor.obtain_descriptor(
SoloPkg.NAME)
parser = argparse.ArgumentParser(
description=descriptor.get_description(),
formatter_class=argparse.RawDescriptionHelpFormatter,
prog="%s %s" % (sys.argv[0], SoloPkg.NAME))
subparsers = parser.add_subparsers(
title="action",
description=_("execute advanced tasks on packages"),
help=_("available commands"))
def _argparse_easygoing_valid_entropy_path(string):
if os.path.isfile(string) and os.path.exists(string):
return string
# see bug 3873, requires raw string
msg = "%s: %s" % (
_("not a valid Entropy package file"),
string)
msg = const_convert_to_rawstring(
msg, from_enctype="utf-8")
raise argparse.ArgumentTypeError(msg)
quickpkg_parser = subparsers.add_parser(
"quickpkg", help=_("generate packages from "
"the installed system"))
quickpkg_parser.add_argument(
"packages", nargs='+', metavar="<package>",
help=_("installed package name"))
quickpkg_parser.add_argument(
"--savedir", metavar="<path>",
type=self._argparse_is_valid_directory,
default=self._savedir,
help=_("destination directory "
"where to save generated packages"))
quickpkg_parser.set_defaults(func=self._quickpkg)
_commands["quickpkg"] = {}
inflate_parser = subparsers.add_parser(
"inflate", help=_("transform SPM package files "
"into Entropy ones"))
inflate_parser.add_argument(
"files", nargs='+', metavar="<file>",
type=_argparse_easygoing_valid_entropy_path,
help=_("SPM package file path"))
inflate_parser.add_argument(
"--savedir", metavar="<path>",
type=self._argparse_is_valid_directory,
default=self._savedir,
help=_("destination directory "
"where to save generated packages"))
inflate_parser.set_defaults(func=self._inflate)
_commands["inflate"] = {}
deflate_parser = subparsers.add_parser(
"deflate", help=_("transform Entropy package files "
"into SPM ones"))
deflate_parser.add_argument(
"files", nargs='+', metavar="<file>",
type=self._argparse_is_valid_entropy_package,
help=_("Entropy package file path"))
deflate_parser.add_argument(
"--savedir", metavar="<path>",
type=self._argparse_is_valid_directory,
default=self._savedir,
help=_("destination directory "
"where to save generated packages"))
deflate_parser.set_defaults(func=self._deflate)
_commands["deflate"] = {}
extract_parser = subparsers.add_parser(
"extract", help=_("extract Entropy metadata "
"from Entropy packages"))
extract_parser.add_argument(
"files", nargs='+', metavar="<file>",
type=_argparse_easygoing_valid_entropy_path,
help=_("Entropy package file path"))
extract_parser.add_argument(
"--savedir", metavar="<path>",
type=self._argparse_is_valid_directory,
default=self._savedir,
help=_("destination directory "
"where to save generated packages"))
extract_parser.set_defaults(func=self._extract)
_commands["extract"] = {}
self._commands = _commands
return parser
def parse(self):
"""
Parse command
"""
parser = self._get_parser()
try:
nsargs = parser.parse_args(self._args)
except IOError as err:
sys.stderr.write("%s\n" % (err,))
return parser.print_help, []
# Python 3.3 bug #16308
if not hasattr(nsargs, "func"):
return parser.print_help, []
self._nsargs = nsargs
return self._call_shared, [nsargs.func]
def bashcomp(self, last_arg):
"""
Overridden from SoloCommand.
"""
self._get_parser() # this will generate self._commands
return self._hierarchical_bashcomp(last_arg, [], self._commands)
def _scan_packages(self, entropy_client, inst_repo, packages):
"""
Scan the list of package names filtering out unmatched
entries.
"""
found_pkgs = []
for package in packages:
package_id, _pkg_rc = inst_repo.atomMatch(package)
if package_id == -1:
mytxt = "!!! %s: %s %s." % (
purple(_("Warning")),
teal(const_convert_to_unicode(package)),
purple(_("is not available")),
)
entropy_client.output(
"!!!", level="warning", importance=1)
entropy_client.output(
mytxt, level="warning", importance=1)
entropy_client.output(
"!!!", level="warning", importance=1)
continue
found_pkgs.append(package_id)
return found_pkgs
@sharedlock
def _quickpkg(self, entropy_client, inst_repo):
"""
Solo Pkg Quickpkg command.
"""
packages = self._nsargs.packages
savedir = self._nsargs.savedir
if not os.path.isdir(savedir) and not os.path.exists(savedir):
# this is validated by the parser
# but not in case of no --savedir provided
const_setup_directory(savedir)
if not os.path.exists(savedir):
entropy_client.output(
"%s: %s" % (
brown(_("broken directory path")),
savedir,), level="error", importance=1)
return 1
package_ids = self._scan_packages(entropy_client, inst_repo, packages)
if not package_ids:
return 1
for package_id in package_ids:
atom = inst_repo.retrieveAtom(package_id)
entropy_client.output(
"%s: %s" % (
teal(_("generating package")),
purple(atom),),
header=brown(" @@ "), back=True)
pkg_data = inst_repo.getPackageData(package_id)
file_path = entropy_client.generate_package(
pkg_data, save_directory=savedir)
if file_path is None:
entropy_client.output(
"%s: %s" % (
darkred(_("package file creation error")),
blue(atom),),
level="error", importance=1)
return 3
entropy_client.output(
"[%s] %s: %s" % (
darkgreen(atom),
teal(_("package generated")),
purple(file_path),),
header=brown(" ## "))
return 0
def _inflate(self, entropy_client):
"""
Solo Pkg Inflate command.
"""
files = self._nsargs.files
savedir = self._nsargs.savedir
if not os.path.isdir(savedir) and not os.path.exists(savedir):
# this is validated by the parser
# but not in case of no --savedir provided
const_setup_directory(savedir)
if not os.path.exists(savedir):
entropy_client.output(
"%s: %s" % (
brown(_("broken directory path")),
savedir,), level="error", importance=1)
return 1
spm = entropy_client.Spm()
for _file in files:
entropy_client.output(
"%s: %s" % (
teal(_("working on package file")),
purple(_file)),
header=darkred(" @@ "),
back=True)
file_name = os.path.basename(_file)
package_path = os.path.join(savedir, file_name)
if os.path.realpath(_file) != os.path.realpath(package_path):
# make a copy first
shutil.copy2(_file, package_path)
pkg_data = spm.extract_package_metadata(package_path)
entropy_client.output(
"%s: %s" % (
teal(_("package file extraction complete")),
purple(package_path)),
header=darkred(" @@ "),
back=True)
# append development revision number
# and create final package file name
sha1 = None
signatures = pkg_data.get('signatures')
if signatures is not None:
sha1 = signatures['sha1']
pkg_data['revision'] = etpConst['spmetprev']
download_dirpath = entropy.tools.create_package_dirpath(
pkg_data['branch'], nonfree=False, restricted=False)
download_name = entropy.dep.create_package_relative_path(
pkg_data['category'], pkg_data['name'],
pkg_data['version'], pkg_data['versiontag'],
ext=etpConst['packagesext'],
revision=pkg_data['revision'],
sha1=sha1)
pkg_data['download'] = download_dirpath + os.path.sep + \
download_name
# migrate to the proper format
final_path = os.path.join(savedir, download_name)
if package_path != final_path:
try:
os.makedirs(os.path.dirname(final_path))
except OSError as err:
if err.errno not in (errno.EISDIR, errno.EEXIST):
raise
shutil.move(package_path, final_path)
package_path = final_path
tmp_fd, tmp_path = const_mkstemp(
prefix="equo.smart.inflate.",
dir=savedir)
os.close(tmp_fd)
# attach entropy metadata to package file
repo = entropy_client.open_generic_repository(tmp_path)
repo.initializeRepository()
_package_id = repo.addPackage(
pkg_data, revision=pkg_data['revision'])
repo.commit()
repo.close()
entropy_client.output(
"%s: %s" % (
teal(_("package metadata generation complete")),
purple(package_path)),
header=darkred(" @@ "),
back=True)
entropy.tools.aggregate_entropy_metadata(
package_path, tmp_path)
os.remove(tmp_path)
entropy_client.output(
"%s: %s" % (
teal(_("package file generated at")),
purple(package_path)),
header=darkred(" @@ "))
return 0
def _deflate(self, entropy_client):
"""
Solo Pkg Deflate command.
"""
files = self._nsargs.files
savedir = self._nsargs.savedir
if not os.path.isdir(savedir) and not os.path.exists(savedir):
# this is validated by the parser
# but not in case of no --savedir provided
const_setup_directory(savedir)
if not os.path.exists(savedir):
entropy_client.output(
"%s: %s" % (
brown(_("broken directory path")),
savedir,), level="error", importance=1)
return 1
for _file in files:
entropy_client.output(
"%s: %s" % (
teal(_("working on package file")),
purple(_file)),
header=darkred(" @@ "),
back=True)
file_name = os.path.basename(_file)
package_path = os.path.join(savedir, file_name)
ext_rc = entropy.tools.remove_entropy_metadata(
_file, package_path)
if not ext_rc:
entropy_client.output(
"%s: %s" % (
teal(_("error during metadata extraction")),
purple(_file)),
header=darkred(" @@ "),
level="error", importance=1)
return 1
entropy_client.output(
"%s: %s" % (
teal(_("package file generated")),
purple(package_path)),
header=darkred(" @@ "))
return 0
def _extract(self, entropy_client):
"""
Solo Pkg Extract command.
"""
files = self._nsargs.files
savedir = self._nsargs.savedir
if not os.path.isdir(savedir) and not os.path.exists(savedir):
# this is validated by the parser
# but not in case of no --savedir provided
const_setup_directory(savedir)
if not os.path.exists(savedir):
entropy_client.output(
"%s: %s" % (
brown(_("broken directory path")),
savedir,), level="error", importance=1)
return 1
for _file in files:
entropy_client.output(
"%s: %s" % (
teal(_("working on package file")),
purple(_file)),
header=darkred(" @@ "),
back=True)
file_name = os.path.basename(_file)
package_path = os.path.join(
savedir, file_name + ".db")
ext_rc = entropy.tools.dump_entropy_metadata(
_file, package_path)
if not ext_rc:
entropy_client.output(
"%s: %s" % (
teal(_("error during metadata extraction")),
purple(_file)),
header=darkred(" @@ "),
level="error", importance=1)
return 1
entropy_client.output(
"%s: %s" % (
teal(_("metadata file generated")),
purple(package_path)),
header=darkred(" @@ "))
return 0
SoloCommandDescriptor.register(
SoloCommandDescriptor(
SoloPkg,
SoloPkg.NAME,
_("execute advanced tasks on packages"))
)