Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The add-task command implemented. #191

Merged
merged 1 commit into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions dem/cli/command/add_task_cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from dem.core.platform import Platform
from dem.cli.console import stderr

def execute(platform: Platform, dev_env_name: str, task_name: str, command: str) -> None:
""" Add a task to a Development Environment.

Args:
platform -- the Platform
dev_env_name -- the Development Environment name
task_name -- the task name
command -- the command
"""
dev_env = platform.get_dev_env_by_name(dev_env_name)
if dev_env is None:
stderr.print(f"[red]Error: Development Environment '{dev_env_name}' not found![/]")
return
dev_env.add_task(task_name, command)
platform.flush_dev_env_properties()
24 changes: 20 additions & 4 deletions dem/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
from typing_extensions import Annotated
import os
from dem import __command__, __app_name__
from dem.cli.command import cp_cmd, import_cmd, info_cmd, list_cmd, create_cmd, modify_cmd, delete_cmd, \
rename_cmd, run_cmd, export_cmd, clone_cmd, add_reg_cmd, list_reg_cmd, del_reg_cmd, add_cat_cmd, list_cat_cmd, del_cat_cmd, \
add_host_cmd, set_default_cmd, uninstall_cmd, install_cmd, assign_cmd, init_cmd, \
list_host_cmd, del_host_cmd, list_tools_cmd
from dem.cli.command import cp_cmd, import_cmd, info_cmd, list_cmd, create_cmd, modify_cmd, \
delete_cmd, rename_cmd, run_cmd, export_cmd, clone_cmd, add_reg_cmd, \
list_reg_cmd, del_reg_cmd, add_cat_cmd, list_cat_cmd, del_cat_cmd, \
add_host_cmd, set_default_cmd, uninstall_cmd, install_cmd, assign_cmd, \
init_cmd, list_host_cmd, del_host_cmd, list_tools_cmd, add_task_cmd
from dem.cli.console import stdout
from dem.core.platform import Platform
from dem.core.exceptions import InternalError
Expand Down Expand Up @@ -90,6 +91,21 @@ def autocomplete_host_name(incomplete: str) -> Generator:

