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
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()
|