diff --git a/.gitignore b/.gitignore index cf40d9c..e4dc7bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ **/.pytest_cache/ **/.DS_Store .idea/ -**/__pycache__ \ No newline at end of file +**/__pycache__ +/.vscode \ No newline at end of file diff --git a/README.md b/README.md index 4e16d30..5fe84d5 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ tarstall is a lightweight package manager for Linux (and similar) systems that m * Ability to keep itself up to date ## Getting Started -Simply download this repo! Make sure you have Python 3 installed and you should be set! You can invoke first time setup by navigating to the directory where tarstall is stored, then going to the ```tarstall_execs``` directory and running ```python3 tarstall -f```. Afterwards, feel free to use ```tarstall -h``` to view a list of commands! +On a system with wget, run the following command: ```wget https://raw.githubusercontent.com/hammy3502/tarstall/master/install_tarstall && python3 install_tarstall```. You may need to enter your root password to install some of the dependencies for tarstall. + +NOTE: Dependencies should be installed manually if you're on a system that doesn't use apt! You'll need to get ahold of `git` and `python3-tk` (tkinter for Python 3). From there, you can run the command above, and everything else will be taken care of for you! ## More Info Tons of more information is provided in the Wiki. The [Basic Usage section of the wiki](https://github.com/hammy3502/tarstall/wiki/Basic-Usage) provides examples, usage, etc. for most commands while the [Features section of the wiki](https://github.com/hammy3502/tarstall/wiki/Features) details all of the features of tarstall. diff --git a/config.py b/config.py index ac47f67..db2a26d 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,5 @@ """tarstall: A package manager for managing archives - Copyright (C) 2019 hammy3502 + Copyright (C) 2020 hammy3502 tarstall is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,9 +22,9 @@ ###VERSIONS### -version = "1.4.2" -prog_internal_version = 73 -file_version = 11 +version = "1.5.0" +prog_internal_version = 82 +file_version = 13 ############# diff --git a/generic.py b/generic.py index 7e24f17..4f14806 100644 --- a/generic.py +++ b/generic.py @@ -1,5 +1,5 @@ """tarstall: A package manager for managing archives - Copyright (C) 2019 hammy3502 + Copyright (C) 2020 hammy3502 tarstall is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/install_tarstall b/install_tarstall new file mode 100755 index 0000000..2832268 --- /dev/null +++ b/install_tarstall @@ -0,0 +1,180 @@ +#!/usr/bin/python3 + +import sys +import os +from subprocess import call +from shutil import which, rmtree + +columns = int(os.popen('stty size', 'r').read().split()[1]) + +def status(msg): + """Print Status Message. + + Prints "major" messages for the end user. + + Args: + msg (str): Message to print. + + """ + print("#"*int(columns*0.75)) + print(msg) + print("#"*int(columns*0.75)) + + +def run(cmds): + """Run Command. + + A tiny function that simply uses 'call', and returns whether or not the call exited successfully. + + Args: + cmds (str[]): The command list to pass to call() + + Returns: + bool: Whether or not the call() succeeded. + + """ + err = call(cmds) + return err == 0 + + +def install_package(pkg): + """Installs a Package. + + Installs the specified package using the distro's package manager. + Currently supports the following package managers: + * apt + * apt-get + * dnf + * pacman + + Dictionary format: + If a given package has a different name depending on the package manager (ie. Debian systems having 'python3-tk' + while Arch systems having 'tk'), a dict can be used to specify that. + Example: + {"apt": "python3-tk", "dnf": "python3-tkinter", "pacman": "tk"} + The above example will install the package "python3-tk" if the user is using apt OR apt-get, + "python3-tkinter" if the user is using dnf, or "tk" if the user is using pacman. + + Args: + pkg (str/dict): If a string, the package to install. See above for dictionary format. + + """ + if type(pkg) is not str: + if which("apt") is not None or which("apt-get") is not None: + pkg = pkg["apt"] + elif which("dnf") is not None: + pkg = pkg["dnf"] + elif which("pacman") is not None: + pkg = pkg["pacman"] + else: + status("This script only supports automatic program installtion through apt(-get), dnf, and pip; please install {} manually!".format(pkg)) + sys.exit(1) + if which("apt") is not None: + passed = run(["sudo", "apt", "install", pkg, "-y"]) + elif which("apt-get") is not None: + passed = run(["sudo", "apt-get", "install", pkg, "-y"]) + elif which("dnf") is not None: + passed = run(["sudo", "dnf", "install", pkg, "-y"]) + elif which("pacman") is not None: + passed = run(["sudo", "pacman", "-S", pkg, "--noconfirm"]) + else: + status("This script only supports automatic program installtion through apt(-get), dnf, and pip; please install {} manually!".format(pkg)) + sys.exit(1) + if not passed: + status("Error installing package {}! Please install it manually, and/or read the error above for information how to resolve this error!".format(pkg)) + sys.exit(1) + + +def setup(): + # Checks + status("Checking for anything missing for tarstall setup") + supported_package_managers = ["apt", "apt-get", "dnf", "pacman"] + supported_shells = ["bash", "zsh"] + if which("sudo") is None: + status("Please install 'sudo'!") + sys.exit(1) + supported = False + for manager in supported_package_managers: + if which(manager) is not None: + supported = True + break + if not supported: + status("You currently don't have a supported package manager! Currently supported package managers are: " + ", ".join(supported_package_managers)) + sys.exit(1) + shell = os.environ["SHELL"] + shell_supported = False + for sshell in supported_shells: + if sshell in shell: + shell_supported = True + break + if not shell_supported: + print(shell) + msg = "WARNING: YOUR SHELL IS NOT SUPPORTED!\n\nYOU WILL HAVE TO MANUALLY ADD ~/.tarstall/tarstall_execs TO YOUR PATH, " \ + "AND ANY PATH/BINLINK FUNCTIONS WILL NOT WORK!\nWOULD YOU LIKE TO PROCEED WITH INSTALLATION ANYWAYS?" + status(msg) + should_proceed = input("Type 'YES' to continue; anything else to exit... ") + if should_proceed.lower() != "yes": + status("Installation cancelled!") + sys.exit(1) + print("All of the checks passed!\n\n\n") + + # User information + status("Welcome!\nThis installer is going to install tarstall! You'll need to enter your sudo password at some points. " + + "This will use your distro's package manager along with pip to install the dependencies required for tarstall!") + cancel = input("When you're ready to start installation, press ENTER! If you would like to cancel, type 'c', then press ENTER!") + if cancel.lower() == 'c': + status("Cancelling tarstall setup...") + sys.exit() + + # Install git and wget + status("Installing git and wget") + install_package("git") + install_package("wget") + + # Clone repository + status("Getting a copy of tarstall") + try: + rmtree("/tmp/tarstall-setup") + except FileNotFoundError: + pass + os.mkdir("/tmp/tarstall-setup") + os.chdir("/tmp/tarstall-setup") + if not run(["git", "clone", "https://github.com/hammy3502/tarstall.git"]): + status("Error while getting the tarstall repository!") + try: + rmtree("/tmp/tarstall-setup") + except FileNotFoundError: + pass + sys.exit(1) + os.chdir("/tmp/tarstall-setup/tarstall") + + # Install requirements + status("Installing tarstall's requirements") + if not run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt", "--user"]): + install_package({"apt": "python3-pip", "dnf": "python3-pip", "pacman": "python-pip"}) + if not run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt", "--user"]) or not \ + run([sys.executable, "-m", "pip", "install", "-r", "requirements-gui.txt", "--user"]): + status("An error occured while installing the GUI requirements!") + sys.exit(1) + + # Test tkinter (since we can't install it through pip) + status("Installing tkinter") + try: + import tkinter + del tkinter + print("Thanks for having tkinter already installed!") + except ImportError: + install_package({"apt": "python3-tk", "dnf": "python3-tkinter", "pacman": "tk"}) + + # Pass control to tarstall + status("Running tarstall installation") + run([sys.executable, "./tarstall_execs/tarstall", "-f"]) + + # Removing tarstall setup directory + try: + rmtree("/tmp/tarstall-setup") + except FileNotFoundError: + pass + +if __name__ == "__main__": + setup() \ No newline at end of file diff --git a/prog_manage.py b/prog_manage.py index ad1b57a..2da49c7 100644 --- a/prog_manage.py +++ b/prog_manage.py @@ -1,5 +1,5 @@ """tarstall: A package manager for managing archives - Copyright (C) 2019 hammy3502 + Copyright (C) 2020 hammy3502 tarstall is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ import os from shutil import copyfile, rmtree, move, which, copy, copytree -from subprocess import call, DEVNULL +from subprocess import call, run, DEVNULL, PIPE import sys import re import getpass @@ -40,6 +40,73 @@ else: c_out = DEVNULL +def add_upgrade_url(program, url): + """Adds an Upgrade URL to a Program. + + Args: + program (str): The program that will be upgraded + url (str): The URL containing the archive to upgrade from. THIS MUST BE A VALID URL!!!!! + + """ + config.db["programs"][program]["update_url"] = url + config.write_db() + + +def remove_update_url(program): + """Removes Upgrade URL for Program. + + Args: + program (str): Program to remove URL of. + + """ + config.db["programs"][program]["update_url"] = None + config.write_db() + + +def wget_program(program): + """Wget an Archive and Overwrite Program. + + Args: + program (str): Program that has an update_url to update + + Returns: + str: "No wget", "Wget error", "Install error" if install() fails, "Success" on success. + + """ + if not config.check_bin("wget"): + return "No wget" + else: + config.vprint("Creating second temp folder for archive.") + try: + rmtree(config.full("/tmp/tarstall-temp2")) + except FileNotFoundError: + pass + os.mkdir("/tmp/tarstall-temp2") + os.chdir("/tmp/tarstall-temp2") + config.vprint("Downloading archive...") + url = config.db["programs"][program]["update_url"] + if config.verbose: + err = call(["wget", url]) + else: + err = call(["wget", url], stdout=DEVNULL, stderr=DEVNULL) + if err != 0: + return "Wget error" + files = os.listdir() + config.vprint("Renaming archive") + os.rename("/tmp/tarstall-temp2/{}".format(files[0]), "/tmp/tarstall-temp2/{}".format(program + ".tar.gz")) + os.chdir("/tmp/") + config.vprint("Using install to install the program.") + inst_status = pre_install("/tmp/tarstall-temp2/{}".format(program + ".tar.gz"), True, show_progress=False) + try: + rmtree(config.full("/tmp/tarstall-temp2")) + except FileNotFoundError: + pass + if inst_status != "Installed": + return "Install error" + else: + return "Success" + + def update_program(program): """Update Program. @@ -51,7 +118,8 @@ def update_program(program): failed to execute, or "Success" on a success. Can also be something from update_git_program if the program supplied is a git installed program. Can also return "OSError" if the supplied - script doesn't specify the shell to be used. + script doesn't specify the shell to be used. Can also return + something from wget_program(). """ if config.db["programs"][program]["git_installed"]: @@ -60,6 +128,12 @@ def update_program(program): return status elif status != "Success": return status + if config.db["programs"][program]["update_url"] is not None: + status = wget_program(program) + if config.db["programs"][program]["post_upgrade_script"] is None: + return status + elif status != "Success": + return status if config.db["programs"][program]["post_upgrade_script"] is not None: if not config.db["programs"][program]["post_upgrade_script"]: config.db["programs"][program]["post_upgrade_script"] = None @@ -104,19 +178,26 @@ def update_git_program(program): program (str): Name of program to update Returns: - str: "No git" if git isn't found, "Error updating" on a generic failure, and "Success" on successful update. + str: "No git" if git isn't found, "Error updating" on a generic failure, "Success" on a successful update, and + "No update" if the program is already up-to-date. """ if not config.check_bin("git"): config.vprint("git isn't installed!") return "No git" - err = call(["git", "pull"], cwd=config.full("~/.tarstall/bin/{}".format(program)), stdout=c_out) + outp = run(["git", "pull"], cwd=config.full("~/.tarstall/bin/{}".format(program)), stdout=PIPE, stderr=PIPE) + err = outp.returncode + output = str(outp.stdout) + "\n\n\n" + str(outp.stderr) if err != 0: config.vprint("Failed updating: {}".format(program)) return "Error updating" else: - config.vprint("Successfully updated: {}".format(program)) - return "Success" + if "Already up to date." in output: + config.vprint("{} is already up to date!".format(program)) + return "No update" + else: + config.vprint("Successfully updated: {}".format(program)) + return "Success" def update_programs(): @@ -134,8 +215,9 @@ def update_programs(): increment = int(100 / len(config.db["programs"].keys())) progress = 0 statuses = {} + generic.progress(progress) for p in config.db["programs"].keys(): - if config.db["programs"][p]["git_installed"] or config.db["programs"][p]["post_upgrade_script"] is not None: + if config.db["programs"][p]["git_installed"] or config.db["programs"][p]["post_upgrade_script"] or config.db["programs"][p]["update_url"]: statuses[p] = update_program(p) progress += increment generic.progress(progress) @@ -252,7 +334,20 @@ def tarstall_startup(start_fts=False, del_lock=False, old_upgrade=False): file_version = get_file_version('file') while config.get_version('file_version') > file_version: # Lingering upgrades check - pass + config.vprint("Upgrading files and database from {} to {}.".format(file_version, config.get_version("file_version"))) + + if file_version == 11: + config.vprint("Adding 'update_url' key in database for all programs!") + for program in config.db["programs"]: + config.db["programs"][program]["update_url"] = None + + elif file_version == 12: + config.vprint("Adding 'has_path' and 'binlinks' to programs.") + for program in config.db["programs"]: + config.db["programs"][program]["has_path"] = False + config.db["programs"][program]["binlinks"] = [] + + config.db["version"]["file_version"] += 1 file_version = get_file_version('file') config.write_db() @@ -270,7 +365,7 @@ def tarstall_startup(start_fts=False, del_lock=False, old_upgrade=False): return "Good" -def pre_install(program, overwrite=None, reinstall=False): +def pre_install(program, overwrite=None, show_progress=True): """Pre-Archive Install. Preparation before installing an archive. @@ -292,11 +387,11 @@ def pre_install(program, overwrite=None, reinstall=False): else: if not overwrite: uninstall(program_internal_name) - return install(program, False, True) # Reinstall + return install(program, False, True, show_progress) # Reinstall elif overwrite: - return install(program, True, True) + return install(program, True, True, show_progress) else: - return install(program) # No reinstall needed to be asked, install program + return install(program, show_progress=show_progress) # No reinstall needed to be asked, install program config.write_db() @@ -399,6 +494,9 @@ def remove_paths_and_binlinks(program): """ config.remove_line(program, "~/.tarstall/.bashrc", 'poundword') + config.db["programs"][program]["has_path"] = False + config.db["programs"][program]["binlinks"] = [] + config.write_db() return "Complete" @@ -452,7 +550,7 @@ def finish_install(program_internal_name, is_git=False): pass config.vprint("Adding program to tarstall list of programs") config.db["programs"].update({program_internal_name: {"git_installed": is_git, "desktops": [], - "post_upgrade_script": None}}) + "post_upgrade_script": None, "update_url": None, "has_path": False, "binlinks": []}}) config.write_db() generic.progress(100) return "Installed" @@ -565,10 +663,25 @@ def gitinstall(git_url, program_internal_name, overwrite=False, reinstall=False) def add_binlink(file_chosen, program_internal_name): + """Add Binlink. + + Args: + file_chosen (str): File to add + program_internal_name (str): Name of program to binlink + + Returns: + str: "Added" or "Already there" + + """ + if file_chosen in config.db["programs"][program_internal_name]["binlinks"]: + return "Already there" line_to_add = 'alias ' + file_chosen + "='cd " + config.full('~/.tarstall/bin/' + program_internal_name) + \ '/ && ./' + file_chosen + "' # " + program_internal_name + "\n" config.vprint("Adding alias to bashrc") config.add_line(line_to_add, "~/.tarstall/.bashrc") + config.db["programs"][program_internal_name]["binlinks"].append(file_chosen) + config.write_db() + return "Added" def pathify(program_internal_name): @@ -579,10 +692,16 @@ def pathify(program_internal_name): Args: program_internal_name (str): Name of program to add to PATH + Returns: + "Complete" or "Already there" + """ + if config.db["programs"][program_internal_name]["has_path"]: + return "Already there" config.vprint('Adding program to PATH') line_to_write = "export PATH=$PATH:~/.tarstall/bin/" + program_internal_name + ' # ' + program_internal_name + '\n' config.add_line(line_to_write, "~/.tarstall/.bashrc") + config.db["programs"][program_internal_name]["has_path"] = True return "Complete" @@ -650,7 +769,7 @@ def update(force_update=False, show_progress=True): config.vprint("Moving in new tarstall files") os.chdir("/tmp/tarstall-update/tarstall/") files = os.listdir() - to_ignore = [".git", ".gitignore", "README.md", "readme-images", "COPYING", "requirements.txt", "requirements-gui.txt", "tests"] + to_ignore = [".git", ".gitignore", "README.md", "readme-images", "COPYING", "requirements.txt", "requirements-gui.txt", "tests", "install_tarstall"] for f in files: if f not in to_ignore: move("/tmp/tarstall-update/tarstall/{}".format(f), config.full("~/.tarstall/{}".format(f))) @@ -706,6 +825,16 @@ def erase(): rmtree("/tmp/tarstall-temp") except FileNotFoundError: pass + generic.progress(95) + try: + rmtree("/tmp/tarstall-temp2") + except FileNotFoundError: + pass + generic.progress(98) + try: + os.remove(config.full("~/.local/share/applications/tarstall.desktop")) + except FileNotFoundError: + pass config.unlock() generic.progress(100) return to_return @@ -842,7 +971,7 @@ def create_command(file_extension, program): return command_to_go -def install(program, overwrite=False, reinstall=False): +def install(program, overwrite=False, reinstall=False, show_progress=True): """Install Archive. Takes an archive and installs it. @@ -852,7 +981,7 @@ def install(program, overwrite=False, reinstall=False): overwrite (bool): Whether or not to assume the program is already installed and to overwite it Returns: - str: A string from finish_install() a string from create_command(), "No rsync", "Bad name", "Installed", or "Error" + str: A string from finish_install() a string from create_command(), "No rsync", "Bad name", "Installed", or "Error". """ if not config.check_bin("rsync") and overwrite: @@ -865,7 +994,7 @@ def install(program, overwrite=False, reinstall=False): rmtree(config.full("/tmp/tarstall-temp")) # Removes temp directory (used during installs) except FileNotFoundError: pass - generic.progress(10) + generic.progress(10, show_progress) config.vprint("Creating new temp directory") os.mkdir(config.full("/tmp/tarstall-temp")) # Creates temp directory for extracting archive config.vprint("Extracting archive to temp directory") @@ -881,16 +1010,22 @@ def install(program, overwrite=False, reinstall=False): print('Failed to run command: ' + command_to_go + "!") print("Program installation halted!") return "Error" - generic.progress(50) + generic.progress(50, show_progress) config.vprint('Checking for folder in folder') + os.chdir("/tmp/tarstall-temp/") if os.path.isdir(config.full('/tmp/tarstall-temp/' + program_internal_name + '/')): config.vprint('Folder in folder detected! Using that directory instead...') source = config.full('/tmp/tarstall-temp/' + program_internal_name) + '/' - dest = config.full('~/.tarstall/bin/') + dest = config.full('~/.tarstall/bin/' + program_internal_name) + '/' + elif len(os.listdir()) == 1 and os.path.isdir(os.listdir()[0]): + config.vprint("Single folder detected!") + folder = os.listdir()[0] + source = config.full("/tmp/tarstall-temp/" + folder) + dest = config.full("~/.tarstall/bin/" + program_internal_name) + '/' else: config.vprint('Folder in folder not detected!') source = config.full('/tmp/tarstall-temp') + '/' - dest = config.full('~/.tarstall/bin/' + program_internal_name + "/") + dest = config.full('~/.tarstall/bin/' + program_internal_name) + '/' config.vprint("Moving program to directory") if overwrite: if verbose: @@ -900,9 +1035,10 @@ def install(program, overwrite=False, reinstall=False): call(["rsync", "-a{}".format(verbose_flag), source, dest], stdout=c_out) else: move(source, dest) - generic.progress(80) + generic.progress(80, show_progress) config.vprint("Adding program to tarstall list of programs") config.vprint('Removing old temp directory...') + os.chdir("/tmp") try: rmtree(config.full("/tmp/tarstall-temp")) except FileNotFoundError: @@ -910,7 +1046,7 @@ def install(program, overwrite=False, reinstall=False): if not reinstall: return finish_install(program_internal_name) else: - generic.progress(100) + generic.progress(100, show_progress) return "Installed" diff --git a/readme-images/gui.png b/readme-images/gui.png index 7b7b4bd..cc37e2f 100644 Binary files a/readme-images/gui.png and b/readme-images/gui.png differ diff --git a/tarstall_execs/tarstall b/tarstall_execs/tarstall index d45d2ee..95e5769 100755 --- a/tarstall_execs/tarstall +++ b/tarstall_execs/tarstall @@ -1,7 +1,7 @@ #!/usr/bin/python3 """tarstall: A package manager for managing archives - Copyright (C) 2019 hammy3502 + Copyright (C) 2020 hammy3502 tarstall is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ sys.path.insert(1, os.path.abspath(os.path.expanduser("{}/..".format(os.path.dir import config import generic import prog_manage +import re from subprocess import call mode = config.read_config("Mode") @@ -36,16 +37,17 @@ if mode == "gui": try: import tkinter del tkinter + try: + import PySimpleGUI as sg + sg.theme("Black") + except ImportError: + config.mode = "cli" + mode = "cli" + print("PySimpleGUI not installed! Defaulting to cli mode...") except ImportError: config.mode = "cli" mode = "cli" print("Tkinter not installed! Defaulting to cli mode...") - try: - import PySimpleGUI as sg - except ImportError: - config.mode = "cli" - mode = "cli" - print("PySimpleGUI not installed! Defaulting to cli mode...") def gui_loop(): """Main Loop for GUI.""" @@ -122,6 +124,42 @@ def gui_loop(): window.Element("manage").Update(disabled=False) +def wget_wizard(program): + if not config.check_bin("wget"): + generic.pprint("You must have 'wget' installed to use this feature!") + return + while True: + if config.db["programs"][program]["update_url"]: + r_msg = "\nr - Remove URL to download a copy of this program when upgraded." + else: + r_msg = "" + msg = """ +Select an option: +a - Add a URL to download a fresh copy of this program when upgraded{r_msg} +e - Return to program management""".format(r_msg=r_msg) + options = ['a', 'e'] + gui_options = ["Add a URL", "Return to Program Management"] + if r_msg: + options.append('r') + gui_options.append("Remove URL from Program") + ans = generic.get_input(msg, options, 'e', gui_options) + if ans == 'a': + new_url = "" + while re.match(r"https://\w.\w", new_url) is None or new_url == "": + new_url = generic.ask("Please enter a URL that points directly to a .tar.gz that contains" + + "a full-copy of the program to install when upgrading. Type 'c' or 'cancel' to cancel: ") + if new_url.lower() == 'c' or new_url.lower() == "cancel": + return + prog_manage.add_upgrade_url(program, new_url) + generic.pprint("URL added successfully!") + elif ans == 'r' and r_msg: + prog_manage.remove_update_url(program) + generic.pprint("Upgrade URL successfully removed from program!") + elif ans == 'e': + return + + + def git_wizard(program): """Extra management for a program installed through git. @@ -271,6 +309,8 @@ def pathify(program): status = prog_manage.pathify(program) if status == "Complete": generic.pprint("Program added to PATH!") + elif status == "Already there": + generic.pprint("Program already added to PATH!") def binlink(program): @@ -289,7 +329,9 @@ def binlink(program): file_chosen = input('Please enter a file listed above. If you would like to cancel, type exit: ') if file_chosen == "exit": return - prog_manage.add_binlink(file_chosen, program) + status = prog_manage.add_binlink(file_chosen, program) + if status == "Already there": + generic.pprint("Binlink not added since it already exists!") yn = generic.get_input('Would you like to continue adding files to be run directly?', ['y', 'n'], 'n') @@ -395,10 +437,15 @@ def manage(program): options.append('q') option_strings.append("Upgrade program") else: - git_msg = "" - g_msg = "" - us = "an upgrade" - if q == "" and config.db["programs"][program]["post_upgrade_script"] is not None: + git_msg = "\nw - Add/remove an update URL for {}".format(program) + g_msg = "w/" + options.append('w') + option_strings.append("Add/remove update URL") + if config.db["programs"][program]["update_url"]: + us = "a post-upgrade" + else: + us = "an upgrade" + if q == "" and (config.db["programs"][program]["post_upgrade_script"] or config.db["programs"][program]["update_url"]): q_msg = "\nq - Upgrade {program}".format(program=program) q = "q/" options.append('q') @@ -461,12 +508,16 @@ E - Exit program management""".format(program=program, git=git_msg, g=g_msg, us= call(["/bin/zsh"]) else: call(["/bin/bash"]) - elif option == 'g': + elif option == 'g' and g_msg == "g/": git_wizard(program) + elif option == 'w' and g_msg == "w/": + wget_wizard(program) elif option == 'q' and q != "": status = prog_manage.update_program(program) if status == "Success": generic.pprint("Program upgrading successful!") + elif status == "No update": + generic.pprint("Program is already up to date!") elif status == "No git": generic.pprint("Git not installed, please install it!") elif status == "Error updating": @@ -477,6 +528,12 @@ E - Exit program management""".format(program=program, git=git_msg, g=g_msg, us= generic.pprint("Error while executing the supplied upgrade script!") elif status == "OSError": generic.pprint("Shell not specified! Please specify one at the top of the supplied script (ex. #!/bin/sh)") + elif status == "No wget": + generic.pprint("Wget is not installed!") + elif status == "Wget error": + generic.pprint("An error occured while downloading the archive!") + elif status == "Install error": + generic.pprint("An error occured while installing the program!") elif option == 'us': msg = """ Please input the path to a script you would like to run to upgrade an installed program. @@ -544,7 +601,7 @@ def parse_args(args=None): group.add_argument('-k', '--remove-lock', help="Remove tarstall lock file (only do this if tarstall isn't already " "running)", action="store_true") group.add_argument('-c', '--config', help="Change tarstall options", action="store_true") - group.add_argument('-q', '--update-programs', help="Update programs installed through git and ones with upgrade scripts", action="store_true") + group.add_argument('-q', '--update-programs', help="Update programs installed through git, ones with upgrade scripts, and ones with a URL assigned to them", action="store_true") if args is None: args = parser.parse_args() else: @@ -577,6 +634,7 @@ def parse_args(args=None): else: generic.pprint('tarstall not installed.') config.unlock() + sys.exit(1) elif status == "Root": generic.pprint("Don't use sudo unless you want your programs installed for root and only root!") @@ -748,6 +806,8 @@ def parse_args(args=None): for p in status.keys(): if status[p] == "Success": msg += p + " updated successfully!\n" + elif status[p] == "No update": + msg += p + " is already up to date!\n" elif status[p] == "OSError": msg += p + " does not have #!/bin/sh or similar specified at the top of its file!" else: @@ -763,6 +823,7 @@ Written by: hammy3502 tarstall Version: {user_version} Internal Version Code: {file_version}.{prog_version} +Branch: {branch} For help, type "tarstall -h" @@ -770,7 +831,7 @@ For additional help, the tarstall wiki is linked below. https://github.com/hammy3502/tarstall/wiki """.format(user_version=config.get_version("version"), file_version=config.get_version("file_version"), - prog_version=config.get_version("prog_internal_version"))) + prog_version=config.get_version("prog_internal_version"), branch=config.branch)) config.unlock() if mode == "gui": diff --git a/tests/test_config.py b/tests/test_config.py index 6e17ee4..aac710b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -70,7 +70,10 @@ def test_get_db(): "package": { "git_installed": False, "desktops": [], - "post_upgrade_script": None + "post_upgrade_script": None, + "update_url": None, + "has_path": False, + "binlinks": [] } } } @@ -90,7 +93,7 @@ def test_extension(): def test_exists(): - assert config.exists("./config.py") is True + assert config.exists(os.path.expanduser("~/.tarstall/config.py")) is True assert config.exists("./config.no") is False diff --git a/tests/test_prog_manage.py b/tests/test_prog_manage.py index be5dbbb..4cf9e79 100644 --- a/tests/test_prog_manage.py +++ b/tests/test_prog_manage.py @@ -36,9 +36,6 @@ def test_get_file_version(): def test_pathify(): prog_manage.pathify("package") assert config.check_line("export PATH=$PATH:~/.tarstall/bin/package # package", "~/.tarstall/.bashrc", "fuzzy") - prog_manage.pathify("test_program") - assert config.check_line("export PATH=$PATH:~/.tarstall/bin/test_program # test_program", "~/.tarstall/.bashrc", - "fuzzy") def test_verbose_toggle(): diff --git a/version b/version index 4ba681d..7366d8d 100644 --- a/version +++ b/version @@ -1 +1 @@ -11.73 \ No newline at end of file +13.82 \ No newline at end of file