From 8104e8fdb0821331972d4016189e71ea1606a6ef Mon Sep 17 00:00:00 2001 From: davidovski Date: Sat, 20 Nov 2021 17:24:42 +0000 Subject: added dependency resolving --- src/colors.py | 2 + src/options.py | 5 +++ src/util.py | 20 ++++++++- src/verbs/install.py | 115 ++++++++++++++++++++++++++++++++++++++------------- src/verbs/sync.py | 2 + 5 files changed, 114 insertions(+), 30 deletions(-) diff --git a/src/colors.py b/src/colors.py index c05d49b..df22d30 100644 --- a/src/colors.py +++ b/src/colors.py @@ -29,3 +29,5 @@ BG_MAGENTA = esc(45) BG_CYAN = esc(46) BG_WHITE = esc(47) BG_DEFAULT = esc(49) + +CLEAR_LINE = "\033[K" diff --git a/src/options.py b/src/options.py index 910b8a3..499f9e7 100644 --- a/src/options.py +++ b/src/options.py @@ -27,6 +27,11 @@ options = { "flag" : True, "desc" : "skip any checksum or signature verification" }, + "n": { + "name" : "no-deps", + "flag" : True, + "desc" : "do not resolve dependencies" + }, "v": { "name" : "verbose", "flag" : True, diff --git a/src/util.py b/src/util.py index 16df7bc..2411f1d 100644 --- a/src/util.py +++ b/src/util.py @@ -3,6 +3,7 @@ import requests import colors import time import os +import hashlib DEFAULT_BAR_COLOR = colors.BLACK + colors.BG_CYAN DEFAULT_BAR_COLOR_RESET = colors.BG_BLACK + colors.CYAN @@ -25,8 +26,8 @@ def loading_bar(completed, total, text, print(color + info, end="\r") - - +def print_reset(text): + print(colors.RESET + text) def curl(url): r = requests.get(url) @@ -36,6 +37,21 @@ def mkdir(path): if not os.path.exists(path): os.makedirs(path) +def md5sum(data): + return hashlib.md5(data) + +def ask_confirmation(text, default=True, no_confirm=False): + yes = "Y" if default else "y" + no = "n" if default else "N" + + if no_confirm: + reponse = "y" if default else "n" + print(f"{text} [{yes},{no}] {colors.RESET} {reponse}") + else: + reponse = input(f"{text} [{yes},{no}] " + colors.RESET) + + return reponse.lower() == "y" or len(reponse) == 0 + if __name__ == "__main__": for i in range(1000): loading_bar(i, 1000, "it is loading...") diff --git a/src/verbs/install.py b/src/verbs/install.py index 11a2183..0847b1d 100644 --- a/src/verbs/install.py +++ b/src/verbs/install.py @@ -4,11 +4,8 @@ import util import colors import time -def find_package(query, repos, packages_dir): - sources = [] - checksum = None - requested_repo = None +def find_package(query, repos, packages_dir, sources): for repo in repos: repo_dir = os.path.join(packages_dir, repo) files = os.listdir(repo_dir) @@ -17,13 +14,21 @@ def find_package(query, repos, packages_dir): requested_repo = repo with open(os.path.join(repo_dir, query)) as file: checksum = file.readline().strip().split("=")[-1] - sources = file.readline().strip().split("=")[-1].split() - return checksum, sources, requested_repo + listed_sources = file.readline().strip().split("=")[-1].split() + found_sources = { + source: util.add_path(url, repo) + for source, url in sources.items() + if source in listed_sources + } + return checksum, found_sources, requested_repo return None, [], None def retrieve_package_info(sources, checksum, package_name, verbose=False, skip_verification=False): + + # TODO we may potentially do this a few times while resolving deps, might want to cache things here + # TODO actually use the ping times we made earlier to decide which source to pick for source,url in sources.items(): package_info_url = util.add_path(url, package_name + ".xipkg.info") status, response = util.curl(package_info_url) @@ -43,14 +48,18 @@ def retrieve_package_info(sources, checksum, package_name, def retrieve_package(sources, checksum, package_name, verbose=False, skip_verification=False): + + # TODO actually use the ping times we made earlier to decide which source to pick + # TODO actually save tar file, and add loading bar for source,url in sources.items(): - package_info_url = util.add_path(url, package_name + ".xipkg.info") + package_info_url = util.add_path(url, package_name + ".xipkg") status, response = util.curl(package_info_url) if status == 200: - info = parse_package_info(response) - if info["CHECKSUM"] == checksum or skip_verification: - return info + downloaded_checksum = util.md5sum(response) + print(downloaded_checksum, "compared to requested", checksum) + if downloaded_checksum == checksum or skip_verification: + return reponse else: if verbose: print(colors.RED @@ -67,11 +76,63 @@ def parse_package_info(packageinfo): split = line.split("=") if len(split) > 1: info[split[0]] = "=".join(split[1:]) + return info -def resolve_dependencies(package_info): - getpkgs = lambda deps: re.findall("[\(\s](\w)[\)\s]") - package_info[""] +def resolve_dependencies(package_info, config): + getpkgs = lambda deps: re.findall("\w*", deps) + deps = getpkgs(package_info["DEPS"]) + + deps = [ + dep for dep in deps if len(dep) > 0 + ] + + return deps + +def find_all_dependencies(package_names, options, config): + # this is all assuming that the order of deps installed doesn't matter + to_check = [p for p in package_names] + all_deps = [] + + while len(to_check) > 0: + util.loading_bar(len(all_deps), len(all_deps) + len(to_check), "Resolving dependencies...") + dep = to_check.pop() + + dep_checksum, dep_sources, dep_repo = find_package(dep, config["repos"], config["dir"]["packages"], config["sources"]) + if dep_checksum is not None: + info = retrieve_package_info( + dep_sources, dep_checksum, dep, + verbose=options["v"], skip_verification=options["u"] + ) + + if len(info) > 0: + all_deps.append(dep) + deps = resolve_dependencies(info, config) + for dep in deps: + if not dep in all_deps: + + if is_installed(dep, config): + print(colors.YELLOW + f"Package {query} has already been installed") + else: + to_check.append(dep) + else: + if options["v"]: + util.print_reset(colors.CLEAR_LINE + colors.RED + f"Failed to retrieve info for {query}") + else: + util.print_reset(colors.CLEAR_LINE + colors.RED + f"Failed to find package {dep}") + + if len(all_deps) > 0: + util.loading_bar(len(all_deps), len(all_deps) + len(to_check), "Resolved dependencies") + print(colors.RESET) + + # assuming that the latter packages are core dependencies + # we can reverse the array to reflect the more important packages to install + all_deps.reverse() + return all_deps + +def is_installed(package_name, config): + # TODO actually find out if its installed + return False def install(args, options, config): sources = config["sources"] @@ -81,23 +142,21 @@ def install(args, options, config): unsafe = options["u"] packages_dir = config["dir"]["packages"] - for query in args: - # TODO FIRST CHECK IF ALREADY INSTALLED - checksum, listed_sources, repo = find_package(query, repos, packages_dir) + to_install = args if options["n"] else find_all_dependencies(args, options, config) - if checksum is not None: - info = retrieve_package_info( - { - source: util.add_path(url, repo) - for source, url in sources.items() - if source in listed_sources - }, checksum, query, - verbose=v, skip_verification=unsafe - ) + if len(to_install) > 0: + print(colors.BLUE + "The following packages will be installed:") + print(end="\t") + for d in to_install: + print(colors.BLUE if d in args else colors.LIGHT_BLUE, d, end="") + print() - print(info) + if util.ask_confirmation(colors.BLUE + "Continue?", no_confirm=options["y"]): + print("installed") else: - print(colors.RED + "Package not found") - print(colors.RESET, end="") + print(colors.RED + "Action cancelled by user") + else: + print(colors.LIGHT_RED + "Nothing to do") + diff --git a/src/verbs/sync.py b/src/verbs/sync.py index bd828ef..c534f4d 100644 --- a/src/verbs/sync.py +++ b/src/verbs/sync.py @@ -24,6 +24,7 @@ def sync_packages(repo, sources, verbose=False): listed = list_packages(url + repo if url[-1] == "/" else f"/{repo}") if len(listed) == 0 and verbose: print(colors.BG_RED + f"No packages found in {source}/{repo}" + colors.RESET) + total += len(listed) for p in listed: if not p in packages: @@ -67,6 +68,7 @@ def save_package_list(validated, location): with open(package_file, "w") as file: file.write("checksum=" + info["checksum"] + "\n") file.write("sources=" + " ".join([source for source in info["sources"]])) + file.write("deps=" + " ".join([source for source in info["sources"]])) ###### !!! ####### -- cgit v1.2.1