You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

119 lines
3.5 KiB

# -*- coding: utf-8 -*-
from collections import OrderedDict
from os.path import exists
from subprocess import check_call
from doodbalib import logger
class Installer(object):
"""Base class to install packages with some package system."""
_cleanup_commands = []
_install_command = None
_remove_command = None
def __init__(self, file_path):
self.file_path = file_path
self._requirements = self.requirements()
def _run_command(self, command):
logger.info("Executing: %s", command)
return check_call(command, shell=isinstance(command, str))
def cleanup(self):
"""Remove cache and other garbage produced by the installer engine."""
for command in self._cleanup_commands:
self._run_command(command)
def install(self):
"""Install the requirements from the given file."""
if self._requirements:
return not self._run_command(self._install_command + self._requirements)
else:
logger.info("No installable requirements found in %s", self.file_path)
return False
def remove(self):
"""Uninstall the requirements from the given file."""
if not self._remove_command:
return
if self._requirements:
self._run_command(self._remove_command + self._requirements)
else:
logger.info("No removable requirements found in %s", self.file_path)
def requirements(self):
"""Get a list of requirements from the given file."""
requirements = []
try:
with open(self.file_path, "r") as fh:
for line in fh:
line = line.strip()
if not line or line.startswith("#"):
continue
requirements += line.split()
except IOError:
# No requirements file
pass
return requirements
class AptInstaller(Installer):
_cleanup_commands = [["apt-get", "-y", "autoremove"], "rm -Rf /var/lib/apt/lists/*"]
_install_command = [
"apt-get",
"-o",
"Dpkg::Options::=--force-confdef",
"-o",
"Dpkg::Options::=--force-confold",
"-y",
"--no-install-recommends",
"install",
]
_remove_command = ["apt-get", "purge", "-y"]
def _dirty(self):
return exists("/var/lib/apt/lists/lock")
def cleanup(self):
if self._dirty():
super(AptInstaller, self).cleanup()
def install(self):
if not self._dirty() and self._requirements:
self._run_command(["apt-get", "update"])
return super(AptInstaller, self).install()
class GemInstaller(Installer):
_cleanup_commands = ["rm -Rf ~/.gem /var/lib/gems/*/cache/"]
_install_command = ["gem", "install", "--no-document", "--no-update-sources"]
class NpmInstaller(Installer):
_cleanup_commands = ["rm -Rf ~/.npm /tmp/*"]
_install_command = ["npm", "install", "-g"]
class PipInstaller(Installer):
_install_command = ["pip", "install", "--no-cache-dir", "-r"]
def requirements(self):
"""Pip will use its ``--requirements`` feature."""
return [self.file_path] if exists(self.file_path) else []
INSTALLERS = OrderedDict(
[
("apt", AptInstaller),
("gem", GemInstaller),
("npm", NpmInstaller),
("pip", PipInstaller),
]
)
def install(installer, file_path):
"""Perform a given type of installation from a given file."""
return INSTALLERS[installer](file_path).install()