Skip to content

Commit

Permalink
Merge 4b8744d into 2e29f9a
Browse files Browse the repository at this point in the history
  • Loading branch information
m3brown committed Dec 21, 2021
2 parents 2e29f9a + 4b8744d commit 34631e2
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 30 deletions.
15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -106,6 +106,21 @@ commands:
cmd: ./manage.py runserver
```

### Shortcuts

Similar to environment variables, shortcuts can be created witin the plz.yaml
file for reference by individual commands.

```yaml
shortcuts:
dc: docker-compose
commands:
start:
cmd: ${dc} up
shell:
cmd: ${dc} run web bash
```

### Globbing

plz supports asterisk expansion. For example, the cmd `ls *.py` will work as expected.
Expand Down
1 change: 1 addition & 0 deletions plz/main.py
Expand Up @@ -46,6 +46,7 @@ def execute_from_config(cmd, args):
kwargs = {
"cwd": cwd,
"args": args,
"shortcuts": config.get("shortcuts", {}),
}
env = compile_environment(
data.get("env", {}), global_env=config.get("global_env", {})
Expand Down
20 changes: 15 additions & 5 deletions plz/runner.py
Expand Up @@ -7,6 +7,12 @@
from .glob_tools import process_absolute_glob, process_relative_glob


def inject_shortcuts(command, shortcuts={}):
for k, v in shortcuts.items():
command = command.replace("${{{}}}".format(k), v)
return command


def run_command(command, cwd=None, args=[], env=None):
pwd = os.getcwd()
if not cwd:
Expand All @@ -26,26 +32,30 @@ def run_command(command, cwd=None, args=[], env=None):
return 0


def gather_and_run_commands(cmd, cwd=None, args=[], env=None):
def gather_and_run_commands(cmd, **kwargs):
"""
The cmd argument can either be a string or list
- If it's a string, execute the command.
- If it's a list, recursively run each item in the list.
"""
if type(cmd) == str:
shortcuts = kwargs.pop("shortcuts", [])
if shortcuts:
cmd = inject_shortcuts(cmd, shortcuts)

print_info_dim(
textwrap.dedent(
"""
===============================================================================
Running command: {}
===============================================================================
""".format(
" ".join([cmd] + args)
" ".join([cmd] + kwargs.get("args", []))
)
)
)
rc = run_command(cmd, cwd=cwd, args=args, env=env)
rc = run_command(cmd, **kwargs)
print()
if rc > 0:
print_error("Process failed", prefix=True)
Expand All @@ -62,12 +72,12 @@ def gather_and_run_commands(cmd, cwd=None, args=[], env=None):
Skipping command due to previous errors: '{}'
===============================================================================
""".format(
" ".join([item] + args)
" ".join([item] + kwargs.get("args", []))
)
)
)
else:
rc = gather_and_run_commands(item, cwd=cwd, args=args, env=env)
rc = gather_and_run_commands(item, **kwargs)
else:
raise Exception("Unrecognized cmd type: {}".format(type(cmd)))
return rc
1 change: 1 addition & 0 deletions plz/schema/v2.py
Expand Up @@ -30,6 +30,7 @@
"additionalProperties": False,
},
"global_env": env_variable_dict,
"shortcuts": env_variable_dict,
},
"additionalProperties": False,
}
73 changes: 64 additions & 9 deletions tests/gather_and_run_commands_test.py
@@ -1,6 +1,6 @@
import pytest

from plz.runner import gather_and_run_commands
from plz.runner import gather_and_run_commands, inject_shortcuts

try:
from mock import call, patch
Expand All @@ -18,7 +18,7 @@ def test_gather_and_run_string_cmd(mock_run):
gather_and_run_commands(cmd)

# Assert
mock_run.assert_called_with(cmd, cwd=None, args=[], env=None)
mock_run.assert_called_with(cmd)


@patch("plz.runner.run_command")
Expand All @@ -40,9 +40,9 @@ def test_gather_and_run_list_cmds(mock_run):
mock_run.return_value = 0
cmd = ["test cmd", "second cmd", "third cmd"]
calls = [
call(cmd[0], cwd=None, args=[], env=None),
call(cmd[1], cwd=None, args=[], env=None),
call(cmd[2], cwd=None, args=[], env=None),
call(cmd[0]),
call(cmd[1]),
call(cmd[2]),
]

# Act
Expand Down Expand Up @@ -72,7 +72,7 @@ def test_gather_and_run_string_cmd_with_cwd(mock_run):
gather_and_run_commands(cmd, cwd="/root/path")

# Assert
mock_run.assert_called_with(cmd, cwd="/root/path", args=[], env=None)
mock_run.assert_called_with(cmd, cwd="/root/path")


@patch("plz.runner.run_command")
Expand All @@ -86,7 +86,7 @@ def test_gather_and_run_string_cmd_with_args(mock_run):
gather_and_run_commands(cmd, args=args)

# Assert
mock_run.assert_called_with(cmd, cwd=None, args=args, env=None)
mock_run.assert_called_with(cmd, args=args)


@patch("plz.runner.run_command")
Expand All @@ -101,7 +101,7 @@ def test_gather_and_run_string_cmd_with_env(mock_run):
gather_and_run_commands(cmd, args=args, env=env)

# Assert
mock_run.assert_called_with(cmd, cwd=None, args=args, env=env)
mock_run.assert_called_with(cmd, args=args, env=env)


@patch("plz.runner.run_command")
Expand All @@ -121,4 +121,59 @@ def test_gather_and_run_list_cmds_with_error(mock_run):
# Assert
assert rc == 1
assert mock_run.call_count == 1
mock_run.assert_called_once_with(cmd[0], cwd=None, args=[], env=None)
mock_run.assert_called_once_with(cmd[0])


@pytest.mark.parametrize(
"cmd,shortcuts",
[
["echo", {}],
["echo", {"foo": "bar"}],
[["echo", "echo"], {}],
[["echo", "echo"], {"foo": "bar"}],
],
)
@patch("plz.runner.inject_shortcuts")
def test_gather_and_run_calls_inject_shortcuts(mock_inject_shortcuts, cmd, shortcuts):
# Arrange
mock_inject_shortcuts.side_effect = lambda x, y: x
if shortcuts:
if type(cmd) == list:
expected_calls = [call(command, shortcuts) for command in cmd]
else:
expected_calls = [call(cmd, shortcuts)]
else:
expected_calls = []

# Act
gather_and_run_commands(cmd, shortcuts=shortcuts)

# Assert
mock_inject_shortcuts.assert_has_calls(expected_calls)


@pytest.mark.parametrize(
"command,shortcuts,expected_output",
[
["test command", {}, "test command"],
["test command", {"foo": "bar"}, "test command"],
["test ${foo} command", {"foo": "bar"}, "test bar command"],
["test ${bar} command", {"foo": "bar"}, "test ${bar} command"],
["${foo} test command", {"foo": "bar"}, "bar test command"],
["test command ${foo}", {"foo": "bar"}, "test command bar"],
[
"test ${derp} command ${foo}",
{"foo": "bar", "derp": "herp derp"},
"test herp derp command bar",
],
['bash -c "${foo}"', {"foo": "bar"}, 'bash -c "bar"'],
],
)
def test_run_command_calls_inject_shortcuts(command, shortcuts, expected_output):
# Arrange

# Act
result = inject_shortcuts(command, shortcuts)

# Assert
assert result == expected_output
41 changes: 31 additions & 10 deletions tests/main_test.py
Expand Up @@ -123,7 +123,7 @@ def test_execute_from_config_with_valid_cmd(mock_plz_config, mock_gather, mock_e
main.execute_from_config("testcmd", args)

# Assert
mock_gather.assert_called_with("derp", cwd=None, args=args)
mock_gather.assert_called_with("derp", cwd=None, args=args, shortcuts={})


@patch("sys.exit")
Expand All @@ -133,16 +133,13 @@ def test_execute_from_config_with_dir(mock_plz_config, mock_gather, mock_exit):
# Arrange
args = ["args"]
config = get_sample_config(dir="foo")
mock_plz_config.return_value = (
config,
None,
)
mock_plz_config.return_value = (config, None)

# Act
main.execute_from_config("testcmd", args)

# Assert
mock_gather.assert_called_with("derp", cwd="foo", args=args)
mock_gather.assert_called_with("derp", cwd="foo", args=args, shortcuts={})


@patch("sys.exit")
Expand All @@ -161,7 +158,7 @@ def test_execute_from_config_with_valid_cmd_and_cwd(
main.execute_from_config("testcmd", args)

# Assert
mock_gather.assert_called_with("derp", cwd=cwd, args=args)
mock_gather.assert_called_with("derp", cwd=cwd, args=args, shortcuts={})


@patch("sys.exit")
Expand All @@ -181,7 +178,9 @@ def test_execute_from_config_with_cwd_and_dir(mock_plz_config, mock_gather, mock
main.execute_from_config("testcmd", args)

# Assert
mock_gather.assert_called_with("derp", cwd="/root/path/foo", args=args)
mock_gather.assert_called_with(
"derp", cwd="/root/path/foo", args=args, shortcuts={}
)


@patch("sys.exit")
Expand All @@ -201,7 +200,29 @@ def test_execute_from_config_passes_env_dict_if_defined(
main.execute_from_config("testcmd", args)

# Assert
mock_gather.assert_called_with(ANY, cwd=ANY, args=ANY, env={"foo": "bar"})
mock_gather.assert_called_with(
ANY, cwd=ANY, args=ANY, env={"foo": "bar"}, shortcuts=ANY
)


@patch("sys.exit")
@patch("plz.main.gather_and_run_commands")
@patch("plz.main.plz_config")
def test_execute_from_config_passes_shortcuts_dict_if_defined(
mock_plz_config, mock_gather, mock_exit
):
# Arrange
args = ["args"]
shortcuts = {"foo": "bar"}
config = get_sample_config()
config["shortcuts"] = shortcuts
mock_plz_config.return_value = (config, None)

# Act
main.execute_from_config("testcmd", args)

# Assert
mock_gather.assert_called_with(ANY, cwd=ANY, args=ANY, shortcuts=shortcuts)


@patch("sys.exit")
Expand Down Expand Up @@ -251,7 +272,7 @@ def test_execute_from_config_with_complex_cmd(mock_plz_config, mock_gather, mock
main.execute_from_config("testcmd", args)

# Assert
mock_gather.assert_called_with(["derp", "herp"], cwd=None, args=args)
mock_gather.assert_called_with(["derp", "herp"], cwd=None, args=args, shortcuts={})


@patch("sys.exit")
Expand Down
17 changes: 11 additions & 6 deletions tests/schema_test.py
Expand Up @@ -90,10 +90,11 @@ def test_validate_command_without_cmd_fails():


@pytest.mark.parametrize(
"is_global",
"config_type",
[
True,
False,
"global_env",
"env",
"shortcut",
],
)
@pytest.mark.parametrize(
Expand All @@ -110,13 +111,17 @@ def test_validate_command_without_cmd_fails():
["1", "bar", True],
],
)
def test_validate_env(key, value, expect_pass, is_global):
def test_validate_env(key, value, expect_pass, config_type):
# Arrange
config = get_sample_config()
if is_global:
if config_type == "global_env":
config["global_env"] = {key: value}
else:
elif config_type == "env":
config["commands"]["test"]["env"] = {key: value}
elif config_type == "shortcut":
config["shortcuts"] = {key: value}
else:
raise Exception("did not recognize config type {}".format(config_type))

# Act
if expect_pass:
Expand Down

0 comments on commit 34631e2

Please sign in to comment.