Skip to content

Commit

Permalink
The add-task command implemented.
Browse files Browse the repository at this point in the history
  • Loading branch information
janosmurai committed Apr 30, 2024
1 parent d49cd5b commit 80fe6ae
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 12 deletions.
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

0 comments on commit 80fe6ae

Please sign in to comment.