import os import argparse from userconfig.cfgfile import Conf from userconfig import Userconfig import sys import subprocess class Debug: _verbose = 0 _COLOR_RED = '\033[91m' _COLOR_BLUE = '\33[34m' _COLOR_GREEN = '\33[32m' _COLOR_YELLOW = '\033[93m' _COLOR_END = '\33[0m' _FORMAT = {'STANDARD': 'INFO: ', 'ERROR': f'{_COLOR_RED}ERROR:{_COLOR_END} ', 'NOTICE': f'{_COLOR_BLUE}NOTICE:{_COLOR_END} ', 'WARNING': f'{_COLOR_YELLOW}WARNING:{_COLOR_END} ', 'SUCCESS': f'{_COLOR_GREEN}SUCCESS:{_COLOR_END} ' } def __init__(self, verbose=0): self.set_verbose(verbose) def set_verbose(self, verbose): self._verbose = verbose def add_verbose(self): self._verbose += 1 def get_verbose(self): return self._verbose def stdout(self, out, verbose_level=0, category='STANDARD'): spaces = '' if verbose_level > 1: spaces = ' '*(verbose_level-1) if self._verbose >= verbose_level: if category in self._FORMAT: print(f'{spaces}{self._FORMAT[category]}{out}') else: print(f'{spaces}[category {category} unknown]:{out}') def stderr(self, out, verbose_level=0, category='STANDARD'): if self._verbose >= verbose_level: if category in self._FORMAT: print(f'{self._FORMAT[category]}{out}', file=sys.stderr) else: print(f'[category {category} unknown]:{out}', file=sys.stderr) def red(self, out): return f'{self._COLOR_RED}{out}{self._COLOR_END}' def green(self, out): return f'{self._COLOR_GREEN}{out}{self._COLOR_END}' def blue(self, out): return f'{self._COLOR_BLUE}{out}{self._COLOR_END}' def yellow(self, out): return f'{self._COLOR_YELLOW}{out}{self._COLOR_END}' def main(): parser = argparse.ArgumentParser(prog='userconfig', description='Manages configuration files, usually in the user home directory') parser.add_argument('-v', help='Verbosity level (multiple v for higher level)', dest='verbose', action='count', default=0) parser.add_argument('-f', '--file', help='userconfig2.cfg config file', dest='file', action='store') cmdline = parser.parse_args() debug = Debug() debug.set_verbose(cmdline.verbose) cfg = Conf(filename=cmdline.file, debug=debug) uc = Userconfig(cfg) cfg.debug.stdout(f'Verbose level: {cfg.debug.get_verbose()}', 1) configdir = cfg.get('configdir') # configdir is the root of the userconfig files # Directory structure: # configdir/ # |-- userconfig2.conf # |-- package1/ # |- package.conf # |- 001_Arch_Linux/ # |- file1 # |- file2 # |- 002_Host_glitters/ # |- file1 # |- 003_all/ # |- file2 # # Terminology: # directories below configdir are packages, packages contain directories categorizing for which host/arch they # are fitted, below that are files which are installed at the destination # # directory names in packages: # [Number]_[category]_[value] # Number: can have leading zeros for better sorting in directory structure, will be used for sorting and priority # category: which kind of match we are looking for. currently: Arch for `uname -s`, Host for `hostname -s` # value: optional, if category requires an input value, for example: Arch_Linux # can be empty for example if category is all (no match needed, we want this always to be applied) cfg.debug.stdout(f'configdir: {configdir}', 1) for package in os.scandir(configdir): # Skip on non-production files if not package.is_dir(): cfg.debug.stdout(f'{package.path} is not a directory, skipping', 2, 'WARNING') continue if package.name in ['.svn', '.git']: cfg.debug.stdout(f'{package.path} is a svn or git data directory, skipping', 2, 'WARNING') continue if os.path.isfile(f'{package.path}/.ignore'): cfg.debug.stdout(f'{package.path} contains .ignore, skipping', 2, 'WARNING') continue # Start processing cfg.debug.stdout(f'Start Package {cfg.debug.green(package.path)}', 1, 'NOTICE') # Get all category directories for package (category_dirs, dir_config) = uc.process_package_dir(package.path) if not category_dirs: cfg.debug.stdout(f'Could not get category_dirs for package {package.name}, skipping package.', 0, 'ERROR') continue if not dir_config: cfg.debug.stdout(f'Could not get dir_config for package {package.name}, skipping package.', 0, 'ERROR') continue cfg.debug.stdout(f'Got categories: {category_dirs}', 2) host_category_dirs = uc.filter_categories(category_dirs) cfg.debug.stdout('Host uses categories: %s' % ", ".join([f'{v[1]}_{v[2]}' for v in host_category_dirs]), 2) file_list = dict() for c in host_category_dirs: file_list = uc.process_category_dir(c, file_list) cfg.debug.stdout(f'Found files: {file_list}', 2) if len(file_list) and os.access(f'{package.path}/install.sh', os.X_OK): cfg.debug.stdout(f'Execute {package.path}/install.sh', 2) subprocess.call([f'{package.path}/install.sh']) for file in file_list: dest = f'{dir_config.get(section="Main", option="dest")}/{file}' cfg.debug.stdout('Generating %s from:\n %s' % (dest, "\n ".join(file_list[file])), 1) try: comment_string = dir_config.get(section="Main", option="commentstring") except ValueError: cfg.debug.stdout(f'commentstring does not exist in config file {dir_config._cfgfiles}', 0, 'ERROR') sys.exit(1) # Make sure all directories for destination file exist if uc.create_destination_directories(dir_config.get(section="Main", option="dest")): cfg.debug.stdout(f'Created target directories {cfg.debug.green(dir_config.get(section="Main", option="dest"))}', 0, 'SUCCESS') else: cfg.debug.stdout(f'All target directories exist', 2) temp_filename = uc.build_file(file_list[file], dest, comment_string) if uc.diff_and_copy_file(temp_filename, dest, comment_string): cfg.debug.stdout(f'Copy {temp_filename} -> {cfg.debug.green(dest)} (changed)\n', 0, 'SUCCESS') else: cfg.debug.stdout(f'Generated file and destination are the same.\n', 1) cfg.debug.stdout(f'End Package {cfg.debug.green(package.path)}\n\n', 1, 'NOTICE') if __name__ == '__main__': main()