Skip to content

Commit

Permalink
Merge pull request #2408 from VannTen/feat/pre-commit-hook
Browse files Browse the repository at this point in the history
turn validate-prescriptions into pre-commit hook
goern authored Jan 17, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 1f16619 + aa451ff commit ef051d9
Showing 3 changed files with 50 additions and 15 deletions.
10 changes: 10 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- id: validate-prescriptions
name: Validating prescriptions
description: Validates Thoth prescriptions file
language: python
entry: thoth-adviser validate-prescriptions --pre-commit yes
types:
- file
- non-executable
- text
- yaml
29 changes: 26 additions & 3 deletions thoth/adviser/cli.py
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
from typing import Any
from typing import Callable
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple

@@ -955,8 +956,18 @@ def dependency_monkey(
click_ctx.exit(int(exit_code != 0))


class _LogErrorCounter(logging.Handler):
def __init__(self):
super(_LogErrorCounter, self).__init__()
self.count = 0

def emit(self, record):
if record.levelno == logging.ERROR:
self.count += 1


@cli.command("validate-prescriptions")
@click.argument("prescriptions", nargs=1, metavar="PRESCRIPTION_DIR")
@click.argument("prescriptions", nargs=-1, metavar="PRESCRIPTION_DIR", type=click.Path(exists=True))
@click.option(
"--show-unit-names",
envvar="THOTH_ADVISER_VALIDATE_PRESCRIPTION_SHOW_UNIT_NAMES",
@@ -973,12 +984,24 @@ def dependency_monkey(
required=False,
help="Serialize validated prescriptions into an output pickle file.",
)
def validate_prescription(prescriptions: str, show_unit_names: bool, output: str) -> None:
@click.option("--pre-commit", envvar="PRE_COMMIT_MODE", type=bool, metavar="PRECOMMIT", required=False, default=False)
def validate_prescription(prescriptions: List[str], show_unit_names: bool, output: str, pre_commit: bool) -> None:
"""Validate the given prescription."""
if pre_commit:
_LOGGER.setLevel(logging.ERROR)
count_handler = _LogErrorCounter()
_LOGGER.addHandler(count_handler)
_LOGGER.info("Validating prescriptions in %r", prescriptions)
prescription = Prescription.validate(prescriptions)
prescription = Prescription.validate(prescriptions, not pre_commit)
_LOGGER.info("Prescriptions %r validated successfully", prescriptions)

if pre_commit:
if count_handler.count != 0:
print(f"{count_handler.count} errors in prescriptions files")
exit(1)
else:
exit(0)

result = {
"prescriptions": [{"name": p[0], "release": p[1]} for p in prescription.prescriptions],
"count": {
26 changes: 14 additions & 12 deletions thoth/adviser/prescription/v1/prescription.py
Original file line number Diff line number Diff line change
@@ -30,10 +30,13 @@
from typing import Tuple
from typing import Dict
from typing import Generator
from typing import Iterable
from typing import Optional
from typing import Type
from typing import TYPE_CHECKING

from voluptuous.humanize import humanize_error
import voluptuous
import attr

from ...exceptions import InternalError
@@ -109,11 +112,11 @@ def _validate_run_base(unit: Dict[str, Any]) -> bool:
return False

@classmethod
def validate(cls, prescriptions: str) -> "Prescription":
def validate(cls, prescriptions: Iterable[str], any_error_fatal: bool = True) -> "Prescription":
"""Validate the given prescription."""
_LOGGER.debug("Validating prescriptions schema")

prescription_instance = cls.load(prescriptions)
prescription_instance = cls.load(prescriptions, any_error_fatal)

# Verify semantics of prescription.
any_error = False
@@ -195,12 +198,8 @@ def from_dict(
"""
try:
PRESCRIPTION_SCHEMA(prescription)
except Exception as exc:
_LOGGER.exception(
"Failed to validate schema for prescription: %s",
str(exc),
)
raise PrescriptionSchemaError(str(exc))
except voluptuous.error.Invalid as exc:
raise PrescriptionSchemaError(humanize_error(prescription, exc))

boots_dict = prescription_instance.boots_dict if prescription_instance is not None else OrderedDict()
for boot_spec in prescription["units"].get("boots") or []:
@@ -279,7 +278,7 @@ def is_empty(self) -> bool:
)

@classmethod
def load(cls, *prescriptions: str) -> "Prescription":
def load(cls, prescriptions: Iterable[str], any_error_fatal: bool = True) -> "Prescription":
"""Load prescription from files or from their YAML representation."""
queue = deque([(p, cls._PRESCRIPTION_DEFAULT_NAME, cls._PRESCRIPTION_DEFAULT_RELEASE) for p in prescriptions])
prescription_instance = Prescription()
@@ -298,9 +297,12 @@ def load(cls, *prescriptions: str) -> "Prescription":
prescription_name=prescription_name,
prescription_release=prescription_release,
)
except Exception:
_LOGGER.error("Failed to load prescription from %r", prescription)
raise
except Exception as e:
if any_error_fatal:
_LOGGER.error("Failed to load prescription from %r", prescription)
raise
else:
_LOGGER.error("%s is invalid:\n%s", prescription, str(e))
elif prescription.endswith(".pickle"):
_LOGGER.debug("Loading prescriptions from %r", prescription)
with open(prescription, "rb") as fp:

0 comments on commit ef051d9

Please sign in to comment.