diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml new file mode 100644 index 0000000..e22665a --- /dev/null +++ b/.github/workflows/python-package-conda.yml @@ -0,0 +1,99 @@ +name: Python Package using Conda + +on: [push] + +jobs: + build-linux: + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.12 + uses: actions/setup-python@v3 + with: + python-version: '3.12' + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + + - name: Add extra channels + run: | + conda config --add channels conda-forge + conda config --add channels defaults + + # - name: Install dependencies + # run: | + # conda env update --file environment.yml --name base + - name: Lint with flake8 + run: | + conda install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 --exclude deprecated . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 --exclude deprecated . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Install current Python library + run: | + source activate base # Activate the conda environment + pip install -e . # Install the current Python library in editable mode + # pip install cloudmesh-vpn + - name: Test with pytest + run: | + conda install pytest + source activate base & pytest tests -rsx + + +# build-windows: +# runs-on: windows-latest +# strategy: +# max-parallel: 5 +# +# steps: +# # - uses: actions/checkout@v3 +# # - name: Set up Python 3.10 +# # uses: conda-incubator/setup-miniconda@v2 +# # with: +# # miniconda-version: "latest" +# - uses: actions/checkout@v3 +# - name: Set up Python 3.12 +# uses: actions/setup-python@v3 +# with: +# python-version: '3.12' +# # - name: Add conda to system path +# # run: | +# # # $CONDA is an environment variable pointing to the root of the miniconda directory +# # echo $CONDA/bin >> $GITHUB_PATH +# # - name: Add extra channels +# # run: | +# # conda config --add channels conda-forge +# # conda config --add channels defaults +# +# # - name: Install dependencies +# # run: | +# # conda env update --file environment.yml --name base +# - name: set up env3 +# run: | +# python -m venv ENV3 +# +# - name: Lint with flake8 +# run: | +# .\ENV3\Scripts\activate.ps1 +# pip install flake8 +# # stop the build if there are Python syntax errors or undefined names +# flake8 --exclude deprecated . --count --select=E9,F63,F7,F82 --show-source --statistics +# # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide +# flake8 --exclude deprecated . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics +# +# - name: Install current Python library +# run: | +# .\ENV3\Scripts\activate.ps1 +# pip install -e . # Install the current Python library in editable mode +# pip install cloudmesh-vpn +# - name: Test with pytest +# run: | +# .\ENV3\Scripts\activate.ps1 +# pip install pytest +# pytest tests -rsx diff --git a/pyproject.toml b/pyproject.toml index d4b4ed1..6f5736f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ dependencies = [ "toml", "docopt", "ordered_set", + "rich", "cloudmesh-common", ] diff --git a/src/cloudmesh/installer/installer.py b/src/cloudmesh/installer/installer.py index 466120b..18a983e 100644 --- a/src/cloudmesh/installer/installer.py +++ b/src/cloudmesh/installer/installer.py @@ -141,16 +141,20 @@ from pprint import pprint import platform -import colorama +# import colorama import requests from cloudmesh.common.StopWatch import StopWatch from cloudmesh.common.console import Console from cloudmesh.common.util import banner from cloudmesh.common.util import readfile from cloudmesh.common.systeminfo import os_is_windows -from cloudmesh.installer.__version__ import version as installer_version +from cloudmesh.installer import __version__ as installer_version from cloudmesh.installer.bundle import * -from colorama import Fore, Style +# from colorama import Fore, Style +from rich.console import Console as RichConsole +from rich.text import Text +from rich.table import Table + from docopt import docopt from ordered_set import OrderedSet from tabulate import tabulate @@ -160,6 +164,9 @@ debug = False benchmark = False +console = RichConsole() +print = console.print + def os_is_pi(): """ @@ -192,7 +199,8 @@ def run(command, verbose=True): except subprocess.CalledProcessError as err: if verbose: print() - print(Fore.RED + f"ERROR: {err}") + # print(Fore.RED + f"ERROR: {err}") + print(f"ERROR: {err}", style="bold red") sys.exit(1) return output.decode("utf-8") @@ -274,13 +282,16 @@ def url(repo, protocol="https"): return f"{prefix}cloudmesh/{repo}" @staticmethod - def error_color(error="ERROR"): + def error_color(error="ERROR") -> str: if error == "ERROR": - color = Fore.RED + # color = Fore.RED + color = "bold red" elif error == "WARNING": - color = Fore.MAGENTA + # color = Fore.MAGENTA + color = "bold magenta" elif error == "INFO": - color = Fore.MAGENTA + # color = Fore.MAGENTA + color = "bold magenta" else: color = "" return color @@ -304,7 +315,8 @@ def clone(repos, error="INFO", protocol="https"): color = Git.error_color(error) print( - color + f" {error}: not downloaded as repo already exists." + f" {error}: not downloaded as repo already exists.", + style=color, ) @staticmethod @@ -324,14 +336,17 @@ def command(repos, name, ok_msg="nothing to commit, working tree clean", r=False try: os.chdir(repo) except FileNotFoundError: - print(Fore.RED + "ERROR:", repo, "not found") + # print(Fore.RED + "ERROR:", repo, "not found") + print("ERROR:", repo, "not found", style="bold red") result = run(f"git {name}", verbose=False) if ok_msg in result: - print(Fore.GREEN + "... ok") + # print(Fore.GREEN + "... ok") + print("... ok", style="bold green") else: print() - print(Fore.RED + result) + # print(Fore.RED + result) + print(result, style="bold red") os.chdir("../") @staticmethod @@ -344,15 +359,18 @@ def _command(repos, command, ok_msg="Uploading", verbose=False, r=False): try: os.chdir(repo) except FileNotFoundError: - print(Fore.RED + "ERROR:", repo, "not found") + # print(Fore.RED + "ERROR:", repo, "not found") + print("ERROR:", repo, "not found", style="bold red") result = run(f"{command}", verbose=False) if ok_msg in result: - print(Fore.GREEN + "... ok") + # print(Fore.GREEN + "... ok") + print("... ok", style="bold green") else: print() - print(Fore.RED + result) + # print(Fore.RED + result) + print(result, style="bold red") if verbose: print() @@ -426,18 +444,20 @@ def install(repos, dev=False, protocol="https"): def yn_question(msg): while True: - query = input(Fore.RED + msg) + print(msg, style="bold red") + query = input() answer = query.lower().strip() if query == "" or answer not in ["yes", "n"]: print("Please answer with yes/n!") else: break - print(Fore.RESET) + # print(Fore.RESET) return answer == "yes" def RED(msg): - print(Fore.RED + msg + Fore.RESET) + # print(Fore.RED + msg + Fore.RESET) + print(msg, style="bold red") def ERROR(msg): @@ -474,7 +494,7 @@ def get_all_repos(): def check_for_bundle(bundle): if bundle is None: - ERROR("No bundle specified.") + ERROR("No bundle specified.") sys.exit(1) elif not ((bundle in repos) or (bundle in ["cloudmesh", "all"])): ERROR(f"The bundle `{bundle}` does not exist") @@ -489,13 +509,22 @@ def bundle_list(repos): return result -def bundle_elements(bundle): - block = Fore.BLUE + f"\n{bundle}:\n" + Fore.RESET - elements = " ".join(repos[bundle]) - block = block + textwrap.indent( - textwrap.fill(elements, 70, break_on_hyphens=False), " " - ) - return block +def bundle_elements(bundle) -> Table: + table = Table(title="Cloudmesh Bundles", + show_lines=True, + # show_header=True, + header_style="black", + title_style="bold black", + ) + + table.add_column("Bundle", style="cyan") + table.add_column("Repos", style="magenta") + + for bundle in repos: + repos_list = repos[bundle] + if repos_list: # check if the list is not empty + table.add_row(bundle, ', '.join(repos_list)) # add the repos as a comma-separated list + return table def main(): @@ -515,7 +544,7 @@ def main(): if arguments["--ssh"]: protocol = "ssh" - colorama.init(autoreset=True) + # colorama.init(autoreset=True) if debug: banner("BEGIN ARGUMENTS") @@ -590,12 +619,9 @@ def _get_bundles(): elif arguments["list"] and not arguments["BUNDLE"] and not arguments["--git"]: if not arguments["--short"]: - banner("Cloudmesh Bundles") - block = "" - for bundle in repos: - block = block + bundle_elements(bundle) - - print(block) + # banner("Cloudmesh Bundles") + table = bundle_elements(bundle) + print(table) else: print(bundle_list(repos)) @@ -624,7 +650,7 @@ def _get_bundles(): native = hasattr(sys, "real_prefix") executable = sys.executable if native: - banner(WARNING, c=Fore.RED) + banner(WARNING, color="red") print() RED( "You are likely not running in a venv. " @@ -649,7 +675,8 @@ def _get_bundles(): packages = repos[bundle] for package in packages: - undefined = Fore.RED + "not found" + Style.RESET_ALL + # undefined = Fore.RED + "not found" + Style.RESET_ALL + undefined = Text("not found", style="red") entry = [ package, undefined, # "git": @@ -843,7 +870,7 @@ def _get_bundles(): environment = arguments["--venv"] print() - banner(WARNING, c=Fore.RED) + banner(WARNING, c="red") RED( textwrap.dedent( @@ -892,7 +919,7 @@ def _get_bundles(): print(f" found -> {egg}") else: print() - banner(WARNING, c=Fore.RED) + banner(WARNING, c="red") RED( textwrap.dedent( @@ -911,8 +938,7 @@ def _get_bundles(): print() if not yn_question( - Fore.RED - + "WARNING: Removing listed files. Do you really want to continue. yes/n)? " + "WARNING: Removing listed files. Do you really want to continue. yes/n)? " ): sys.exit(1) diff --git a/tests/test_installer.py b/tests/test_installer.py index a20a91d..a1ac1db 100644 --- a/tests/test_installer.py +++ b/tests/test_installer.py @@ -8,7 +8,6 @@ import os import pytest -from cloudmesh.common.util import readfile from cloudmesh.common.Shell import Shell from cloudmesh.common.util import banner @@ -31,7 +30,7 @@ def test_create_dir(self): def test_version(self): banner("test_version") - cmd = "cloudmesh-installer version" + cmd = "cmsi version" result = Shell.run(cmd) print(result) print() @@ -41,7 +40,7 @@ def test_info(self): banner("test_info") print("PWD:", os.getcwd()) - cmd = "cloudmesh-installer info" + cmd = "cmsi info" result = Shell.run(cmd) print(result) print() @@ -50,7 +49,7 @@ def test_info(self): def test_list(self): banner("list") - cmd = "cloudmesh-installer list" + cmd = "cmsi list" result = Shell.run(cmd) print(result) assert "cloudmesh-common" in result @@ -59,15 +58,18 @@ def test_non_existing(self): banner("test_non_existing") print("PWD:", os.getcwd()) - cmd = "cloudmesh-installer git clone WRONG" - result = Shell.run(cmd) - assert True + cmd = "cmsi git clone WRONG" + try: + result = Shell.run(cmd) + assert False + except RuntimeError: + assert True def test_clone_community(self): banner("test_clone_community") print("PWD:", os.getcwd()) - cmd = "cloudmesh-installer git clone community" + cmd = "cmsi git clone community" result = Shell.run(cmd) print(result) assert os.path.isdir("cloudmesh-community.github.io") @@ -76,7 +78,7 @@ def test_clone_cms(self): banner("test_clone_cms") print("PWD:", os.getcwd()) - cmd = "cloudmesh-installer git clone cms" + cmd = "cmsi git clone cms" result = Shell.run(cmd) print("RESULT:", result) @@ -96,10 +98,15 @@ def test_install_cms(self): banner("test_install_cms") print("PWD:", os.getcwd()) - cmd = "cloudmesh-installer install cms" + cmd = "cmsi install cms" result = Shell.run(cmd) print("RESULT:", result) - assert os.path.isdir("cloudmesh-cmd5/cloudmesh_cmd5.egg-info") + # Try to import the module and check if it raises an ImportError + try: + import cloudmesh.shell + assert True + except ImportError: + assert False def test_cms_help(self): banner("test_cms_help")