# DEM commands
@typer_cli.command()
def add_task(dev_env_name: Annotated[str, typer.Argument(help="Name of the Development Environment to add the task to.",
autocompletion=autocomplete_dev_env_name)],
task_name: Annotated[str, typer.Argument(help="Name of the task.")],
command: Annotated[str, typer.Argument(help="The command the task should execute.")]) -> None:
"""
Add a new task to the Development Environment.

The command will be executed when the `dem run dev_env_name task_name` command is called. The
command must be surrounded by quotes.
"""
if platform:
add_task_cmd.execute(platform, dev_env_name, task_name, command)
else:
raise InternalError("Error: The platform hasn't been initialized properly!")
@typer_cli.command()
def set_default(dev_env_name: Annotated[str,
typer.Argument(help="The name of the Development Environment to set as default.",
autocompletion=autocomplete_installed_dev_env_name)]) -> None:
Expand Down
10 changes: 7 additions & 3 deletions dem/core/dev_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ def __init__(self, descriptor: dict | None = None, descriptor_path: str | None =
self.name: str = descriptor["name"]
self.tool_image_descriptors: list[dict[str, str]] = descriptor["tools"]
self.tool_images: list[ToolImage] = []
descriptor_installed = descriptor.get("installed", "False")
if "True" == descriptor_installed:
self.tasks: dict[str, str] = descriptor.get("tasks", {})
if "True" == descriptor.get("installed", "False"):
self.is_installed = True
else:
self.is_installed = False
Expand All @@ -68,6 +68,9 @@ def assign_tool_image_instances(self, tool_images: ToolImages) -> None:
tool_image = tool_images.all_tool_images.get(tool_image_name, ToolImage(tool_image_name))
self.tool_images.append(tool_image)

def add_task(self, task_name: str, command: str) -> None:
self.tasks[task_name] = command

def get_tool_image_status(self) -> Status:
""" Get the status of the Tool Images.

Expand All @@ -93,7 +96,8 @@ def get_deserialized(self, omit_is_installed: bool = False) -> dict[str, str]:
"""
dev_env_json_deserialized: dict = {
"name": self.name,
"tools": self.tool_image_descriptors
"tools": self.tool_image_descriptors,
"tasks": self.tasks
}

if omit_is_installed is False:
Expand Down
26 changes: 26 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,32 @@ title: Commands
# Development Environment management


## **`dem add-task DEV_ENV_NAME TASK_NAME COMMAND`**

**Description:**

Add a new task to the Development Environment.

A task is a command that can be run in the context of the Development Environment.
The task can be run with the `dem run` command.

**Arguments:**

| Argument | Description | Required |
|------------------|---------------------------------------------------------|----------------:|
| `DEV_ENV_NAME` | Name of the Development Environment. | :material-check:|
| `TASK_NAME` | Name of the task. | :material-check:|
| `COMMAND` | Command to run. Must be enclosed with quotes. | :material-check:|

**Examples:**

| Example | Description |
|--------------------|---------------------------------------------------------|
| `dem add-task dev_env_name list-dir "ls -la"` | Add a new command called `list-dir` that lists the content of the current directory. The task can be executed with `dem run dev_env_name list-dir`. |
| `dem add-task dev_env_name build "docker run --rm -v \"$(pwd)\":/work axemsolutions/make_gnu-arm:13.2 make"` | Add a new command called `build` that builds the project in a docker container. The task can be executed with `dem run dev_env_name build`. |

---

## **`dem assign DEV_ENV_NAME, [PROJECT_PATH]`**

**Description:**
Expand Down
51 changes: 51 additions & 0 deletions tests/cli/test_add_task_cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Unit tests for the add-task CLI command."""
# tests/cli/test_add_task_cmd.py

# Unit under test:
import dem.cli.main as main
import dem.cli.command.add_task_cmd as add_task_cmd

# Test framework
from typer.testing import CliRunner
from unittest.mock import patch, MagicMock, call

## 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)

def test_add_task_cmd() -> None:
# Setup
mock_platform = MagicMock()
main.platform = mock_platform

mock_dev_env = MagicMock()
mock_platform.get_dev_env_by_name.return_value = mock_dev_env

# Run
result = runner.invoke(main.typer_cli, ["add-task", "my-dev-env", "my-task", "my-command"])

# Check
assert result.exit_code == 0

mock_platform.get_dev_env_by_name.assert_called_once_with("my-dev-env")
mock_dev_env.add_task.assert_called_once_with("my-task", "my-command")
mock_platform.flush_dev_env_properties.assert_called_once()

@patch("dem.cli.command.add_task_cmd.stderr.print")
def test_execute_dev_env_not_found(mock_stderr_print: MagicMock) -> None:
# Setup
mock_platform = MagicMock()
mock_platform.get_dev_env_by_name.return_value = None

test_dev_env_name = "my-dev-env"
test_task_name = "my-task"
test_command = "my-command"

# Run
add_task_cmd.execute(mock_platform, test_dev_env_name, test_task_name, test_command)

# Check
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: Development Environment '{test_dev_env_name}' not found![/]")
2 changes: 2 additions & 0 deletions tests/cli/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,12 @@ def test_platform_not_initialized() -> None:
test_path = "test_path"
test_name = "test_name"
test_url = "test_url"
test_command = "test_command"
mock_ctx = MagicMock()
main.platform = None

units_to_test = {
main.add_task: [test_dev_env_name, test_name, test_command],
main.set_default: [test_dev_env_name],
main.list_: [],
main.list_tools: [],
Expand Down
52 changes: 47 additions & 5 deletions tests/core/test_dev_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ def test_DevEnv() -> None:
test_descriptor = {
"name": "test_name",
"installed": "True",
"tools": [MagicMock()]
"tools": [MagicMock()],
"tasks": {
"test_task_name1": "test_task_command1",
"test_task_name2": "test_task_command2",
"test_task_name3": "test_task_command3"
}
}

# Run unit under test
Expand All @@ -24,6 +29,7 @@ def test_DevEnv() -> None:
# Check expectations
assert test_dev_env.name is test_descriptor["name"]
assert test_dev_env.tool_image_descriptors is test_descriptor["tools"]
assert test_dev_env.tasks is test_descriptor["tasks"]

@patch("dem.core.dev_env.json.load")
@patch("dem.core.dev_env.open")
Expand All @@ -35,7 +41,12 @@ def test_DevEnv_with_descriptor_path(mock_path_exists: MagicMock, mock_open: Mag
test_descriptor = {
"name": "test_name",
"installed": "True",
"tools": [MagicMock()]
"tools": [MagicMock()],
"tasks": {
"test_task_name1": "test_task_command1",
"test_task_name2": "test_task_command2",
"test_task_name3": "test_task_command3"
}
}
mock_path_exists.return_value = True
mock_file = MagicMock()
Expand All @@ -49,6 +60,7 @@ def test_DevEnv_with_descriptor_path(mock_path_exists: MagicMock, mock_open: Mag
assert test_dev_env.name is test_descriptor["name"]
assert test_dev_env.tool_image_descriptors is test_descriptor["tools"]
assert test_dev_env.is_installed is True
assert test_dev_env.tasks is test_descriptor["tasks"]

mock_path_exists.assert_called_once_with(test_descriptor_path)
mock_open.assert_called_once_with(test_descriptor_path, "r")
Expand Down Expand Up @@ -131,6 +143,29 @@ def test_DevEnv_assign_tool_image_instances() -> None:
for tool_image in test_dev_env.tool_images:
assert tool_image is mock_tool_images.all_tool_images[tool_image.name]

def test_DevEnv_add_task() -> None:
# Test setup
test_descriptor = {
"name": "test_name",
"installed": "True",
"tools": [MagicMock()],
"tasks": {
"test_task_name1": "test_task_command1",
"test_task_name2": "test_task_command2",
"test_task_name3": "test_task_command3"
}
}
test_dev_env = dev_env.DevEnv(test_descriptor)

test_task_name = "test_task_name4"
test_command = "test_task_command4"

# Run unit under test
test_dev_env.add_task(test_task_name, test_command)

# Check expectations
assert test_dev_env.tasks[test_task_name] == test_command

@patch.object(dev_env.DevEnv, "__init__")
def test_DevEnv_get_tool_image_status(mock___init__: MagicMock) -> None:
# Test setup
Expand Down Expand Up @@ -222,7 +257,12 @@ def test_DevEnv_get_deserialized_is_installed_true() -> None:
"image_name": "test_image_name4",
"image_version": "test_image_tag4"
},
]
],
"tasks": {
"test_task_name1": "test_task_command1",
"test_task_name2": "test_task_command2",
"test_task_name3": "test_task_command3"
}
}
test_dev_env = dev_env.DevEnv(test_descriptor)

Expand Down Expand Up @@ -254,7 +294,8 @@ def test_DevEnv_get_deserialized_is_installed_false() -> None:
"image_name": "test_image_name4",
"image_version": "test_image_tag4"
},
]
],
"tasks": {}
}
test_dev_env = dev_env.DevEnv(test_descriptor)

Expand Down Expand Up @@ -286,7 +327,8 @@ def test_DevEnv_get_deserialized_omit_is_installed() -> None:
"image_name": "test_image_name4",
"image_version": "test_image_tag4"
},
]
],
"tasks": {}
}
test_dev_env = dev_env.DevEnv(test_descriptor)

Expand Down
Loading