From 3c45a31b70afe5d3eab57e2831ac5abde5d539d6 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 7 Jan 2023 11:07:18 -0600 Subject: [PATCH] alright now we're gettin crazy --- config.ini | 13 +++++++ install | 5 +-- installer/__init__.py | 89 +++++++++++++------------------------------ installer/host.py | 29 ++++++++++++++ installer/log.py | 19 +++++++++ installer/options.py | 67 ++++++++++++++++++++++++++++++++ installer/pylintrc | 4 ++ installer/targets.py | 3 ++ 8 files changed, 164 insertions(+), 65 deletions(-) create mode 100644 config.ini create mode 100644 installer/host.py create mode 100644 installer/log.py create mode 100644 installer/options.py create mode 100644 installer/pylintrc create mode 100644 installer/targets.py diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..fecf2b8 --- /dev/null +++ b/config.ini @@ -0,0 +1,13 @@ +[home] +dirs= + .vim/ftplugin + .vim/pack + +files= + .bash_profile + .bashrc + .screenrc + .tmux.conf + .vimrc + .config/htop/htoprc + .config/lazydocker/config.yml diff --git a/install b/install index c0f766c..afbf5ed 100755 --- a/install +++ b/install @@ -2,6 +2,5 @@ """ installs preferences """ -from installer import Installer -installer = Installer.from_cli_args() -installer.run() +from installer import install +install() diff --git a/installer/__init__.py b/installer/__init__.py index 97d946e..ab12045 100644 --- a/installer/__init__.py +++ b/installer/__init__.py @@ -4,51 +4,32 @@ just exists to define our installer import argparse import json -import logging import os import pathlib import platform import subprocess import shutil - from functools import cached_property +from installer import host +from installer.options import Options +from installer import log + +def install(): + """ + runs our install process based on cli arguments + """ + installer = Installer() + installer.run() + class Installer: """ manages the installation of preferences files """ - def __init__(self, quiet=False, verbose=False): - log_level = logging.INFO - if verbose: - log_level = logging.DEBUG - if quiet: - log_level = logging.ERROR - logging.basicConfig(level=log_level, format='') - self.log = logging.getLogger('install') + def __init__(self): + self.options = Options.from_cli_args() self.config_path = "include.json" - @classmethod - def from_cli_args(cls): - """ - builds an installer from CLI arguments - """ - args = cls.parse_cli() - installer = cls(quiet=args.quiet, verbose=args.verbose) - return installer - - @classmethod - def parse_cli(cls): - """ - parses our cli arguments - """ - parser = argparse.ArgumentParser( - prog = 'install', - description = 'installs my preferences', - epilog = 'this should work on a bunch of different environments') - parser.add_argument('-v', '--verbose', action='store_true') - parser.add_argument('-q', '--quiet', action='store_true') - return parser.parse_args() - def run(self): """ runs the install process @@ -61,34 +42,34 @@ class Installer: """ installs a given file """ - self.log.debug("install: %s", name) + log.debug("install: %s", name) source_path = self.prefs_dir / name - self.log.debug(" source: %s", source_path) + log.debug(" source: %s", source_path) if not source_path.exists(): - self.log.warning("home file source path %s does not exist", source_path) + log.warning("home file source path %s does not exist", source_path) return if not source_path.is_file(): - self.log.warning("home file source path %s is not a file", source_path) + log.warning("home file source path %s is not a file", source_path) return - if self.is_linux: + if host.is_linux: target_path = pathlib.Path.home() / name - self.log.debug(" target: %s", target_path) + log.debug(" target: %s", target_path) if target_path.exists(): - self.log.debug(" target path exists, will remove") + log.debug(" target path exists, will remove") target_path.unlink() - self.log.debug(" link: %s -> %s", target_path, source_path) + log.debug(" link: %s -> %s", target_path, source_path) target_path.symlink_to(source_path) - if self.is_wsl: + if host.is_wsl: target_path = self.windows_home_dir / pathlib.PureWindowsPath(name) - self.log.debug(" target: %s", target_path) + log.debug(" target: %s", target_path) if target_path.exists(): - self.log.debug(" target path exists, will remove") + log.debug(" target path exists, will remove") target_path.unlink() - self.log.debug(" copy: %s -> %s", source_path, target_path) + log.debug(" copy: %s -> %s", source_path, target_path) shutil.copy(source_path, target_path) @cached_property @@ -97,7 +78,7 @@ class Installer: loads the json configuration """ with open(self.config_path, 'r', encoding='utf-8') as config_fp: - self.log.debug("loading config from path %s", self.config_path) + log.debug("loading config from path %s", self.config_path) return json.load(config_fp) @property @@ -108,22 +89,6 @@ class Installer: here = pathlib.Path(os.path.realpath(__file__)) return here.parent.parent - @classmethod - @cached_property - def is_linux(cls): - """ - true if we're on linux (including WSL), false otherwise - """ - return platform.system() == 'Linux' - - @classmethod - @cached_property - def is_wsl(cls): - """ - true if we're running Linux on WSL - """ - return 'WSL2' in platform.platform() - @cached_property def windows_home_dir(self): """ @@ -131,7 +96,7 @@ class Installer: returns it as a Posix path representing its mount point from the perspective of WSL. """ - if not self.is_wsl: + if not host.is_wsl: raise Exception("cannot get windows home dir from anything other than wsl") res = subprocess.run(['wslvar', 'USERPROFILE'], check=False, capture_output=True) diff --git a/installer/host.py b/installer/host.py new file mode 100644 index 0000000..5db43e6 --- /dev/null +++ b/installer/host.py @@ -0,0 +1,29 @@ +""" +host module represents our host: the machine on which the installer script is +running. On WSL, that means we're on Linux +""" + +import sys +import platform +from functools import cached_property + +class _Host: + """ + hacking the python module system a little to make the module look like a + singleton object so that it can have properties + """ + @cached_property + def is_wsl(self): + """ + true if we're running Linux on WSL + """ + return 'WSL2' in platform.platform() + + @cached_property + def is_linux(self): + """ + true if we're on linux (including WSL), false otherwise + """ + return platform.system() == 'Linux' + +sys.modules[__name__] = _Host() diff --git a/installer/log.py b/installer/log.py new file mode 100644 index 0000000..06354d5 --- /dev/null +++ b/installer/log.py @@ -0,0 +1,19 @@ +""" +a logging object +""" + +import logging +import sys + +class _Log: + def __init__(self): + logging.basicConfig(level=logging.INFO, format='') + self._target = logging.getLogger() + + def __getattr__(self, name): + return getattr(self._target, name) + + def __dir__(self): + return dir(self._target) + +sys.modules[__name__] = _Log() diff --git a/installer/options.py b/installer/options.py new file mode 100644 index 0000000..8ab1a19 --- /dev/null +++ b/installer/options.py @@ -0,0 +1,67 @@ +""" +cli options +""" + +import argparse + +from installer import log + +class Options: + """ + turns the cli arguments into an object with nice fields and the like + """ + @classmethod + def from_cli_args(cls): + """ + creates an options object by parsing the command-line + """ + parser = argparse.ArgumentParser( + prog = 'install', + description = """ + installs preferences files. This is designed to work on a bunch + of different systems, and can manage Windows preferences from + WSL. + """) + parser.add_argument('-v', '--verbose', action='store_true') + parser.add_argument('-q', '--quiet', action='store_true') + parser.add_argument('-c', '--config', default='config.ini', + help="path to config file", + metavar='config.ini', + type=argparse.FileType('r', encoding='utf-8')) + options = cls() + parser.parse_args(namespace=options) + + if options.quiet: + log.setLevel(50) + + if options.verbose: + log.setLevel(10) + + return options + + @property + def quiet(self): + # pylint: disable=missing-function-docstring + return self._quiet + + @quiet.setter + def quiet(self, val): + self._quiet = val + + @property + def verbose(self): + # pylint: disable=missing-function-docstring + return self._verbose and not self._quiet + + @verbose.setter + def verbose(self, val): + self._verbose = val + + @property + def config(self): + # pylint: disable=missing-function-docstring + return self._config + + @config.setter + def config(self, val): + self._config = val diff --git a/installer/pylintrc b/installer/pylintrc new file mode 100644 index 0000000..4c97b91 --- /dev/null +++ b/installer/pylintrc @@ -0,0 +1,4 @@ +[MAIN] + +disable= + no-member, diff --git a/installer/targets.py b/installer/targets.py new file mode 100644 index 0000000..a5e6da1 --- /dev/null +++ b/installer/targets.py @@ -0,0 +1,3 @@ +""" +defines target classes: the places where things should be written +"""