From 1f261e7a14fd2618012246038f100afb957f667b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Mon, 24 Jan 2022 19:47:38 +0100 Subject: Initial commit. --- main.py | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100755 main.py (limited to 'main.py') diff --git a/main.py b/main.py new file mode 100755 index 0000000..86ee9c8 --- /dev/null +++ b/main.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 + +import re +import os +import sys +import subprocess +import yaml +import logging +import logging.config + + +def get_config_files(): + subpath = ['aur-runner', 'config.yaml'] + conf_files = [] + # TODO better name for this, preferably matching the program name + if d := os.getenv('OVERRIDE_DIR'): + conf_files.append(os.path.join(d, *subpath)) + if d := os.getenv('XDG_CONFIG_HOME'): + conf_files.append(os.path.join(d, *subpath)) + if d := os.getenv('HOME'): + conf_files.append(os.path.join(d, '.config', *subpath)) + if d := os.getenv('XDG_CONFIG_DIRS'): + for part in d.split(':'): + conf_files.append(os.path.join(part, *subpath)) + conf_files.append(os.path.join('/etc/xdg', *subpath)) + return conf_files + + +default_config_yaml = """ +package-list: aur-packages.yaml +source-file: '*virtual*' +path: + - /usr/local/sbin + - /usr/local/bin + - /usr/bin + - /usr/bin/site_perl + - /usr/bin/vendor_perl + - /usr/bin/core_perl +cache-dir: cache +pkgdest-dir: dest +logging: + version: 1 + formatters: + detailed: + class: logging.Formatter + format: '[%(asctime)s] %(name)-15s %(levelname)-8s %(message)s' + datefmt: '%Y-%m-%dT%H:%M:%S' + handlers: + console: + class: logging.StreamHandler + formatter: detailed + level: DEBUG + root: + level: DEBUG + handlers: + - console +""" + + +default_config = yaml.safe_load(default_config_yaml) + + +for conf_file in get_config_files(): + try: + with open(conf_file) as f: + user_config = yaml.safe_load(f) + user_config['source-file'] = conf_file + break + except FileNotFoundError: + pass +else: + # No config file found + user_config = {} + + +def get_conf(key, default=None): + if default: + return user_config.get(key, default_config.get(key, default)) + else: + return user_config.get(key, default_config[key]) + + +logging.config.dictConfig(get_conf('logging')) +logger = logging.getLogger(__name__) + + +auracle_args = ['--color=never'] +pacman_args = ['--noconfirm', '--asdeps', '--noprogressbar', '--needed'] +makepkg_args = ['--nocolor'] + + +def gather_packages(pkgs): + """ + Figure out which packages to install, and in which order + + Takes a list of package names, and returns to lists, one of + packages which are alreaddy available in the repos, and a list of + packages which needs to be fetched from the aur. Both are in + dependency order, and all repo packages should be assumed to be + required for the aur packages to be build/installed. + """ + repo_pkgs = [] + aur_pkgs = [] + cmd = subprocess.run(['auracle', *auracle_args, 'buildorder', *pkgs], capture_output=True, text=True) + + for line in cmd.stdout.split('\n'): + if not line: continue + m = re.match(r'(SATISFIED|TARGET)?(AUR|REPOS|UNKNOWN) ([^ ]*) ?(.*)', line) + status = m[1] + source = m[2] + package = m[3] + if status == 'SATISFIED': + logger.debug(f'Package already installed: {package}') + continue + if source == 'REPOS': + logger.debug(f'Would install from repo: {package}') + repo_pkgs.append(package) + elif source == 'AUR': + logger.debug(f'Would install from aur: {package}') + aur_pkgs.append(package) + elif source == 'UNKNOWN': + # auracle buildorder guile-chickadee fails with + # 'UNKNOWN guile-opengl guile-chickadee' + # Since guile-opengl is provided by guile-opengl-git, and + # auracle doesn't find that. The information is however there + # on the aur: https://aur.archlinux.org/packages/guile-chickadee/ + logger.fatal('Something went wrong', line, m[4]) + sys.exit(1) + + return repo_pkgs, aur_pkgs + +################################################## + + +def main(): + with open(get_conf('package-list')) as f: + pkgs = yaml.safe_load(f) + + if type(pkgs) != list: + logger.fatal('Package list is not a list') + os.exit(1) + + # os.path.join discards earlier components whenever it finds an absolute path + cachedir = os.path.join(os.getcwd(), get_conf('cache-dir')) + pkgdest = os.path.join(os.getcwd(), get_conf('pkgdest-dir')) + + for dir in [cachedir, pkgdest]: + try: + os.mkdir(dir) + except FileExistsError: + pass + + path = get_conf('path') + + repo_pkgs, aur_pkgs = gather_packages(pkgs) + + def log_cmd(cmd): + if cmd.returncode == 0: + logger.info('%s finished without errors', cmd.args) + else: + logger.warning('%s exited with status code %s', cmd.args, cmd.retruncode) + + if repo_pkgs: + logger.info(f'Installing from the repos: {repo_pkgs}') + log_cmd(subprocess.run(['sudo', 'pacman', *pacman_args, '-S', *repo_pkgs])) + + for package in aur_pkgs: + cmd = subprocess.run(['auracle', *auracle_args, '--chdir', cachedir, 'clone', package], + capture_output=True, text=True) + cwd = re.match('[^:]*: (.*)', cmd.stdout)[1] + env = { 'PKGDEST': pkgdest, + 'PATH': ':'.join(path), + } + log_cmd(subprocess.run(['makepkg', *makepkg_args, '--install', *pacman_args], env=env, cwd=cwd)) + +if __name__ == '__main__': + main() -- cgit v1.2.3