Skip to content

Commit

Permalink
Refactor plz.yaml schema
Browse files Browse the repository at this point in the history
  • Loading branch information
m3brown committed Dec 5, 2021
1 parent 051e573 commit 50fcdfb
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 141 deletions.
44 changes: 22 additions & 22 deletions plz.yaml
@@ -1,24 +1,24 @@
commands:
- id: publish
cmd:
# Confirm we're on the master branch
- bash -c "git rev-parse --abbrev-ref HEAD | xargs -I {} bash -c '[ {} == master ] || (echo Please checkout master branch for publishing && exit 1)'"
# Confirm we're up to date with remote origin
- bash -c "git fetch origin; [ `git rev-list HEAD...origin/master --count` = 0 ] || (echo Please fetch latest commits from origin && exit 1)"
# Confirm there aren not any local, uncommitted changes
- bash -c "git diff-index --name-status --exit-code HEAD || (echo Please revert or PR local changes to origin && exit 1)"
# Confirm all tests are passing
- bash -c "plz test || (echo Please fix the tests and try again && exit 1)"
- poetry publish --build
# Create tag and push to remote origin
- bash -c "poetry version | awk '{print $2}' | xargs -I {} bash -c 'git tag -a {} -m {} && git push origin {}'"
publish:
cmd:
# Confirm we're on the master branch
- bash -c "git rev-parse --abbrev-ref HEAD | xargs -I {} bash -c '[ {} == master ] || (echo Please checkout master branch for publishing && exit 1)'"
# Confirm we're up to date with remote origin
- bash -c "git fetch origin; [ `git rev-list HEAD...origin/master --count` = 0 ] || (echo Please fetch latest commits from origin && exit 1)"
# Confirm there aren not any local, uncommitted changes
- bash -c "git diff-index --name-status --exit-code HEAD || (echo Please revert or PR local changes to origin && exit 1)"
# Confirm all tests are passing
- bash -c "plz test || (echo Please fix the tests and try again && exit 1)"
- poetry publish --build
# Create tag and push to remote origin
- bash -c "poetry version | awk '{print $2}' | xargs -I {} bash -c 'git tag -a {} -m {} && git push origin {}'"

- id: test
cmd:
- poetry run python -m pytest
- id: setup
cmd:
- poetry install
- poetry run pre-commit install
- id: lint
cmd: poetry run pre-commit run --all-files
test:
cmd:
- poetry run python -m pytest
setup:
cmd:
- poetry install
- poetry run pre-commit install
lint:
cmd: poetry run pre-commit run --all-files
12 changes: 12 additions & 0 deletions plz/colorize.py
Expand Up @@ -2,6 +2,8 @@

ERROR = Fore.RED
ERROR_DIM = Fore.RED + Style.DIM
WARNING = Fore.YELLOW
WARNING_DIM = Fore.YELLOW + Style.DIM
INFO = Fore.CYAN
INFO_DIM = Fore.CYAN + Style.DIM
RESET = Style.RESET_ALL
Expand All @@ -22,6 +24,16 @@ def print_error_dim(text, prefix=False):
return __print_text(text, ERROR_DIM, prefix_string)


def print_warning(text, prefix=False):
prefix_string = "WARNING" if prefix else None
return __print_text(text, WARNING, prefix_string)


def print_warning_dim(text, prefix=False):
prefix_string = "WARNING" if prefix else None
return __print_text(text, WARNING_DIM, prefix_string)


def print_info(text, prefix=False):
prefix_string = "INFO" if prefix else None
return __print_text(text, INFO, prefix_string)
Expand Down
11 changes: 8 additions & 3 deletions plz/config.py
Expand Up @@ -4,8 +4,9 @@
import textwrap

import yaml
from jsonschema.exceptions import ValidationError

from .colorize import print_error, print_info
from .colorize import print_error, print_info, print_warning
from .schema import validate_configuration_data

