Skip to content

Commit

Permalink
feat(execute): arg replacement
Browse files Browse the repository at this point in the history
  • Loading branch information
lt-mayonesa committed Oct 28, 2023
1 parent 8c4c53a commit f459405
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 17 deletions.
1 change: 1 addition & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion hexagon/domain/tool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class ActionTool(Tool):
def executable_str(self):
if isinstance(self.action, list):
return "\n".join(self.action)
return self.action
cmd = str(self.action)
return cmd[:-1] if cmd.endswith("\n") else cmd


class FunctionTool(Tool):
Expand Down
51 changes: 35 additions & 16 deletions hexagon/runtime/execute/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@


@execution_hook
def execute_action(tool: ActionTool, env_args: Any, env: Env, cli_args: CliArgs):
def execute_action(
tool: ActionTool, env_args: Any, env: Optional[Env], cli_args: CliArgs
):
custom_tools_path = configuration.custom_tools_path
action_to_execute: str = tool.executable_str
script_interpreter, script_abs_path = __get_interpreter_and_path(action_to_execute)
Expand All @@ -59,7 +61,7 @@ def execute_action(tool: ActionTool, env_args: Any, env: Env, cli_args: CliArgs)
return

split_action = action_to_execute.split(" ")
return_code, executed_command = _execute_command(
return_code, executed_command = _execute_inline_command(
split_action[0],
env_args,
cli_args,
Expand Down Expand Up @@ -144,7 +146,7 @@ def __args_with_optional_env(cli_args: CliArgs, env: Optional[Env]):
) + cli_args.raw_extra_args


def _execute_command(
def _execute_inline_command(
command: str,
env_args: Any,
cli_args: CliArgs,
Expand All @@ -153,20 +155,16 @@ def _execute_command(
action_args: List[str] = None,
):
action_args = action_args if action_args else []
hexagon_args = __sanitize_args_for_command(
env_args, *__args_with_optional_env(cli_args, env)
cmd_as_string = " ".join(
[command] + action_args + __args_with_optional_env(cli_args, env)
).format(
tool=tool,
env=env,
env_args=env_args,
cli_args=cli_args.format_friendly_extra_args, # TODO: make format_friendly_extra_args work here
)
cmd_as_string = " ".join([command] + action_args + hexagon_args)

env_vars = os.environ.copy()
env_vars[ENVVAR_EXECUTION_TOOL] = tool.json()
if env:
env_vars[ENVVAR_EXECUTION_ENV] = env.json()

return (
subprocess.call(cmd_as_string, shell=True, env=env_vars),
cmd_as_string,
)
return __call_subprocess(cmd_as_string, env, tool)


def _execute_script(
Expand All @@ -177,7 +175,17 @@ def _execute_script(
env: Env,
cli_args: CliArgs,
):
_execute_command(command, env_args, cli_args, tool, env, [script_path])
action_args = [script_path]
hexagon_args = __sanitize_args_for_command(
env_args, *__args_with_optional_env(cli_args, env)
)
cmd_as_string = " ".join([command] + action_args + hexagon_args).format(
tool=tool,
env=env,
env_args=env_args,
cli_args=cli_args.extra_args,
)
return __call_subprocess(cmd_as_string, env, tool)


def __sanitize_args_for_command(*args: Union[List[any], Dict, Env]):
Expand Down Expand Up @@ -218,3 +226,14 @@ def __load_module(module: str):
return sys.modules[module]

return importlib.import_module(module)


def __call_subprocess(command: str, env: Optional[Env], tool: ActionTool):
env_vars = os.environ.copy()
env_vars[ENVVAR_EXECUTION_TOOL] = tool.json()
if env:
env_vars[ENVVAR_EXECUTION_ENV] = env.json()
return (
subprocess.call(command, shell=True, env=env_vars),
command,
)
1 change: 1 addition & 0 deletions hexagon/runtime/execute/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def select_and_execute_tool(
) -> List[str]:
tool = search_by_name_or_alias(tools, cli_args.tool and cli_args.tool.value)
env = search_by_name_or_alias(envs, cli_args.env and cli_args.env.value)
# FIXME: validate selected env: if tool has envs defined and env is None -> should fail

tool = select_tool(tools, tool)
if tool.traced:
Expand Down
15 changes: 15 additions & 0 deletions hexagon/support/input/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,21 @@ def count(self):
def key_value_arg(key, arg):
return f"{key}={arg}"

@property
def format_friendly_extra_args(self):
return (
{
"positional": sorted(
[v for k, v in self.extra_args.items() if k.isdigit()]
),
"optional": {
k: v for k, v in self.extra_args.items() if not k.isdigit()
},
}
if self.extra_args
else {}
)


class ToolArgs(BaseModel):
__tracer__ = None
Expand Down
53 changes: 53 additions & 0 deletions tests_e2e/__specs/execute_tool_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,59 @@ def test_execute_multiline_command_with_input_as_list():
)


def test_execute_a_formatted_command_with_env_args():
(
as_a_user(__file__)
.run_hexagon(["a-simple-formatted-command", "dev"])
.then_output_should_be(["environment: development, tool: A formatted command"])
.exit()
)
(
as_a_user(__file__)
.run_hexagon(["a-simple-formatted-command", "qa"])
.then_output_should_be(
["environment: quality assurance, tool: A formatted command"]
)
.exit()
)


def test_execute_a_formatted_command_with_object_env_args():
(
as_a_user(__file__)
.run_hexagon(["a-complex-formatted-command", "dev"])
.then_output_should_be(["Hello John, you are 30 years old"])
.exit()
)
(
as_a_user(__file__)
.run_hexagon(["a-complex-formatted-command", "qa"])
.then_output_should_be(["Hello Jane, you are 40 years old"])
.exit()
)


def test_execute_a_formatted_command_with_all_action_args():
(
as_a_user(__file__)
.run_hexagon(
[
"all-tool-args-are-replaced",
"dev",
"cli_positional_value",
"--cli_optional='value'",
]
)
.then_output_should_be(
[
"tool alias is ataar, selected env is dev",
"env_args is 32, cli_args are cli_positional_value and 'value'",
]
)
.exit()
)


def test_execute_inline_command_with_path():
path = os.environ["PATH"]
(
Expand Down
33 changes: 33 additions & 0 deletions tests_e2e/execute_tool_2/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,39 @@ tools:
alias: mcal
long_name: A multiline command as list

- name: a-simple-formatted-command
alias: asfc
long_name: A formatted command
type: shell
envs:
dev: development
qa: quality assurance
action: 'echo "environment: {env_args}, tool: {tool.long_name}"'

- name: a-complex-formatted-command
alias: acfc
long_name: A formatted command
type: shell
envs:
dev:
name: 'John'
age: 30
qa:
name: 'Jane'
age: 40
action: 'echo "Hello {env_args[name]}, you are {env_args[age]} years old"'

- name: all-tool-args-are-replaced
alias: ataar
long_name: A formatted command
type: shell
envs:
dev: 32
qa: 123
action: |
echo "tool alias is {tool.alias}, selected env is {env.long_name}"
echo "env_args is {env_args}, cli_args are {cli_args[positional][0]} and {cli_args[optional][cli_optional]}"
- name: inline-command-with-PATH
action: 'echo "$PATH"'
type: shell
Expand Down

0 comments on commit f459405

Please sign in to comment.