diff --git a/dem/cli/command/info_cmd.py b/dem/cli/command/info_cmd.py index edd8d81..0fa0218 100644 --- a/dem/cli/command/info_cmd.py +++ b/dem/cli/command/info_cmd.py @@ -6,49 +6,147 @@ from dem.cli.console import stdout, stderr from dem.core.platform import Platform from rich.table import Table +import typer image_status_messages = { - ToolImage.NOT_AVAILABLE: "[red]Error: Required image is not available![/]", - ToolImage.LOCAL_ONLY: "Image is available locally.", - ToolImage.REGISTRY_ONLY: "Image is available in the registry.", - ToolImage.LOCAL_AND_REGISTRY: "Image is available locally and in the registry.", + ToolImage.NOT_AVAILABLE: "[red]Error: not available![/]", + ToolImage.LOCAL_ONLY: "Local", + ToolImage.REGISTRY_ONLY: "Registry", + ToolImage.LOCAL_AND_REGISTRY: "Local and Registry", } -def print_info(dev_env: DevEnv) -> None: - """ Print information about the given Development Environment. +def print_local_dev_env_info(dev_env: DevEnv) -> None: + """ Print information about the given local Development Environment. Args: dev_env -- the Development Environment to print information about """ tool_info_table = Table() tool_info_table.add_column("Image") - tool_info_table.add_column("Status") + tool_info_table.add_column("Availability") - for tool_image in dev_env.tool_images: + for tool_image in sorted(dev_env.tool_images, key=lambda x: x.name): tool_info_table.add_row(tool_image.name, image_status_messages[tool_image.availability]) + if dev_env.is_installed: + installation_status = "[green]Installed[/]" + else: + installation_status = "Not installed" + stdout.print(f"\n[bold]Development Environment: {dev_env.name}[/]\n") + stdout.print(f"Installation state: {installation_status}\n") stdout.print(tool_info_table) -def execute(platform: Platform, arg_dev_env_name: str) -> None: - """ Print information about the given Development Environment. + if dev_env.is_installed and dev_env.get_tool_image_status() == DevEnv.Status.REINSTALL_NEEDED: + stderr.print("\n[red]Error: Incomplete local install! The Dev Env must be reinstalled![/]") + + if dev_env.get_tool_image_status() == DevEnv.Status.UNAVAILABLE_IMAGE: + stderr.print("\n[red]Error: Required image could not be found either locally or in the registry![/]") + +def local_info(platform: Platform, dev_env_name: str) -> None: + """ Gather and print information about the given local Development Environment. Args: platform -- the platform - arg_dev_env_name -- the name of the Development Environment to print information about + dev_env_name -- the name of the Development Environment to print information about + + Raises: + typer.Abort -- if the Development Environment is unknown """ platform.assign_tool_image_instances_to_all_dev_envs() - dev_env = platform.get_dev_env_by_name(arg_dev_env_name) + dev_env = platform.get_dev_env_by_name(dev_env_name) if dev_env is None: - for catalog in platform.dev_env_catalogs.catalogs: + stderr.print(f"[red]Error: Unknown Development Environment: {dev_env_name}[/]\n") + raise typer.Abort() + + print_local_dev_env_info(dev_env) + +def print_cat_dev_env_info(dev_env: DevEnv, cat_name: str) -> None: + """ Print information about the given catalog Development Environment. + + Args: + dev_env -- the Development Environment to print information about + cat_name -- the name of the catalog the Development Environment belongs to + """ + tool_info_table = Table() + tool_info_table.add_column("Image") + tool_info_table.add_column("Availability") + + for tool_image in sorted(dev_env.tool_images, key=lambda x: x.name): + tool_info_table.add_row(tool_image.name, + image_status_messages[tool_image.availability]) + stdout.print(f"\n[bold]Development Environment: {dev_env.name}[/]\n") + stdout.print(f"Catalog: {cat_name}\n") + stdout.print(tool_info_table) + + if dev_env.get_tool_image_status() == DevEnv.Status.UNAVAILABLE_IMAGE: + stderr.print("\n[red]Error: Required image could not be found in the registry![/]") + +def cat_dev_env_info(platform: Platform, dev_env_name: str, selected_cats: list[str]) -> None: + """ Gather and print information about the given catalog Development Environment. + + Args: + platform -- the platform + dev_env_name -- the name of the Development Environment to print information about + selected_cats -- the selected catalog names, empty list means all catalogs + """ + for catalog in platform.dev_env_catalogs.catalogs: + if catalog.name in selected_cats or not selected_cats: catalog.request_dev_envs() - dev_env = catalog.get_dev_env_by_name(arg_dev_env_name) + dev_env = catalog.get_dev_env_by_name(dev_env_name) if dev_env: dev_env.assign_tool_image_instances(platform.tool_images) + print_cat_dev_env_info(dev_env, catalog.name) break + else: + stderr.print(f"[red]Error: Unknown Development Environment: {dev_env_name}[/]\n") - if dev_env is None: - stderr.print(f"[red]Error: Unknown Development Environment: {arg_dev_env_name}[/]\n") +def selected_cats_info(platform: Platform, dev_env_name: str, selected_cats: list[str]) -> None: + """ Print information about the given Development Environment from the selected catalogs. + + Args: + platform -- the platform + dev_env_name -- the name of the Development Environment to print information about + selected_cats -- the selected catalog names + + Raises: + typer.Abort -- if the selected catalog is unknown + """ + available_cats = set([cat.name for cat in platform.dev_env_catalogs.catalogs]) + selected_cats = set(selected_cats) + + if not selected_cats.issubset(available_cats): + stderr.print(f"[red]Error: Unknown catalog(s): {', '.join(selected_cats - available_cats)}[/]\n") + raise typer.Abort() + + cat_dev_env_info(platform, dev_env_name, selected_cats) + +def all_cats_info(platform: Platform, dev_env_name: str) -> None: + """ Print information about the given Development Environment from all catalogs. + + Args: + platform -- the platform + dev_env_name -- the name of the Development Environment to print information about + """ + cat_dev_env_info(platform, dev_env_name, []) + +def execute(platform: Platform, arg_dev_env_name: str, cat: bool, selected_cats: list[str]) -> None: + """ Print information about the given Development Environment. + + Args: + platform -- the platform + arg_dev_env_name -- the name of the Development Environment to print information about + cat -- the flag to print information about the Development Environment from the catalogs + selected_cats -- the selected catalog names + + Raises: + typer.Abort -- if the Development Environment is unknown or if the selected catalog is + unknown + """ + if not cat: + local_info(platform, arg_dev_env_name) + elif selected_cats: + selected_cats_info(platform, arg_dev_env_name, selected_cats) else: - print_info(dev_env) \ No newline at end of file + all_cats_info(platform, arg_dev_env_name) \ No newline at end of file diff --git a/dem/cli/command/list_tools_cmd.py b/dem/cli/command/list_tools_cmd.py index 43ba9f8..1400dd3 100644 --- a/dem/cli/command/list_tools_cmd.py +++ b/dem/cli/command/list_tools_cmd.py @@ -29,12 +29,12 @@ def list_local_tools(platform: Platform) -> None: # tool_image[0] is the name of the tool image and tool_image[1] is the ToolImage instance stdout.print(f" {tool_image[0]}") -def update_tools_from_selected_regs(platform: Platform, specified_regs: list[str]) -> None: +def update_tools_from_selected_regs(platform: Platform, selected_regs: list[str]) -> None: """ Update the tools from the selected registries only. Args: platform -- the Platform - specified_regs -- the selected registry names + selected_regs -- the selected registry names Exceptions: typer.Abort -- if an unknown registry is specified @@ -44,14 +44,14 @@ def update_tools_from_selected_regs(platform: Platform, specified_regs: list[str platform.disable_tool_update = True available_regs = set([reg["name"] for reg in platform.config_file.registries]) - specified_regs = set(specified_regs) + selected_regs = set(selected_regs) - if not specified_regs.issubset(available_regs): - for unkown_reg in specified_regs - available_regs: + if not selected_regs.issubset(available_regs): + for unkown_reg in selected_regs - available_regs: stderr.print(f"[red]Error: Registry {unkown_reg} is not available![/]") raise typer.Abort() - platform.tool_images.update(reg_selection=specified_regs) + platform.tool_images.update(reg_selection=selected_regs) def list_tools_from_regs(platform: Platform, table: Table) -> None: """ List the available tools from the registries. diff --git a/dem/cli/main.py b/dem/cli/main.py index 7e78416..936f635 100644 --- a/dem/cli/main.py +++ b/dem/cli/main.py @@ -112,16 +112,23 @@ def list_tools(reg: Annotated[bool, typer.Option(help="List the available tools else: raise InternalError("Error: The platform hasn't been initialized properly!") -@typer_cli.command() +@typer_cli.command(context_settings={"allow_extra_args": True}) def info(dev_env_name: Annotated[str, typer.Argument(help="Name of the Development Environment to get info about.", - autocompletion=autocomplete_dev_env_name)]) -> None: + autocompletion=autocomplete_dev_env_name)], + cat: Annotated[bool, typer.Option(help="Get the Dev Env from the catalogs")] = False, + ctx: Annotated[typer.Context, typer.Option()] = None) -> None: """ Get information about the specified Development Environment available locally or in the catalogs. + --cat: DEM will search for the Dev Env in the catalogs and will print the details of the first + match. You can specifiy the catalogs' name to search in after this option. If no catalog is + specified, all the available catalogs will be used. If the Dev Env is not found in the catalogs, + an error message will be printed. + Note: Autocomplete only works with the locally avialable Dev Envs. """ - if platform: - info_cmd.execute(platform, dev_env_name) + if platform and ctx: + info_cmd.execute(platform, dev_env_name, cat, ctx.args) else: raise InternalError("Error: The platform hasn't been initialized properly!") diff --git a/dem/core/dev_env.py b/dem/core/dev_env.py index 6fbc6e9..8988173 100755 --- a/dem/core/dev_env.py +++ b/dem/core/dev_env.py @@ -71,8 +71,6 @@ def assign_tool_image_instances(self, tool_images: ToolImages) -> None: def get_tool_image_status(self) -> Status: """ Get the status of the Tool Images. - Can only be used if the Dev Env is insalled. - This method checks the availability of the assigned Tool Images. If at least one of the Tool Images is unkonwn: NOT_AVAILABLE. If at least one of the Tool Images is only available in the registry: REINSTALL_NEEDED. diff --git a/docs/commands.md b/docs/commands.md index 6394948..028c668 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,46 +1,56 @@ +The commands are grouped by their functionality. The commands are listed in alphabetical order. +The groupings are: + +- Development Environment management +- Development Environment Catalog management +- Registry management +- Host management + # Development Environment management -## **`dem list [OPTIONS] [*CATALOG_NAMES]`** +## **`dem assign DEV_ENV_NAME, [PROJECT_PATH]`** -List the locally available Dev Envs. +Assign a Development Environment to a project. -Options: +If the project already has a Development Environment assigned, the user will be asked if they want to +overwrite it or not. -`--cat`: List the available Dev Envs from the catalogs. Specify the catalogs' name to list the Dev -Envs from. More then one catalog can be specified. If no catalog is specified, all the available -catalogs will be used. +Projects that have a Development Environment assigned, can be initialized with the `init` command. -Examples: +Arguments: -- `dem list` List the locally available Dev Envs. -- `dem list --cat` List all the Dev Envs from all the available catalogs. -- `dem list --cat catalog1 catalog2` List all the Dev Envs from the catalog1 and catalog2. +`DEV_ENV_NAME` Name of the Development Environment to assign. [required] + +`[PROJECT_PATH]` Path of the project to assign the Development Environment to. If not set, the current +working directory will be used. --- -## **`dem list-tools [OPTIONS] [*REGISTRY_NAMES]`** +## **`dem clone DEV_ENV_NAME`** -List the available tools. +Clone a Development Environment descriptor from the catalogs. -Options: +Only the Development Environment descriptor will be cloned, the required tool images won't be pulled. +If a Development Environment with the same name has been already available on the host PC, the user +will be asked if they want to overwrite it or not. -`--reg`: List the available tools from the registries. Specify the registries' name to list the -tools from. More then one registry can be specified. If no registry is specified, all the available -registries will be used. +:information_source: After cloning, the Development Environment can be installed with the `install` command. -Examples: +Arguments: -- `dem list-tools` List the locally available tools. -- `dem list-tools --reg` List all the tools from all the available registries. -- `dem list-tools --reg registry1 registry2` List all the tools from the registry1 and registry2. +`DEV_ENV_NAME` Clone the descriptor of the Dev Env. [required] -## **`dem info DEV_ENV_NAME`** +--- -Get information about the specified Development Environment. +## **`dem cp DEV_ENV_NAME NEW_DEV_ENV_NAME`** + +Create a copy of an existing local Development Environment. Arguments: -`DEV_ENV_NAME` Name of the Development Environment to get info about. [required] +`DEV_ENV_NAME` Name of the Development Environment to copy. [required] + +`NEW_DEV_ENV_NAME` Name of the New Development Environment. [required] --- @@ -70,79 +80,83 @@ Arguments: --- -## **`dem clone DEV_ENV_NAME`** - -Clone a Development Environment descriptor from the catalogs. - -Only the Development Environment descriptor will be cloned, the required tool images won't be pulled. -If a Development Environment with the same name has been already available on the host PC, the user -will be asked if they want to overwrite it or not. +## **`dem delete DEV_ENV_NAME`** -:info: After cloning, the Development Environment can be installed with the `install` command. +Delete the Dev Env descriptor from the local descriptor storage. +If the Dev Env is installed, the user will be asked whether they want to uninstall it. Arguments: -`DEV_ENV_NAME` Clone the descriptor of the Dev Env. [required] +`DEV_ENV_NAME` Name of the Development Environment to delete. [required] --- -## **`dem rename DEV_ENV_NAME NEW_DEV_ENV_NAME`** - -Rename the Development Environment. - -Arguments: +## **`dem export DEV_ENV_NAME [PATH_TO_EXPORT]`** -`DEV_ENV_NAME` Name of the Development Environment to rename. [required] -`NEW_DEV_ENV_NAME` The new name. [required] +Export a Development Environment descriptor in JSON format to a text file. This file can be imported with the `load` command on another host. ---- +The way the file gets named can be set by the PATH_TO_EXPORT argument: -## **`dem modify DEV_ENV_NAME`** +1. If it's not set, the file gets saved to the current directory with the name of the Development +Environment and without extension. +2. If only a name is set, the file gets saved with that name to the current directory, optionally +with the set extension. +3. If the argument is a directory path, the file gets saved there with the name of the Development +Environment, without extension. +4. If the argument is a path with the file name, then the exported content gets saved into that file. +The extension can be set with the file name. -Modify a Development Environment descriptor available from the local descriptor storage (catalog). +!!! Note -Running this command will open up the Dev Env Settings Window, prefilled with the current selection: + The exported file only contains the Development Environment descriptor in JSON format. For a + successful import the DEM needs access to all the registries where the required images are + stored. -![Dev Env Settings Window](wp-content/dev_env_settings_window.png) +Arguments: -The table on the left shows the available tool types. Select the ones you want to use in the -Development Environment. You can navigate with the :material-arrow-up: and :material-arrow-down: or -:material-alpha-k: and :material-alpha-j: keys. Move the cursor to the tool image you would like to -select or deselect and press the :material-keyboard-return:. -On the right side, you can see the tool images that are selected. +`DEV_ENV_NAME` The name of the Development Environment to export. -When the Dev Env is ready, press :material-keyboard-return: on the `save` button. +`[PATH_TO_EXPORT]` Where to save the exported descriptor in JSON format. If not set, the current +directory will be used. -!!! info +--- - After the modification, the Development Environment can be installed with the `install` command. +## **`dem info DEV_ENV_NAME [OPTIONS] [*CATALOG_NAMES]`** -Arguments: +Get information about the specified Development Environment available locally or in the catalogs. -`DEV_ENV_NAME` Name of the Development Environment to modify. [required] +`--cat`: DEM will search for the Dev Env in the catalogs and will print the details of the first +match. You can specifiy the catalogs' name to search in after this option. If no catalog is +specified, all the available catalogs will be used. If the Dev Env is not found in the catalogs, +an error message will be printed. ---- +:information_source: Autocomplete only works with the locally avialable Dev Envs. -## **`dem delete DEV_ENV_NAME`** +Arguments: -Delete the Dev Env descriptor from the local descriptor storage. -If the Dev Env is installed, the user will be asked whether they want to uninstall it. +`DEV_ENV_NAME` Name of the Development Environment to get info about. [required] +`[OPTIONS]` --cat: Search in the catalogs. [optional] +`[*CATALOG_NAMES]` Catalogs to search in. [optional -Arguments: +Examples: -`DEV_ENV_NAME` Name of the Development Environment to delete. [required] +- `dem info dev_env_name` Get information about the locally available Development Environment. +- `dem info dev_env_name --cat` Get information about the Development Environment from the catalogs. +- `dem info dev_env_name --cat catalog1 catalog2` Get information about the Development Environment from the catalog1 and catalog2. --- -## **`dem cp DEV_ENV_NAME NEW_DEV_ENV_NAME`** +## **`dem init [PROJECT_PATH]`** -Create a copy of an existing local Development Environment. +Initialize a project with the assigned Development Environment. -Arguments: +:information_source: After the initialization, the Development Environment can be installed with the `install` +command. -`DEV_ENV_NAME` Name of the Development Environment to copy. [required] +Arguments: -`NEW_DEV_ENV_NAME` Name of the New Development Environment. [required] +`PROJECT_PATH` Path of the project to initialize. If not set, the current working directory will be +used. --- @@ -159,128 +173,144 @@ Arguments: --- -## **`dem uninstall DEV_ENV_NAME`** +## **`dem list [OPTIONS] [*CATALOG_NAMES]`** -Uninstall the selected Development Environment. Sets the installed flag to False. DEM checks whether -a tool image is required or not by any of the remaining installed local Development Environments. In -case the tool image is not required anymore, the DEM tries to delete it. +List the locally available Dev Envs. + +Options: + +`--cat`: List the available Dev Envs from the catalogs. Specify the catalogs' name to list the Dev +Envs from. More then one catalog can be specified. If no catalog is specified, all the available +catalogs will be used. Arguments: -`DEV_ENV_NAME` Name of the Development Environment to uninstall. [required] +`[OPTIONS]` --cat: List the Dev Envs from the catalogs. [optional] +`[*CATALOG_NAMES]` Catalogs to list the Dev Envs from. [optional] + +Examples: + +- `dem list` List the locally available Dev Envs. +- `dem list --cat` List all the Dev Envs from all the available catalogs. +- `dem list --cat catalog1 catalog2` List all the Dev Envs from the catalog1 and catalog2. --- -## **`dem assign DEV_ENV_NAME, [PROJECT_PATH]`** +## **`dem list-tools [OPTIONS] [*REGISTRY_NAMES]`** -Assign a Development Environment to a project. +List the available tools. -If the project already has a Development Environment assigned, the user will be asked if they want to -overwrite it or not. +Options: -Projects that have a Development Environment assigned, can be initialized with the `init` command. +`--reg`: List the available tools from the registries. Specify the registries' name to list the +tools from. More then one registry can be specified. If no registry is specified, all the available +registries will be used. Arguments: -`DEV_ENV_NAME` Name of the Development Environment to assign. [required] +`[OPTIONS]` --reg: List the tools from the registries. [optional +`[*REGISTRY_NAMES]` Registries to list the tools from. [optional] -`[PROJECT_PATH]` Path of the project to assign the Development Environment to. If not set, the current -working directory will be used. +Examples: + +- `dem list-tools` List the locally available tools. +- `dem list-tools --reg` List all the tools from all the available registries. +- `dem list-tools --reg registry1 registry2` List all the tools from the registry1 and registry2. --- -## **`dem init [PROJECT_PATH]`** +## **`dem load PATH_TO_DEV_ENV`** -Initialize a project with the assigned Development Environment. +Imports a Development Environment. -:info: After the initialization, the Development Environment can be installed with the `install` -command. +!!! Note + + The file to import only contains the Development Environment descriptor. For a successful import + the DEM needs access to all the registries where the required images are stored. + +:information_source: After the import, the Development Environment can be installed with the `install` command. Arguments: -`PROJECT_PATH` Path of the project to initialize. If not set, the current working directory will be -used. +`PATH_TO_DEV_ENV` Path of the JSON file to import. Can be an absolute path or a relative path to the +current directory. --- -## **`dem run DEV_ENV_NAME *`** - -:warning: Experimental feature! -Run a container in the context of a Development Environment. +## **`dem modify DEV_ENV_NAME`** -This command works the same way as the `docker run`, but with some restrictions, and the first -argument is the name of the Development Environment. +Modify a Development Environment descriptor available from the local descriptor storage (catalog). -:warning: The supported docker run options: -`-p, --name, -v, --privileged, --rm, --name, -d` -See the [Docker documentation](https://docs.docker.com/engine/reference/commandline/run/) for more -info. +Running this command will open up the Dev Env Settings Window, prefilled with the current selection: -Arguments: +![Dev Env Settings Window](wp-content/dev_env_settings_window.png) -`DEV_ENV_NAME` Name of the Development Environment. [required] +The table on the left shows the available tool types. Select the ones you want to use in the +Development Environment. You can navigate with the :material-arrow-up: and :material-arrow-down: or +:material-alpha-k: and :material-alpha-j: keys. Move the cursor to the tool image you would like to +select or deselect and press the :material-keyboard-return:. +On the right side, you can see the tool images that are selected. -`*` Variable-length argument list that will be passed to the `docker run` command. +When the Dev Env is ready, press :material-keyboard-return: on the `save` button. ---- +!!! info -## **`dem export DEV_ENV_NAME [PATH_TO_EXPORT]`** + After the modification, the Development Environment can be installed with the `install` command. -Export a Development Environment descriptor in JSON format to a text file. This file can be imported with the `load` command on another host. +Arguments: -The way the file gets named can be set by the PATH_TO_EXPORT argument: +`DEV_ENV_NAME` Name of the Development Environment to modify. [required] -1. If it's not set, the file gets saved to the current directory with the name of the Development -Environment and without extension. -2. If only a name is set, the file gets saved with that name to the current directory, optionally -with the set extension. -3. If the argument is a directory path, the file gets saved there with the name of the Development -Environment, without extension. -4. If the argument is a path with the file name, then the exported content gets saved into that file. -The extension can be set with the file name. +--- -!!! Note +## **`dem rename DEV_ENV_NAME NEW_DEV_ENV_NAME`** - The exported file only contains the Development Environment descriptor in JSON format. For a - successful import the DEM needs access to all the registries where the required images are - stored. +Rename the Development Environment. Arguments: -`DEV_ENV_NAME` The name of the Development Environment to export. - -`[PATH_TO_EXPORT]` Where to save the exported descriptor in JSON format. If not set, the current -directory will be used. +`DEV_ENV_NAME` Name of the Development Environment to rename. [required] +`NEW_DEV_ENV_NAME` The new name. [required] --- -## **`dem load PATH_TO_DEV_ENV`** +## **`dem run DEV_ENV_NAME *`** -Imports a Development Environment. +:warning: Experimental feature! -!!! Note +Run a container in the context of a Development Environment. - The file to import only contains the Development Environment descriptor. For a successful import - the DEM needs access to all the registries where the required images are stored. +This command works the same way as the `docker run`, but with some restrictions, and the first +argument is the name of the Development Environment. -:info: After the import, the Development Environment can be installed with the `install` command. +:warning: The supported docker run options: +`-p, --name, -v, --privileged, --rm, --name, -d` +See the [Docker documentation](https://docs.docker.com/engine/reference/commandline/run/) for more +info. Arguments: -`PATH_TO_DEV_ENV` Path of the JSON file to import. Can be an absolute path or a relative path to the -current directory. +`DEV_ENV_NAME` Name of the Development Environment. [required] + +`*` Variable-length argument list that will be passed to the `docker run` command. --- -# Development Environment Catalog management +## **`dem uninstall DEV_ENV_NAME`** -## **`dem list-cat`** +Uninstall the selected Development Environment. Sets the installed flag to False. DEM checks whether +a tool image is required or not by any of the remaining installed local Development Environments. In +case the tool image is not required anymore, the DEM tries to delete it. -List the available catalogs. +Arguments: + +`DEV_ENV_NAME` Name of the Development Environment to uninstall. [required] --- +# Development Environment Catalog management + ## **`dem add-cat NAME URL`** Add a new catalog. @@ -305,14 +335,14 @@ Arguments: --- -# Registry management - -## **`dem list-reg`** +## **`dem list-cat`** -List the available registries. +List the available catalogs. --- +# Registry management + ## **`dem add-reg NAME URL`** Add a new registry. @@ -341,6 +371,12 @@ Arguments: `NAME` Name of the registry to delete. [required] +## **`dem list-reg`** + +List the available registries. + +--- + # Host management ## **`dem add-host NAME ADDRESS`** @@ -355,16 +391,16 @@ Arguments: --- -## **`dem list-host`** - -List the available hosts from the config file. - ---- - ## **`dem del-host NAME`** Delete a host from the config file. Arguments: -`NAME` Name of the host to delete. [required] \ No newline at end of file +`NAME` Name of the host to delete. [required] + +## **`dem list-host`** + +List the available hosts from the config file. + +--- diff --git a/tests/cli/test_info_cmd.py b/tests/cli/test_info_cmd.py index 6116d7d..1dfdc5b 100644 --- a/tests/cli/test_info_cmd.py +++ b/tests/cli/test_info_cmd.py @@ -8,113 +8,365 @@ # Test framework from typer.testing import CliRunner from unittest.mock import MagicMock, call, patch - -import io -from rich.console import Console -from rich.table import Table -from dem.core.tool_images import ToolImages +from pytest import raises ## Global test variables # In order to test stdout and stderr separately, the stderr can't be mixed into the stdout. runner = CliRunner(mix_stderr=False) -## Test helpers +@patch("dem.cli.command.info_cmd.stdout.print") +@patch("dem.cli.command.info_cmd.Table") +def test_print_local_dev_env_info_installed(mock_Table: MagicMock, + mock_stdout_print: MagicMock) -> None: + # Setup + mock_dev_env = MagicMock() + mock_dev_env.name = "test_dev_env" + mock_table = MagicMock() + mock_Table.return_value = mock_table + + mock_tool_image1 = MagicMock() + mock_tool_image1.name = "tool_image1" + mock_tool_image1.availability = info_cmd.ToolImage.LOCAL_ONLY + mock_tool_image2 = MagicMock() + mock_tool_image2.name = "tool_image2" + mock_tool_image2.availability = info_cmd.ToolImage.LOCAL_AND_REGISTRY + mock_dev_env.tool_images = [mock_tool_image1, mock_tool_image2] + mock_dev_env.is_installed = True -def get_expected_table(expected_tools: list[list[str]]) ->str: - expected_table = Table() - expected_table.add_column("Type") - expected_table.add_column("Image") - expected_table.add_column("Status") - for expected_tool in expected_tools: - expected_table.add_row(*expected_tool) - console = Console(file=io.StringIO()) - console.print(expected_table) - return console.file.getvalue() + # Run the test + info_cmd.print_local_dev_env_info(mock_dev_env) -## Test cases + # Verify the output + mock_Table.assert_called_once() + mock_table.add_column.assert_has_calls([call("Image"), call("Availability")]) + mock_table.add_row.assert_has_calls([call("tool_image1", "Local"), + call("tool_image2", "Local and Registry")]) + mock_stdout_print.assert_has_calls([call("\n[bold]Development Environment: test_dev_env[/]\n"), + call("Installation state: [green]Installed[/]\n"), + call(mock_table)]) @patch("dem.cli.command.info_cmd.stdout.print") @patch("dem.cli.command.info_cmd.Table") -def test_print_info(mock_Table: MagicMock, mock_stdout_print: MagicMock) -> None: - # Test setup +def test_print_local_dev_env_info_not_installed(mock_Table: MagicMock, + mock_stdout_print: MagicMock) -> None: + # Setup + mock_dev_env = MagicMock() + mock_dev_env.name = "test_dev_env" mock_table = MagicMock() mock_Table.return_value = mock_table mock_tool_image1 = MagicMock() - mock_tool_image1.name = "axemsolutions/make_gnu_arm:latest" - mock_tool_image1.availability = info_cmd.ToolImage.LOCAL_AND_REGISTRY - + mock_tool_image1.name = "tool_image1" + mock_tool_image1.availability = info_cmd.ToolImage.LOCAL_ONLY mock_tool_image2 = MagicMock() - mock_tool_image2.name = "axemsolutions/stlink_org:latest" - mock_tool_image2.availability = info_cmd.ToolImage.LOCAL_ONLY + mock_tool_image2.name = "tool_image2" + mock_tool_image2.availability = info_cmd.ToolImage.LOCAL_AND_REGISTRY + mock_dev_env.tool_images = [mock_tool_image1, mock_tool_image2] + mock_dev_env.is_installed = False - mock_tool_image3 = MagicMock() - mock_tool_image3.name = "axemsolutions/cpputest:latest" - mock_tool_image3.availability = info_cmd.ToolImage.REGISTRY_ONLY + # Run the test + info_cmd.print_local_dev_env_info(mock_dev_env) + # Verify the output + mock_Table.assert_called_once() + mock_table.add_column.assert_has_calls([call("Image"), call("Availability")]) + mock_table.add_row.assert_has_calls([call("tool_image1", "Local"), + call("tool_image2", "Local and Registry")]) + mock_stdout_print.assert_has_calls([call("\n[bold]Development Environment: test_dev_env[/]\n"), + call("Installation state: Not installed\n"), + call(mock_table)]) + +@patch("dem.cli.command.info_cmd.stdout.print") +@patch("dem.cli.command.info_cmd.stderr.print") +@patch("dem.cli.command.info_cmd.Table") +def test_print_local_dev_env_info_reinstall_needed(mock_Table: MagicMock, + mock_stderr_print: MagicMock, + mock_stdout_print: MagicMock) -> None: + # Setup mock_dev_env = MagicMock() - mock_dev_env.tool_images = [ mock_tool_image1, mock_tool_image2, mock_tool_image3] + mock_dev_env.name = "test_dev_env" + mock_table = MagicMock() + mock_Table.return_value = mock_table - # Run unit under test - info_cmd.print_info(mock_dev_env) - - # Check expectations - mock_table.add_column.assert_has_calls([ - call("Image"), - call("Status") - ]) - mock_table.add_row.assert_has_calls([ - call("axemsolutions/make_gnu_arm:latest", "Image is available locally and in the registry."), - call("axemsolutions/stlink_org:latest", "Image is available locally."), - call("axemsolutions/cpputest:latest", "Image is available in the registry."), - ]) - mock_stdout_print.assert_called_once_with(mock_table) + mock_tool_image1 = MagicMock() + mock_tool_image1.name = "tool_image1" + mock_tool_image1.availability = info_cmd.ToolImage.LOCAL_ONLY + mock_tool_image2 = MagicMock() + mock_tool_image2.name = "tool_image2" + mock_tool_image2.availability = info_cmd.ToolImage.LOCAL_AND_REGISTRY + mock_dev_env.tool_images = [mock_tool_image1, mock_tool_image2] + mock_dev_env.is_installed = True + mock_dev_env.get_tool_image_status.return_value = info_cmd.DevEnv.Status.REINSTALL_NEEDED + + # Run the test + info_cmd.print_local_dev_env_info(mock_dev_env) + + # Verify the output + mock_Table.assert_called_once() + mock_table.add_column.assert_has_calls([call("Image"), call("Availability")]) + mock_table.add_row.assert_has_calls([call("tool_image1", "Local"), + call("tool_image2", "Local and Registry")]) + mock_stdout_print.assert_has_calls([call("\n[bold]Development Environment: test_dev_env[/]\n"), + call("Installation state: [green]Installed[/]\n"), + call(mock_table)]) + mock_stderr_print.assert_called_once_with("\n[red]Error: Incomplete local install! The Dev Env must be reinstalled![/]") +@patch("dem.cli.command.info_cmd.stdout.print") @patch("dem.cli.command.info_cmd.stderr.print") -def test_execute_dev_env_not_found(mock_stderr_print: MagicMock) -> None: - # Test setup +@patch("dem.cli.command.info_cmd.Table") +def test_print_local_dev_env_info_unavailable_image(mock_Table: MagicMock, + mock_stderr_print: MagicMock, + mock_stdout_print: MagicMock) -> None: + # Setup + mock_dev_env = MagicMock() + mock_dev_env.name = "test_dev_env" + mock_table = MagicMock() + mock_Table.return_value = mock_table + + mock_tool_image1 = MagicMock() + mock_tool_image1.name = "tool_image1" + mock_tool_image1.availability = info_cmd.ToolImage.LOCAL_ONLY + mock_tool_image2 = MagicMock() + mock_tool_image2.name = "tool_image2" + mock_tool_image2.availability = info_cmd.ToolImage.LOCAL_AND_REGISTRY + mock_dev_env.tool_images = [mock_tool_image1, mock_tool_image2] + mock_dev_env.is_installed = False + mock_dev_env.get_tool_image_status.return_value = info_cmd.DevEnv.Status.UNAVAILABLE_IMAGE + + # Run the test + info_cmd.print_local_dev_env_info(mock_dev_env) + + # Verify the output + mock_Table.assert_called_once() + mock_table.add_column.assert_has_calls([call("Image"), call("Availability")]) + mock_table.add_row.assert_has_calls([call("tool_image1", "Local"), + call("tool_image2", "Local and Registry")]) + mock_stdout_print.assert_has_calls([call("\n[bold]Development Environment: test_dev_env[/]\n"), + call("Installation state: Not installed\n"), + call(mock_table)]) + mock_stderr_print.assert_called_once_with("\n[red]Error: Required image could not be found either locally or in the registry![/]") + +@patch("dem.cli.command.info_cmd.print_local_dev_env_info") +def test_local_info(mock_print_local_dev_env_info: MagicMock) -> None: + # Setup mock_platform = MagicMock() - main.platform = mock_platform - mock_platform.dev_env_catalogs.catalogs = [] + mock_dev_env = MagicMock() + mock_platform.get_dev_env_by_name.return_value = mock_dev_env - test_dev_env_name = "test_dev_env_name" + # Run the test + info_cmd.local_info(mock_platform, "test_dev_env") + + # Verify the output + mock_platform.assign_tool_image_instances_to_all_dev_envs.assert_called_once() + mock_platform.get_dev_env_by_name.assert_called_once_with("test_dev_env") + mock_print_local_dev_env_info.assert_called_once_with(mock_dev_env) + +@patch("dem.cli.command.info_cmd.stderr.print") +def test_local_info_unknown_dev_env(mock_stderr_print: MagicMock) -> None: + # Setup + mock_platform = MagicMock() mock_platform.get_dev_env_by_name.return_value = None - # Run unit under test - runner_result = runner.invoke(main.typer_cli, ["info", test_dev_env_name], color=True) + # Run the test + with raises(info_cmd.typer.Abort): + info_cmd.local_info(mock_platform, "unknown_dev_env") - # Check expectations - assert runner_result.exit_code == 0 - + # Verify the output mock_platform.assign_tool_image_instances_to_all_dev_envs.assert_called_once() - mock_platform.get_dev_env_by_name.assert_called_once_with(test_dev_env_name) - mock_stderr_print.assert_called_once_with(f"[red]Error: Unknown Development Environment: {test_dev_env_name}[/]\n") + mock_platform.get_dev_env_by_name.assert_called_once_with("unknown_dev_env") + mock_stderr_print.assert_called_once_with("[red]Error: Unknown Development Environment: unknown_dev_env[/]\n") -@patch("dem.cli.command.info_cmd.print_info") -def test_execute(mock_print_info: MagicMock) -> None: - # Test setup - mock_platform = MagicMock() - main.platform = mock_platform +@patch("dem.cli.command.info_cmd.stdout.print") +@patch("dem.cli.command.info_cmd.Table") +def test_print_cat_dev_env_info(mock_Table: MagicMock, + mock_stdout_print: MagicMock) -> None: + # Setup + mock_dev_env = MagicMock() + mock_dev_env.name = "test_dev_env" + mock_table = MagicMock() + mock_Table.return_value = mock_table - test_dev_env_name = "test_dev_env_name" - mock_platform.get_dev_env_by_name.return_value = None + mock_tool_image1 = MagicMock() + mock_tool_image1.name = "tool_image1" + mock_tool_image1.availability = info_cmd.ToolImage.REGISTRY_ONLY + mock_tool_image2 = MagicMock() + mock_tool_image2.name = "tool_image2" + mock_tool_image2.availability = info_cmd.ToolImage.LOCAL_AND_REGISTRY + mock_dev_env.tool_images = [mock_tool_image1, mock_tool_image2] + mock_dev_env.get_tool_image_status.return_value = info_cmd.DevEnv.Status.OK + + # Run the test + info_cmd.print_cat_dev_env_info(mock_dev_env, "test_cat") + # Verify the output + mock_Table.assert_called_once() + mock_table.add_column.assert_has_calls([call("Image"), call("Availability")]) + mock_table.add_row.assert_has_calls([call("tool_image1", "Registry"), + call("tool_image2", "Local and Registry")]) + mock_stdout_print.assert_has_calls([call("\n[bold]Development Environment: test_dev_env[/]\n"), + call(f"Catalog: test_cat\n"), + call(mock_table)]) + +@patch("dem.cli.command.info_cmd.stderr.print") +@patch("dem.cli.command.info_cmd.stdout.print") +@patch("dem.cli.command.info_cmd.Table") +def test_print_cat_dev_env_info_unavailable_image(mock_Table: MagicMock, + mock_stdout_print: MagicMock, + mock_stderr_print: MagicMock) -> None: + # Setup mock_dev_env = MagicMock() + mock_dev_env.name = "test_dev_env" + mock_table = MagicMock() + mock_Table.return_value = mock_table + + mock_tool_image1 = MagicMock() + mock_tool_image1.name = "tool_image1" + mock_tool_image1.availability = info_cmd.ToolImage.REGISTRY_ONLY + mock_tool_image2 = MagicMock() + mock_tool_image2.name = "tool_image2" + mock_tool_image2.availability = info_cmd.ToolImage.LOCAL_AND_REGISTRY + mock_dev_env.tool_images = [mock_tool_image1, mock_tool_image2] + mock_dev_env.get_tool_image_status.return_value = info_cmd.DevEnv.Status.UNAVAILABLE_IMAGE + + # Run the test + info_cmd.print_cat_dev_env_info(mock_dev_env, "test_cat") + # Verify the output + mock_Table.assert_called_once() + mock_table.add_column.assert_has_calls([call("Image"), call("Availability")]) + mock_table.add_row.assert_has_calls([call("tool_image1", "Registry"), + call("tool_image2", "Local and Registry")]) + mock_stdout_print.assert_has_calls([call("\n[bold]Development Environment: test_dev_env[/]\n"), + call(f"Catalog: test_cat\n"), + call(mock_table)]) + mock_stderr_print.assert_called_once_with("\n[red]Error: Required image could not be found in the registry![/]") + +@patch("dem.cli.command.info_cmd.print_cat_dev_env_info") +def test_cat_dev_env_info(mock_print_cat_dev_env_info: MagicMock) -> None: + # Setup + mock_dev_env = MagicMock() + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" mock_catalog = MagicMock() mock_catalog.get_dev_env_by_name.return_value = mock_dev_env + mock_catalog.name = "test_cat" mock_platform.dev_env_catalogs.catalogs = [mock_catalog] - # Run unit under test - runner_result = runner.invoke(main.typer_cli, ["info", test_dev_env_name], color=True) + # Run the test + info_cmd.cat_dev_env_info(mock_platform, test_dev_env_name, []) - # Check expectations - assert runner_result.exit_code == 0 + # Verify the output + mock_catalog.request_dev_envs.assert_called_once() + mock_catalog.get_dev_env_by_name.assert_called_once_with(test_dev_env_name) + mock_print_cat_dev_env_info.assert_called_once_with(mock_dev_env, "test_cat") - mock_platform.assign_tool_image_instances_to_all_dev_envs.assert_called_once() - mock_platform.get_dev_env_by_name.assert_called_once_with(test_dev_env_name) +@patch("dem.cli.command.info_cmd.stderr.print") +def test_cat_dev_env_info_unknown_dev_env(mock_stderr_print: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" + mock_catalog = MagicMock() + mock_catalog.get_dev_env_by_name.return_value = None + mock_platform.dev_env_catalogs.catalogs = [mock_catalog] + + # Run the test + info_cmd.cat_dev_env_info(mock_platform, test_dev_env_name, []) + + # Verify the output mock_catalog.request_dev_envs.assert_called_once() mock_catalog.get_dev_env_by_name.assert_called_once_with(test_dev_env_name) - mock_dev_env.assign_tool_image_instances.assert_called_once_with(mock_platform.tool_images) - mock_print_info.assert_called_once_with(mock_dev_env) \ No newline at end of file + mock_stderr_print.assert_called_once_with("[red]Error: Unknown Development Environment: test_dev_env[/]\n") + +@patch("dem.cli.command.info_cmd.stderr.print") +def test_selected_cats_info_unknown_catalog(mock_stderr_print: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" + mock_catalog = MagicMock() + mock_catalog.name = "test_cat" + mock_platform.dev_env_catalogs.catalogs = [mock_catalog] + + # Run the test + with raises(info_cmd.typer.Abort): + info_cmd.selected_cats_info(mock_platform, test_dev_env_name, ["unknown_cat"]) + + # Verify the output + mock_stderr_print.assert_called_once_with("[red]Error: Unknown catalog(s): unknown_cat[/]\n") + +@patch("dem.cli.command.info_cmd.cat_dev_env_info") +def test_selected_cats_info(mock_cat_dev_env_info: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" + mock_catalog = MagicMock() + mock_catalog.name = "test_cat" + mock_platform.dev_env_catalogs.catalogs = [mock_catalog] + + # Run the test + info_cmd.selected_cats_info(mock_platform, test_dev_env_name, ["test_cat"]) + + # Verify the output + mock_cat_dev_env_info.assert_called_once_with(mock_platform, test_dev_env_name, { "test_cat" }) + +@patch("dem.cli.command.info_cmd.cat_dev_env_info") +def test_all_cats_info(mock_cat_dev_env_info: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" + + # Run the test + info_cmd.all_cats_info(mock_platform, test_dev_env_name) + + # Verify the output + mock_cat_dev_env_info.assert_called_once_with(mock_platform, test_dev_env_name, []) + +@patch("dem.cli.command.info_cmd.local_info") +def test_execute_local_info(mock_local_info: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" + + # Run the test + info_cmd.execute(mock_platform, test_dev_env_name, False, []) + + # Verify the output + mock_local_info.assert_called_once_with(mock_platform, test_dev_env_name) + +@patch("dem.cli.command.info_cmd.selected_cats_info") +def test_execute_selected_cats_info(mock_selected_cats_info: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" + + # Run the test + info_cmd.execute(mock_platform, test_dev_env_name, True, ["test_cat"]) + + # Verify the output + mock_selected_cats_info.assert_called_once_with(mock_platform, test_dev_env_name, ["test_cat"]) + +@patch("dem.cli.command.info_cmd.all_cats_info") +def test_execute_all_cats_info(mock_all_cats_info: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + test_dev_env_name = "test_dev_env" + + # Run the test + info_cmd.execute(mock_platform, test_dev_env_name, True, []) + + # Verify the output + mock_all_cats_info.assert_called_once_with(mock_platform, test_dev_env_name) + +@patch("dem.cli.command.info_cmd.execute") +def test_info_cmd(mock_execute: MagicMock) -> None: + # Setup + mock_platform = MagicMock() + main.platform = mock_platform + + # Run the test + result = runner.invoke(main.typer_cli, ["info", "test_dev_env"]) + + # Verify the output + assert result.exit_code == 0 + + mock_execute.assert_called_once_with(mock_platform, "test_dev_env", False, []) \ No newline at end of file