DOC_URL = "https://github.com/m3brown/plz"
Expand Down Expand Up @@ -66,7 +67,11 @@ def git_root():
def load_config(filename):
try:
config = yaml.load(open(filename), Loader=yaml.SafeLoader)
validate_configuration_data(config)
try:
validate_configuration_data(config)
except ValidationError as e:
print_warning("\n" + str(e))
raise InvalidYamlException(filename)
print_info("Using config: {}".format(filename), prefix=True)
return config
except yaml.YAMLError as e:
Expand All @@ -89,7 +94,7 @@ def plz_config():
if not match:
match = find_file(".plz.yaml")
if match:
print_error(
print_warning(
"DEPRECATION WARNING: Please rename '.plz.yaml' to 'plz.yaml'"
)
else:
Expand Down
50 changes: 24 additions & 26 deletions plz/main.py
Expand Up @@ -16,10 +16,10 @@ def usage():


def list_options(config):
options = sorted([task["id"] for task in config["commands"]])
options = config["commands"].keys()
print("Available commands from config:")
for cmd in options:
print(" - {cmd}".format(cmd=cmd))
print(" - {}".format(cmd))
print()


Expand All @@ -30,38 +30,36 @@ def compile_environment(cmd_env: Optional[dict], global_env: Optional[dict]) ->
return {}


def command_detail(command):
def command_detail(key, data):
print()
id = command.pop("id")
print("id: {}".format(id))
print(yaml.dump(command))
print(yaml.dump({key: data}))


def execute_from_config(cmd, args):
(config, cwd) = plz_config()

for task in config["commands"]:
if "id" in task and task["id"] == cmd:
if "cmd" in task:
if "dir" in task:
cwd = os.path.join(cwd or "", task["dir"])
kwargs = {
"cwd": cwd,
"args": args,
}
env = compile_environment(
task.get("env", {}), global_env=config.get("global_env", {})
)
if env:
kwargs["env"] = env
rc = gather_and_run_commands(task["cmd"], **kwargs)
sys.exit(rc)
data = config["commands"].get(cmd, None)
if data:
if "cmd" in data.keys():
if "dir" in data.keys():
cwd = os.path.join(cwd or "", data["dir"])
kwargs = {
"cwd": cwd,
"args": args,
}
env = compile_environment(
data.get("env", {}), global_env=config.get("global_env", {})
)
if env:
kwargs["env"] = env
rc = gather_and_run_commands(data["cmd"], **kwargs)
sys.exit(rc)
if cmd and cmd.lower() == "help":
if len(args) == 1:
for task in config["commands"]:
if "id" in task and task["id"] == args[0]:
command_detail(task)
sys.exit(0)
data = config["commands"].get(args[0], None)
if data:
command_detail(args[0], data)
sys.exit(0)

usage()
list_options(config)
Expand Down
25 changes: 8 additions & 17 deletions plz/schema.py
@@ -1,6 +1,4 @@
import re

from jsonschema import FormatChecker, exceptions, validate
from jsonschema import exceptions, validate

single_word_regex = "^[A-Za-z0-9_-]+$"

Expand All @@ -10,18 +8,9 @@
"additionalProperties": False,
}

plz_format_checker = FormatChecker()


@plz_format_checker.checks("single_word", TypeError)
def is_single_word(instance):
return type(instance) == str and re.match(single_word_regex, instance)


command_schema = {
"type": "object",
"properties": {
"id": {"type": "string", "format": "single_word"}, # todo: single_word_string
"cmd": {
"anyOf": [
{"type": "string"},
Expand All @@ -30,29 +19,31 @@ def is_single_word(instance):
},
"env": env_variable_dict,
},
"required": ["id", "cmd"],
"required": ["cmd"],
}

schema = {
"type": "object",
"properties": {
"commands": {
"type": "array",
"items": command_schema,
"type": "object",
"patternProperties": {single_word_regex: command_schema},
"additionalProperties": False,
},
"global_env": env_variable_dict,
"additionalProperties": False,
},
}


def validate_configuration_data(parsed_data):
try:
validate(parsed_data, schema, format_checker=plz_format_checker)
validate(parsed_data, schema)
except TypeError as e:
integer_message = "expected string or bytes-like object"
if str(e) == integer_message:
raise exceptions.ValidationError(
"Parsing exception: '{}'. Confirm all integer values in the .plz.yaml config are wrapped in quotes.".format(
"Parsing exception: '{}'. Confirm all integer values in the plz.yaml config are wrapped in quotes.".format(
integer_message
)
)
Expand Down
19 changes: 9 additions & 10 deletions tests/config_test.py
Expand Up @@ -142,16 +142,14 @@ def test_load_config_loads_yaml_file(mock_open):
# Arrange
mock_open.return_value = StringIO(
textwrap.dedent(
u"""
"""
commands:
- id: run
cmd: echo "./manage.py runserver"
run:
cmd: echo "./manage.py runserver"
"""
)
)
expected_result = {
"commands": [{"id": "run", "cmd": 'echo "./manage.py runserver"'}]
}
expected_result = {"commands": {"run": {"cmd": 'echo "./manage.py runserver"'}}}

# Act
result = load_config("path")
Expand All @@ -165,10 +163,11 @@ def test_load_config_aborts_if_bad_yaml_file(mock_open):
# Arrange
mock_open.return_value = StringIO(
textwrap.dedent(
u"""
- id: run
cmd: echo "./manage.py runserver"
foo
"""
commands:
run:
cmd: echo "./manage.py runserver"
foo
"""
)
)
Expand Down

0 comments on commit 50fcdfb

Please sign in to comment.