From e81a2915e1095acd7440d40867ad9a36d8ecc8f8 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 21 Oct 2024 10:26:03 +0000 Subject: [PATCH 01/15] add exponential backoff --- .submodules/setup/__main__.py | 159 +++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 31 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index b94dec84..b19080a1 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -6,6 +6,7 @@ """ import json +import os import re import subprocess import typing as t @@ -13,8 +14,9 @@ from dataclasses import dataclass from pathlib import Path from subprocess import CalledProcessError +from time import sleep -from colorama import Fore, Style +from colorama import Back, Fore, Style from colorama import init as colorama_init BASE_DIR = Path(__file__).resolve().parent.parent.parent @@ -28,6 +30,25 @@ class Submodule: url: str +def generate_console_link( + url: str, + label: t.Optional[str] = None, + parameters: str = "", +): + """Generates a link to be printed in the console. + + Args: + url: The link to follow. + label: The label of the link. If not given, the url will be the label. + parameters: Any url parameters you may have. + + Returns: + A link that can be clicked in the console. + """ + # OSC 8 ; params ; URI ST OSC 8 ;; ST + return f"\033]8;{parameters};{url}\033\\{label or url}\033]8;;\033\\" + + def get_namespace() -> Namespace: """Get the command line values passed to this script. @@ -94,7 +115,7 @@ def login_to_github(): ) -def fork_repo(name: str, url: str): +def fork_repo(url: str): """Fork a repo on GitHub. https://cli.github.com/manual/gh_repo_fork @@ -102,8 +123,11 @@ def fork_repo(name: str, url: str): Args: owner: The owner of the repo to fork. name: The name of the repo to fork. + + Returns: + A flag designating whether the repo was successfully forked. """ - print(Style.BRIGHT + f'Forking repo "{name}".' + Style.RESET_ALL) + print(Style.BRIGHT + "Forking repo..." + Style.RESET_ALL) try: subprocess.run( @@ -118,7 +142,11 @@ def fork_repo(name: str, url: str): check=True, ) except CalledProcessError: - pass + print(Style.BRIGHT + Fore.RED + "Failed to fork repo." + Style.RESET_ALL) + + return False + + return True def clone_repo(name: str, path: str): @@ -128,16 +156,42 @@ def clone_repo(name: str, path: str): Args: name: The name of the repo to clone. + + Returns: + A flag designating whether the repo was successfully cloned. """ - print(Style.BRIGHT + f'Cloning repo "{name}".' + Style.RESET_ALL) + print(Style.BRIGHT + "Cloning repo..." + Style.RESET_ALL) - try: - subprocess.run( - ["gh", "repo", "clone", name, str(BASE_DIR / path)], - check=True, - ) - except CalledProcessError: - pass + repo_dir = str(BASE_DIR / path) + + if os.path.isdir(repo_dir) and os.listdir(repo_dir): + print(Style.BRIGHT + repo_dir + Style.RESET_ALL + " already exists.") + + return True + + retry_delay, max_retries = 1, 5 + for retry_index in range(max_retries): + try: + subprocess.run( + ["gh", "repo", "clone", name, repo_dir], + check=True, + ) + + return True + except CalledProcessError: + print( + Style.BRIGHT + + Fore.YELLOW + + f"Retrying clone in {retry_delay} seconds." + + f" Attempt {retry_index + 1}/{max_retries}." + + Style.RESET_ALL + ) + sleep(retry_delay) + retry_delay *= 2 + + print(Style.BRIGHT + Fore.RED + "Failed to clone repo." + Style.RESET_ALL) + + return False def view_repo(name: str): @@ -148,19 +202,24 @@ def view_repo(name: str): Args: name: The name of the repo to view. """ - print(Style.BRIGHT + f'Viewing repo "{name}".' + Style.RESET_ALL) - - repo_str = subprocess.run( - [ - "gh", - "repo", - "view", - name, - "--json=" + ",".join(["name", "url", "createdAt", "isFork"]), - ], - check=True, - stdout=subprocess.PIPE, - ).stdout.decode("utf-8") + print(Style.BRIGHT + "Viewing repo..." + Style.RESET_ALL) + + try: + repo_str = subprocess.run( + [ + "gh", + "repo", + "view", + name, + "--json=" + ",".join(["name", "url", "createdAt", "isFork"]), + ], + check=True, + stdout=subprocess.PIPE, + ).stdout.decode("utf-8") + except CalledProcessError: + print(Fore.YELLOW + "Failed to view repo." + Style.RESET_ALL) + + return repo = json.loads(repo_str) print(json.dumps(repo, indent=2)) @@ -177,14 +236,52 @@ def main() -> None: if not namespace.skip_login: login_to_github() - for name, submodule in submodules.items(): - fork_repo(name, submodule.url) - - clone_repo(name, submodule.path) + error = False - view_repo(name) + for i, (name, submodule) in enumerate(submodules.items(), start=1): + print( + Style.DIM + + Back.GREEN + + f"Submodule ({i}/{len(submodules)}): {name}" + + Style.RESET_ALL + ) - print(Style.BRIGHT + Fore.GREEN + "Setup completed." + Style.RESET_ALL) + forked_repo = fork_repo(submodule.url) + + cloned_repo = False + if forked_repo: + cloned_repo = clone_repo(name, submodule.path) + + view_repo(name) + + if not error and (not forked_repo or not cloned_repo): + error = True + + print() + print( + Style.BRIGHT + + Fore.RED + + "💥💣💥 Finished with errors. 💥💣💥" + + Style.RESET_ALL + + "\n\n" + + "This may not be an issue and may be occurring because you've run" + + " this setup script before. Please read the above logs to discover if" + + " further action is required." + + "\n\n" + + "If you require help, please reach out to " + + generate_console_link( + "mailto:codeforlife@ocado.com", + "codeforlife@ocado.com", + ) + + "." + if error + else Style.BRIGHT + + Fore.GREEN + + "✨🍰✨ Finished without errors. ✨🍰✨" + + Style.RESET_ALL + + "\n\n" + + "Happy coding!" + ) if __name__ == "__main__": From ab8ce082b625d33121299cdebfbb11b797925b08 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 21 Oct 2024 13:15:14 +0000 Subject: [PATCH 02/15] fix over writing repo --- .submodules/setup/__main__.py | 41 ++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index b19080a1..adb635df 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -13,6 +13,7 @@ from argparse import ArgumentParser, Namespace from dataclasses import dataclass from pathlib import Path +from shutil import rmtree from subprocess import CalledProcessError from time import sleep @@ -61,6 +62,17 @@ def get_namespace() -> Namespace: action="store_true", dest="skip_login", default=False, + help="Skip login if already logged in.", + ) + arg_parser.add_argument( + "--overwrite-clone", + action="store_true", + dest="overwrite_clone", + default=False, + help=( + "Deletes each clone's current directory if they have one and clones" + " each repo." + ), ) return arg_parser.parse_args() @@ -99,20 +111,14 @@ def read_submodules() -> t.Dict[str, Submodule]: def login_to_github(): - """Log into GitHub with the CLI and setup Git to use the CLI as a credential - helper. + """Log into GitHub with the CLI. https://cli.github.com/manual/gh_auth_login - https://cli.github.com/manual/gh_auth_setup-git """ subprocess.run( ["gh", "auth", "login", "--web"], check=True, ) - subprocess.run( - ["gh", "auth", "setup-git"], - check=True, - ) def fork_repo(url: str): @@ -149,17 +155,21 @@ def fork_repo(url: str): return True -def clone_repo(name: str, path: str): +def clone_repo(name: str, path: str, overwrite: bool): + # pylint: disable=line-too-long """Clone a repo from GitHub. https://cli.github.com/manual/gh_repo_clone Args: name: The name of the repo to clone. + path: The paths to clone the repo to. + overwrite: A flag designating whether to delete the repo's current directory if it exists and clone the repo in the directory. Returns: A flag designating whether the repo was successfully cloned. """ + # pylint: enable=line-too-long print(Style.BRIGHT + "Cloning repo..." + Style.RESET_ALL) repo_dir = str(BASE_DIR / path) @@ -167,7 +177,10 @@ def clone_repo(name: str, path: str): if os.path.isdir(repo_dir) and os.listdir(repo_dir): print(Style.BRIGHT + repo_dir + Style.RESET_ALL + " already exists.") - return True + if overwrite: + rmtree(repo_dir) + else: + return True retry_delay, max_retries = 1, 5 for retry_index in range(max_retries): @@ -179,6 +192,9 @@ def clone_repo(name: str, path: str): return True except CalledProcessError: + if os.path.isdir(repo_dir): + rmtree(repo_dir) + print( Style.BRIGHT + Fore.YELLOW @@ -186,6 +202,7 @@ def clone_repo(name: str, path: str): + f" Attempt {retry_index + 1}/{max_retries}." + Style.RESET_ALL ) + sleep(retry_delay) retry_delay *= 2 @@ -250,7 +267,11 @@ def main() -> None: cloned_repo = False if forked_repo: - cloned_repo = clone_repo(name, submodule.path) + cloned_repo = clone_repo( + name, + submodule.path, + namespace.overwrite_clone, + ) view_repo(name) From 292b50a28c6553243ca937efe0af4b9750dcb18e Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 21 Oct 2024 14:50:49 +0000 Subject: [PATCH 03/15] final touches --- .submodules/setup/Pipfile | 1 + .submodules/setup/Pipfile.lock | 78 ++++++++++++-- .submodules/setup/__main__.py | 180 ++++++++++++++++++++------------- 3 files changed, 182 insertions(+), 77 deletions(-) diff --git a/.submodules/setup/Pipfile b/.submodules/setup/Pipfile index d35fbe7c..415df001 100644 --- a/.submodules/setup/Pipfile +++ b/.submodules/setup/Pipfile @@ -5,6 +5,7 @@ name = "pypi" [packages] colorama = "==0.4.6" +inquirer = "==3.4.0" [dev-packages] black = "==24.8.0" diff --git a/.submodules/setup/Pipfile.lock b/.submodules/setup/Pipfile.lock index 4ff413e0..4c63982b 100644 --- a/.submodules/setup/Pipfile.lock +++ b/.submodules/setup/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "7ace988b06a3bbcd451ebf9dcd145ccc9de0d3fd55f583a0619944a4381aa98e" + "sha256": "7698450d46a6ab01ec8333eebb2719c8b41f0ccdf7e17ca7d53b4fa258503a92" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,14 @@ ] }, "default": { + "blessed": { + "hashes": [ + "sha256:0c542922586a265e699188e52d5f5ac5ec0dd517e5a1041d90d2bbf23f906058", + "sha256:2cdd67f8746e048f00df47a2880f4d6acbcdb399031b604e34ba8f71d5787680" + ], + "markers": "python_version >= '2.7'", + "version": "==1.20.0" + }, "colorama": { "hashes": [ "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", @@ -24,6 +32,62 @@ "index": "pypi", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", "version": "==0.4.6" + }, + "editor": { + "hashes": [ + "sha256:bb6989e872638cd119db9a4fce284cd8e13c553886a1c044c6b8d8a160c871f8", + "sha256:e818e6913f26c2a81eadef503a2741d7cca7f235d20e217274a009ecd5a74abf" + ], + "markers": "python_version >= '3.8'", + "version": "==1.6.6" + }, + "inquirer": { + "hashes": [ + "sha256:8edc99c076386ee2d2204e5e3653c2488244e82cb197b2d498b3c1b5ffb25d0b", + "sha256:bb0ec93c833e4ce7b51b98b1644b0a4d2bb39755c39787f6a504e4fee7a11b60" + ], + "index": "pypi", + "markers": "python_full_version >= '3.8.1'", + "version": "==3.4.0" + }, + "readchar": { + "hashes": [ + "sha256:2a587a27c981e6d25a518730ad4c88c429c315439baa6fda55d7a8b3ac4cb62a", + "sha256:44807cbbe377b72079fea6cba8aa91c809982d7d727b2f0dbb2d1a8084914faa" + ], + "markers": "python_version >= '3.8'", + "version": "==4.2.0" + }, + "runs": { + "hashes": [ + "sha256:0980dcbc25aba1505f307ac4f0e9e92cbd0be2a15a1e983ee86c24c87b839dfd", + "sha256:9dc1815e2895cfb3a48317b173b9f1eac9ba5549b36a847b5cc60c3bf82ecef1" + ], + "markers": "python_version >= '3.8'", + "version": "==1.2.2" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "wcwidth": { + "hashes": [ + "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", + "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5" + ], + "version": "==0.2.13" + }, + "xmod": { + "hashes": [ + "sha256:38c76486b9d672c546d57d8035df0beb7f4a9b088bc3fb2de5431ae821444377", + "sha256:a24e9458a4853489042522bdca9e50ee2eac5ab75c809a91150a8a7f40670d48" + ], + "markers": "python_version >= '3.8'", + "version": "==1.8.1" } }, "develop": { @@ -74,11 +138,11 @@ }, "dill": { "hashes": [ - "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", - "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7" + "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", + "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c" ], "markers": "python_version >= '3.11'", - "version": "==0.3.8" + "version": "==0.3.9" }, "iniconfig": { "hashes": [ @@ -164,11 +228,11 @@ }, "platformdirs": { "hashes": [ - "sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5", - "sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0" + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" ], "markers": "python_version >= '3.8'", - "version": "==4.3.3" + "version": "==4.3.6" }, "pluggy": { "hashes": [ diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index adb635df..e6bc052e 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -10,13 +10,13 @@ import re import subprocess import typing as t -from argparse import ArgumentParser, Namespace from dataclasses import dataclass from pathlib import Path from shutil import rmtree from subprocess import CalledProcessError from time import sleep +import inquirer # type: ignore[import-untyped] from colorama import Back, Fore, Style from colorama import init as colorama_init @@ -31,6 +31,62 @@ class Submodule: url: str +def print_intro(): + """Prints the Code For Life logo with ascii art.""" + M, C, Y = Fore.MAGENTA, Fore.CYAN, Fore.YELLOW + + print( + Style.BRIGHT + + f""" + {M}_____ {Y}_ ______ _ {M}_ {C}__ + {M}/ ____| {Y}| | | ____| | | {M}(_){C}/ _| + {M}| | {C}___ {Y}__| | {M}___ {Y}| |__ {M}___ {C}_ __ {Y}| | {M}_| {C}|_ {Y}___ + {M}| | {C}/ _ \\ {Y}/ _` |{M}/ _ \\ {Y}| __{M}/ _ \\{C}| '__| {Y}| | {M}| | {C}_{Y}/ _ \\ + {M}| |___{C}| (_) | {Y}(_| | {M}__/ {Y}| | {M}| (_) {C}| | {Y}| |____{M}| | {C}|{Y}| __/ + {M}\\_____{C}\\___/ {Y}\\__,_|{M}\\___| {Y}|_| {M}\\___/{C}|_| {Y}|______{M}|_|{C}_| {Y}\\___| +""" + + Style.RESET_ALL + + "\nTo learn more, check out " + + generate_console_link( + "https://docs.codeforlife.education/", "our documentation" + ) + + ".\n" + ) + + +def print_exit(error: bool): + """Prints the exiting statement to the console. + + Args: + error: Whether there was an error during the script-run. + """ + print() + print( + Style.BRIGHT + + Fore.RED + + "💥💣💥 Finished with errors. 💥💣💥" + + Style.RESET_ALL + + "\n\n" + + "This may not be an issue and may be occurring because you've run" + + " this setup script before. Please read the above logs to discover if" + + " further action is required." + + "\n\n" + + "If you require help, please reach out to " + + generate_console_link( + "mailto:codeforlife@ocado.com", + "codeforlife@ocado.com", + ) + + "." + if error + else Style.BRIGHT + + Fore.GREEN + + "✨🍰✨ Finished without errors. ✨🍰✨" + + Style.RESET_ALL + + "\n\n" + + "Happy coding!" + ) + + def generate_console_link( url: str, label: t.Optional[str] = None, @@ -50,34 +106,6 @@ def generate_console_link( return f"\033]8;{parameters};{url}\033\\{label or url}\033]8;;\033\\" -def get_namespace() -> Namespace: - """Get the command line values passed to this script. - - Returns: - An object containing all the command line values. - """ - arg_parser = ArgumentParser() - arg_parser.add_argument( - "--skip-login", - action="store_true", - dest="skip_login", - default=False, - help="Skip login if already logged in.", - ) - arg_parser.add_argument( - "--overwrite-clone", - action="store_true", - dest="overwrite_clone", - default=False, - help=( - "Deletes each clone's current directory if they have one and clones" - " each repo." - ), - ) - - return arg_parser.parse_args() - - def read_submodules() -> t.Dict[str, Submodule]: """Read the submodules from .gitmodules (located at the workspace's root). @@ -115,10 +143,40 @@ def login_to_github(): https://cli.github.com/manual/gh_auth_login """ - subprocess.run( - ["gh", "auth", "login", "--web"], + print(Style.BRIGHT + "Checking if you are logged into GitHub..." + Style.RESET_ALL) + + status = subprocess.run( + ["gh", "auth", "status"], check=True, - ) + stdout=subprocess.PIPE, + ).stdout.decode("utf-8") + + logged_in = not status.startswith("You are not logged into any GitHub hosts") + + if logged_in: + answers = inquirer.prompt( + [ + inquirer.Confirm( + "stay_logged_in", + message="Continue with logged in account?", + ) + ] + ) + + if answers: + logged_in = t.cast(bool, answers["stay_logged_in"]) + + if not logged_in: + subprocess.run( + ["gh", "auth", "logout"], + check=True, + ) + + if not logged_in: + subprocess.run( + ["gh", "auth", "login", "--web"], + check=True, + ) def fork_repo(url: str): @@ -155,7 +213,7 @@ def fork_repo(url: str): return True -def clone_repo(name: str, path: str, overwrite: bool): +def clone_repo(name: str, path: str): # pylint: disable=line-too-long """Clone a repo from GitHub. @@ -164,7 +222,6 @@ def clone_repo(name: str, path: str, overwrite: bool): Args: name: The name of the repo to clone. path: The paths to clone the repo to. - overwrite: A flag designating whether to delete the repo's current directory if it exists and clone the repo in the directory. Returns: A flag designating whether the repo was successfully cloned. @@ -177,11 +234,23 @@ def clone_repo(name: str, path: str, overwrite: bool): if os.path.isdir(repo_dir) and os.listdir(repo_dir): print(Style.BRIGHT + repo_dir + Style.RESET_ALL + " already exists.") - if overwrite: - rmtree(repo_dir) - else: + answers = inquirer.prompt( + [ + inquirer.Confirm( + "overwrite", + message=( + "Delete the repo's current directory and clone the repo in" + " the directory?" + ), + ) + ] + ) + + if not answers or not t.cast(bool, answers["overwrite"]): return True + rmtree(repo_dir) + retry_delay, max_retries = 1, 5 for retry_index in range(max_retries): try: @@ -246,12 +315,11 @@ def main() -> None: """Entry point.""" colorama_init() - namespace = get_namespace() + print_intro() submodules = read_submodules() - if not namespace.skip_login: - login_to_github() + login_to_github() error = False @@ -267,42 +335,14 @@ def main() -> None: cloned_repo = False if forked_repo: - cloned_repo = clone_repo( - name, - submodule.path, - namespace.overwrite_clone, - ) + cloned_repo = clone_repo(name, submodule.path) view_repo(name) if not error and (not forked_repo or not cloned_repo): error = True - print() - print( - Style.BRIGHT - + Fore.RED - + "💥💣💥 Finished with errors. 💥💣💥" - + Style.RESET_ALL - + "\n\n" - + "This may not be an issue and may be occurring because you've run" - + " this setup script before. Please read the above logs to discover if" - + " further action is required." - + "\n\n" - + "If you require help, please reach out to " - + generate_console_link( - "mailto:codeforlife@ocado.com", - "codeforlife@ocado.com", - ) - + "." - if error - else Style.BRIGHT - + Fore.GREEN - + "✨🍰✨ Finished without errors. ✨🍰✨" - + Style.RESET_ALL - + "\n\n" - + "Happy coding!" - ) + print_exit(error) if __name__ == "__main__": From 61c5c2687d9c3d86fc81b1c51444cf71b6e262cb Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 21 Oct 2024 14:56:20 +0000 Subject: [PATCH 04/15] use bright --- .submodules/setup/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index e6bc052e..2635fab5 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -303,7 +303,7 @@ def view_repo(name: str): stdout=subprocess.PIPE, ).stdout.decode("utf-8") except CalledProcessError: - print(Fore.YELLOW + "Failed to view repo." + Style.RESET_ALL) + print(Style.BRIGHT + Fore.YELLOW + "Failed to view repo." + Style.RESET_ALL) return From 3dd24a4dd59da4cc66dd3b3117d78582e5abf5e5 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 21 Oct 2024 14:59:21 +0000 Subject: [PATCH 05/15] comment --- .submodules/setup/__main__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 2635fab5..8bd058c9 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -33,6 +33,7 @@ class Submodule: def print_intro(): """Prints the Code For Life logo with ascii art.""" + # short hand M, C, Y = Fore.MAGENTA, Fore.CYAN, Fore.YELLOW print( From 8b7a0bee090afb4ca734cd100fefa5ca3d502411 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 21 Oct 2024 15:01:45 +0000 Subject: [PATCH 06/15] fixed colors --- .submodules/setup/__main__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 8bd058c9..d2e82cef 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -63,11 +63,12 @@ def print_exit(error: bool): """ print() print( - Style.BRIGHT + "💥💣💥 " + + Style.BRIGHT + Fore.RED - + "💥💣💥 Finished with errors. 💥💣💥" + + "Finished with errors." + Style.RESET_ALL - + "\n\n" + + " 💥💣💥\n\n" + "This may not be an issue and may be occurring because you've run" + " this setup script before. Please read the above logs to discover if" + " further action is required." @@ -79,11 +80,12 @@ def print_exit(error: bool): ) + "." if error - else Style.BRIGHT + else "✨🍰✨ " + + Style.BRIGHT + Fore.GREEN - + "✨🍰✨ Finished without errors. ✨🍰✨" + + "Finished without errors." + Style.RESET_ALL - + "\n\n" + + " ✨🍰✨\n\n" + "Happy coding!" ) From a086a78f7e4babbab87f66abb626c6bf39faaee2 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Mon, 21 Oct 2024 17:22:19 +0000 Subject: [PATCH 07/15] visit our site --- .submodules/setup/__main__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index d2e82cef..2ee6aac3 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -47,9 +47,15 @@ def print_intro(): {M}\\_____{C}\\___/ {Y}\\__,_|{M}\\___| {Y}|_| {M}\\___/{C}|_| {Y}|______{M}|_|{C}_| {Y}\\___| """ + Style.RESET_ALL - + "\nTo learn more, check out " + + "\nTo learn more, " + generate_console_link( - "https://docs.codeforlife.education/", "our documentation" + "https://docs.codeforlife.education/", + "read our documentation", + ) + + " and " + + generate_console_link( + "https://www.codeforlife.education/", + "visit our site", ) + ".\n" ) From fa907354772da9e9bde7b3f4ea0c34e487beb752 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 10:30:54 +0000 Subject: [PATCH 08/15] add instructions --- .submodules/setup/__main__.py | 43 +++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 2ee6aac3..1fc0d093 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -57,8 +57,47 @@ def print_intro(): "https://www.codeforlife.education/", "visit our site", ) - + ".\n" + + ".\n\n" + + "👇👀👇 " + + Style.BRIGHT + + Back.YELLOW + + "PLEASE READ INSTRUCTIONS" + + Style.RESET_ALL + + " 👇👀👇\n\n" + + "This script will help you setup your CFL dev container by:\n" + + " - forking each repo within our " + + generate_console_link( + "https://github.com/ocadotechnology/codeforlife-workspace", + "workspace", + ) + + " into your personal GitHub account\n" + + " - cloning each fork from your personal GitHub account into this" + + " container\n\n" + + "In a moment you will be asked to log into your personal GitHub" + + " account so that we may setup your CFL dev container as described" + + " above. Use your keyboard to select/input your option when prompted." + + "\n\n" + + Style.DIM + + "If you have any concerns about logging into your personal GitHub" + + " account, rest assured we don't perform any malicious actions with" + " it. You're welcome to read the source code of this script here: " + + "/codeforlife-workspace/.submodules/setup/__main__.py.\n\n" + + Style.RESET_ALL + + "👆👀👆 " + + Style.BRIGHT + + Back.YELLOW + + "PLEASE READ INSTRUCTIONS" + + Style.RESET_ALL + + " 👆👀👆\n" + ) + input( + "Press " + + Style.BRIGHT + + "Enter" + + Style.RESET_ALL + + " after you have read the instructions..." ) + print() def print_exit(error: bool): @@ -183,7 +222,7 @@ def login_to_github(): if not logged_in: subprocess.run( - ["gh", "auth", "login", "--web"], + ["gh", "auth", "login", "--web", "--git-protocol=https"], check=True, ) From 4dac2c20c6e3681f732cf7090519ceb4b935f856 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 10:33:46 +0000 Subject: [PATCH 09/15] newline --- .submodules/setup/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 1fc0d093..4e0040b1 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -97,7 +97,7 @@ def print_intro(): + Style.RESET_ALL + " after you have read the instructions..." ) - print() + print("\n") def print_exit(error: bool): From ded41a9bda33d5583d40012a5a38c2268ac2024d Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 10:54:05 +0000 Subject: [PATCH 10/15] grammar police --- .submodules/setup/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 4e0040b1..653f42dc 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -64,7 +64,7 @@ def print_intro(): + "PLEASE READ INSTRUCTIONS" + Style.RESET_ALL + " 👇👀👇\n\n" - + "This script will help you setup your CFL dev container by:\n" + + "This script will help you set up your CFL dev container by:\n" + " - forking each repo within our " + generate_console_link( "https://github.com/ocadotechnology/codeforlife-workspace", @@ -74,7 +74,7 @@ def print_intro(): + " - cloning each fork from your personal GitHub account into this" + " container\n\n" + "In a moment you will be asked to log into your personal GitHub" - + " account so that we may setup your CFL dev container as described" + + " account so that we may set up your CFL dev container as described" + " above. Use your keyboard to select/input your option when prompted." + "\n\n" + Style.DIM From 23711a0e1ee602f313955e09a80ba69fbee0a5dd Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 12:14:02 +0000 Subject: [PATCH 11/15] fixes --- .submodules/setup/__main__.py | 38 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 653f42dc..79146a50 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -9,6 +9,7 @@ import os import re import subprocess +import sys import typing as t from dataclasses import dataclass from pathlib import Path @@ -99,6 +100,18 @@ def print_intro(): ) print("\n") + answers = inquirer.prompt( + [ + inquirer.Confirm( + "exit", + message="Have you already set up your container and would like exit?", + ) + ] + ) + + if answers and t.cast(bool, answers["exit"]): + sys.exit() + def print_exit(error: bool): """Prints the exiting statement to the console. @@ -299,8 +312,10 @@ def clone_repo(name: str, path: str): rmtree(repo_dir) - retry_delay, max_retries = 1, 5 - for retry_index in range(max_retries): + max_attempts = 5 + retry_delay = 1 + retry_attempts = max_attempts - 1 + for attempt_index in range(max_attempts): try: subprocess.run( ["gh", "repo", "clone", name, repo_dir], @@ -312,16 +327,17 @@ def clone_repo(name: str, path: str): if os.path.isdir(repo_dir): rmtree(repo_dir) - print( - Style.BRIGHT - + Fore.YELLOW - + f"Retrying clone in {retry_delay} seconds." - + f" Attempt {retry_index + 1}/{max_retries}." - + Style.RESET_ALL - ) + if attempt_index != retry_attempts: + print( + Style.BRIGHT + + Fore.YELLOW + + f"Retrying clone in {retry_delay} seconds." + + f" Attempt {attempt_index + 1}/{retry_attempts}." + + Style.RESET_ALL + ) - sleep(retry_delay) - retry_delay *= 2 + sleep(retry_delay) + retry_delay *= 2 print(Style.BRIGHT + Fore.RED + "Failed to clone repo." + Style.RESET_ALL) From dd552dc49282f0838a75bdae9aecd80c33cc6cd9 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 12:15:39 +0000 Subject: [PATCH 12/15] remove spaces --- .submodules/setup/__main__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 79146a50..ff3dbf89 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -40,12 +40,12 @@ def print_intro(): print( Style.BRIGHT + f""" - {M}_____ {Y}_ ______ _ {M}_ {C}__ - {M}/ ____| {Y}| | | ____| | | {M}(_){C}/ _| - {M}| | {C}___ {Y}__| | {M}___ {Y}| |__ {M}___ {C}_ __ {Y}| | {M}_| {C}|_ {Y}___ - {M}| | {C}/ _ \\ {Y}/ _` |{M}/ _ \\ {Y}| __{M}/ _ \\{C}| '__| {Y}| | {M}| | {C}_{Y}/ _ \\ - {M}| |___{C}| (_) | {Y}(_| | {M}__/ {Y}| | {M}| (_) {C}| | {Y}| |____{M}| | {C}|{Y}| __/ - {M}\\_____{C}\\___/ {Y}\\__,_|{M}\\___| {Y}|_| {M}\\___/{C}|_| {Y}|______{M}|_|{C}_| {Y}\\___| + {M}_____ {Y}_ ______ _ {M}_ {C}__ + {M}/ ____| {Y}| | | ____| | | {M}(_){C}/ _| +{M}| | {C}___ {Y}__| | {M}___ {Y}| |__ {M}___ {C}_ __ {Y}| | {M}_| {C}|_ {Y}___ +{M}| | {C}/ _ \\ {Y}/ _` |{M}/ _ \\ {Y}| __{M}/ _ \\{C}| '__| {Y}| | {M}| | {C}_{Y}/ _ \\ +{M}| |___{C}| (_) | {Y}(_| | {M}__/ {Y}| | {M}| (_) {C}| | {Y}| |____{M}| | {C}|{Y}| __/ + {M}\\_____{C}\\___/ {Y}\\__,_|{M}\\___| {Y}|_| {M}\\___/{C}|_| {Y}|______{M}|_|{C}_| {Y}\\___| """ + Style.RESET_ALL + "\nTo learn more, " From 46dc3994c14fa410c2eb5f4a297d9df03544cd64 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 13:12:31 +0000 Subject: [PATCH 13/15] gh auth status --- .submodules/setup/__main__.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index ff3dbf89..28f2b4bf 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -202,17 +202,21 @@ def read_submodules() -> t.Dict[str, Submodule]: def login_to_github(): """Log into GitHub with the CLI. + https://cli.github.com/manual/gh_auth_status + https://cli.github.com/manual/gh_auth_logout https://cli.github.com/manual/gh_auth_login """ print(Style.BRIGHT + "Checking if you are logged into GitHub..." + Style.RESET_ALL) - status = subprocess.run( - ["gh", "auth", "status"], - check=True, - stdout=subprocess.PIPE, - ).stdout.decode("utf-8") + logged_in = True - logged_in = not status.startswith("You are not logged into any GitHub hosts") + try: + subprocess.run( + ["gh", "auth", "status"], + check=True, + ) + except CalledProcessError: + logged_in = False if logged_in: answers = inquirer.prompt( From 1efcf6780ce905c42248a573bf2c479a8757a599 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 13:18:35 +0000 Subject: [PATCH 14/15] feedback --- .submodules/setup/__main__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index 28f2b4bf..a55c634c 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -103,13 +103,13 @@ def print_intro(): answers = inquirer.prompt( [ inquirer.Confirm( - "exit", - message="Have you already set up your container and would like exit?", + "proceed", + message="Would you like to proceed with setting up your dev container?", ) ] ) - if answers and t.cast(bool, answers["exit"]): + if answers and not t.cast(bool, answers["proceed"]): sys.exit() From cd0c50549153453cf7ea12781ecfec766ba539e0 Mon Sep 17 00:00:00 2001 From: --global <--global> Date: Tue, 22 Oct 2024 13:21:47 +0000 Subject: [PATCH 15/15] remove new line --- .submodules/setup/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.submodules/setup/__main__.py b/.submodules/setup/__main__.py index a55c634c..e6f5f05e 100644 --- a/.submodules/setup/__main__.py +++ b/.submodules/setup/__main__.py @@ -98,7 +98,7 @@ def print_intro(): + Style.RESET_ALL + " after you have read the instructions..." ) - print("\n") + print() answers = inquirer.prompt( [