diff --git a/src/viv/viv.py b/src/viv/viv.py index 5cb12f8..1c04de8 100755 --- a/src/viv/viv.py +++ b/src/viv/viv.py @@ -21,6 +21,8 @@ import sys import tempfile import threading import time +from urllib.request import urlopen +from urllib.error import HTTPError import venv from argparse import SUPPRESS, Action from argparse import ArgumentParser as StdArgParser @@ -49,7 +51,7 @@ from typing import ( Generator, ) -__version__ = "22.12a3-41-gddb03df-dev" +__version__ = "22.12a3-44-g3b9c2e6-dev" @dataclass @@ -305,6 +307,16 @@ def echo( fd.write(output) +def confirm(question: str) -> bool: + while True: + ans = input(question + a.style(" (Y)es/(n)o: ", "yellow")).strip().lower() + if ans in ("y", "yes"): + return True + elif ans in ("n", "no"): + return False + sys.stdout.write("\nPlease select (Y)es or (n)o.") + + def run( command: List[str], spinmsg: str = "", @@ -540,6 +552,12 @@ _viv_use({spec}) 1: ] +SHOW_TEMPLATE = f"""\ + {a.style('Version', 'bold')}: {{version}} + {a.style('CLI', 'bold')}: {{cli}} + {a.style('Source File', 'bold')}: {{src}} +""" + def noqa(txt: str) -> str: max_length = max(map(len, txt.splitlines())) @@ -898,7 +916,63 @@ class Viv: def manage(self, args: Namespace) -> None: """manage viv installation""" - echo("not yet implemented. sorry") + + if args.cmd == "show": + # NOTE: could reuse the table output for this? + echo("Installation Info:") + sys.stdout.write( + SHOW_TEMPLATE.format( + version=__version__, + cli=shutil.which("viv"), + src=Path(__file__).resolve(), + ) + ) + + elif args.cmd == "update": + try: + r = urlopen( + "https://raw.githubusercontent.com/daylinmorgan/viv/" + + args.reference + + "/src/viv/viv.py" + ) + except HTTPError as e: + error( + "Issue updating viv see below:" + + a.style("-> ", "red").join(["\n"] + repr(e).splitlines()) + ) + if "404" in repr(e): + echo("Please check your reference is valid.", style="red") + sys.exit(1) + + viv_src = r.read() + + (hash := hashlib.sha256()).update(viv_src) + sha256 = hash.hexdigest() + + ( + src_cache := Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) + / "viv" + / "src" + ).mkdir(exist_ok=True, parents=True) + cached_version = src_cache / f"{sha256}.py" + + if not cached_version.is_file(): + with cached_version.open("w") as f: + f.write(viv_src.decode()) + + sys.path.append(str(src_cache)) + next_version = __import__(sha256).__version__ + q = ( + "Update source at: " + + a.style(Path(__file__).resolve(), "bold") + + f" \n from version {__version__} to {next_version}?" + ) + + if confirm(q): + print("AWAY THEN") + + elif args.cmd == "install": + echo("not yet implemented. sorry") def _get_subcmd_parser( self, @@ -964,16 +1038,15 @@ class Viv: nargs="*", ) - p_exe_python = p_exe_sub.add_parser( + p_exe_sub.add_parser( "python", help="run command with python", parents=[p_vivenv_arg, p_exe_shared], - ) - p_exe_pip = p_exe_sub.add_parser( + ).set_defaults(func=self.exe, exe="python") + + p_exe_sub.add_parser( "pip", help="run command with pip", parents=[p_vivenv_arg, p_exe_shared] - ) - p_exe_python.set_defaults(func=self.exe, exe="python") - p_exe_pip.set_defaults(func=self.exe, exe="pip") + ).set_defaults(func=self.exe, exe="pip") p_remove = self._get_subcmd_parser( subparsers, @@ -1018,7 +1091,32 @@ class Viv: parents=[p_vivenv_arg], ) - self._get_subcmd_parser(subparsers, name="manage") + p_manage_sub = self._get_subcmd_parser( + subparsers, name="manage" + ).add_subparsers(title="subcommand", metavar="", required=True) + + p_manage_sub.add_parser( + "install", + help="install viv", + ).set_defaults(func=self.manage, cmd="install") + + ( + p_manage_update := p_manage_sub.add_parser( + "update", + help="update viv version", + ) + ).set_defaults(func=self.manage, cmd="update") + + p_manage_update.add_argument( + "-r", + "--reference", + help="git reference (branch/tag/commit)", + default="main", + ) + + p_manage_sub.add_parser( + "show", help="show current installation info" + ).set_defaults(func=self.manage, cmd="show") args = parser.parse_args()