Merge "Reduce config error context"
This commit is contained in:
commit
f291e52850
|
@ -319,14 +319,33 @@ class ConfigurationSyntaxWarning(object, metaclass=abc.ABCMeta):
|
|||
zuul_error_message = 'Unknown Configuration Warning'
|
||||
|
||||
@abc.abstractmethod
|
||||
def makeConfigurationError(self, stanza, conf):
|
||||
def makeConfigurationError(self, stanza, conf, attr=None):
|
||||
"Return a ConfigurationError object"
|
||||
|
||||
|
||||
class DeprecationWarning(ConfigurationSyntaxWarning):
|
||||
def makeConfigurationError(self, stanza, conf):
|
||||
def makeConfigurationError(self, stanza, conf, attr=None):
|
||||
context = conf.get('_source_context')
|
||||
start_mark = conf.get('_start_mark')
|
||||
snippet = None
|
||||
name = conf.get('name')
|
||||
line = start_mark.line
|
||||
if name:
|
||||
name = f'"{name}"'
|
||||
else:
|
||||
name = 'following'
|
||||
if attr is not None:
|
||||
pointer = (
|
||||
f'The problem appears in the "{attr}" attribute\n'
|
||||
f'of the {name} {stanza} stanza:'
|
||||
)
|
||||
line = getattr(attr, 'line', line)
|
||||
else:
|
||||
pointer = (
|
||||
f'The problem appears in the the {name} {stanza} stanza:'
|
||||
)
|
||||
snippet = start_mark.getLineSnippet(line).rstrip()
|
||||
location = start_mark.getLineLocation(line)
|
||||
intro = textwrap.fill(textwrap.dedent("""\
|
||||
Zuul encountered a deprecated syntax while parsing its
|
||||
configuration in the repo {repo} on branch {branch}. The
|
||||
|
@ -340,17 +359,17 @@ class DeprecationWarning(ConfigurationSyntaxWarning):
|
|||
|
||||
{error}
|
||||
|
||||
The problem appears in the following {stanza} stanza:
|
||||
{pointer}
|
||||
|
||||
{content}
|
||||
|
||||
{start_mark}""")
|
||||
{location}""")
|
||||
|
||||
m = m.format(intro=intro,
|
||||
error=indent(self.zuul_error_message),
|
||||
stanza=stanza,
|
||||
content=indent(start_mark.snippet.rstrip()),
|
||||
start_mark=str(start_mark))
|
||||
pointer=pointer,
|
||||
content=indent(snippet),
|
||||
location=location)
|
||||
|
||||
return model.ConfigurationError(
|
||||
context, start_mark, m,
|
||||
|
@ -517,10 +536,16 @@ class LocalAccumulator:
|
|||
"""An error accumulator that wraps another accumulator (like
|
||||
LoadingErrors) while holding local context information.
|
||||
"""
|
||||
def __init__(self, accumulator, stanza, conf):
|
||||
def __init__(self, accumulator, stanza, conf, attr=None):
|
||||
self.accumulator = accumulator
|
||||
self.stanza = stanza
|
||||
self.conf = conf
|
||||
self.attr = attr
|
||||
|
||||
def extend(self, attr=None):
|
||||
"""Return a new accumulator that extends this one with additional
|
||||
info"""
|
||||
return LocalAccumulator(self.accumulator, self.stanza, self.conf, attr)
|
||||
|
||||
def addError(self, error):
|
||||
"""Add an error to the accumulator with the local context info.
|
||||
|
@ -532,10 +557,20 @@ class LocalAccumulator:
|
|||
exception handlers, so this method doesn't expect them.
|
||||
"""
|
||||
|
||||
e = error.makeConfigurationError(self.stanza, self.conf)
|
||||
e = error.makeConfigurationError(self.stanza, self.conf, self.attr)
|
||||
self.accumulator.addError(e)
|
||||
|
||||
|
||||
def get_conf_attr(conf, attr, accumulator=None):
|
||||
found = None
|
||||
for k in conf.keys():
|
||||
if k == attr:
|
||||
found = k
|
||||
break
|
||||
acc = accumulator.extend(found) if accumulator else None
|
||||
return acc, conf.get(found)
|
||||
|
||||
|
||||
class ZuulSafeLoader(yaml.EncryptedLoader):
|
||||
zuul_node_types = frozenset(('job', 'nodeset', 'secret', 'pipeline',
|
||||
'project', 'project-template',
|
||||
|
@ -569,6 +604,8 @@ class ZuulSafeLoader(yaml.EncryptedLoader):
|
|||
raise YAMLDuplicateKeyError(k.value, node, self.zuul_context,
|
||||
mark)
|
||||
keys.add(k.value)
|
||||
if k.tag == 'tag:yaml.org,2002:str':
|
||||
k.value = yaml.ZuulConfigKey(k.value, node.start_mark.line)
|
||||
r = super(ZuulSafeLoader, self).construct_mapping(node, deep)
|
||||
keys = frozenset(r.keys())
|
||||
if len(keys) == 1 and keys.intersection(self.zuul_node_types):
|
||||
|
@ -659,7 +696,8 @@ class PragmaParser(object):
|
|||
if bm is not None:
|
||||
source_context.implied_branch_matchers = bm
|
||||
|
||||
branches = conf.get('implied-branches')
|
||||
acc, branches = get_conf_attr(conf, 'implied-branches',
|
||||
error_accumulator)
|
||||
if branches is not None:
|
||||
# This is a BranchMatcher (not an ImpliedBranchMatcher)
|
||||
# because as user input, we allow/expect this to be
|
||||
|
@ -667,7 +705,7 @@ class PragmaParser(object):
|
|||
# (automatically generated from source file branches) are
|
||||
# ImpliedBranchMatchers.
|
||||
source_context.implied_branches = [
|
||||
change_matcher.BranchMatcher(make_regex(x, error_accumulator))
|
||||
change_matcher.BranchMatcher(make_regex(x, acc))
|
||||
for x in as_list(branches)]
|
||||
|
||||
|
||||
|
@ -1219,9 +1257,11 @@ class JobParser(object):
|
|||
|
||||
branches = None
|
||||
if 'branches' in conf:
|
||||
acc, conf_branches = get_conf_attr(conf, 'branches',
|
||||
error_accumulator)
|
||||
branches = [
|
||||
change_matcher.BranchMatcher(
|
||||
make_regex(x, error_accumulator))
|
||||
make_regex(x, acc))
|
||||
for x in as_list(conf['branches'])
|
||||
]
|
||||
elif not project_pipeline:
|
||||
|
|
|
@ -18,7 +18,13 @@ import voluptuous as v
|
|||
from zuul.trigger import BaseTrigger
|
||||
from zuul.driver.gerrit.gerritmodel import GerritEventFilter
|
||||
from zuul.driver.gerrit import gerritsource
|
||||
from zuul.driver.util import scalar_or_list, to_list, make_regex, ZUUL_REGEX
|
||||
from zuul.driver.util import (
|
||||
scalar_or_list,
|
||||
to_list,
|
||||
make_regex,
|
||||
ZUUL_REGEX,
|
||||
get_conf_attr,
|
||||
)
|
||||
from zuul.configloader import DeprecationWarning
|
||||
|
||||
|
||||
|
@ -58,11 +64,13 @@ class GerritTrigger(BaseTrigger):
|
|||
usernames = to_list(trigger.get('username_filter'))
|
||||
ignore_deletes = trigger.get('ignore-deletes', True)
|
||||
if 'require-approval' in trigger:
|
||||
error_accumulator.addError(
|
||||
GerritRequireApprovalDeprecation())
|
||||
acc, _ = get_conf_attr(trigger, 'require-approval',
|
||||
error_accumulator)
|
||||
acc.addError(GerritRequireApprovalDeprecation())
|
||||
if 'reject-approval' in trigger:
|
||||
error_accumulator.addError(
|
||||
GerritRejectApprovalDeprecation())
|
||||
acc, _ = get_conf_attr(trigger, 'reject-approval',
|
||||
error_accumulator)
|
||||
acc.addError(GerritRejectApprovalDeprecation())
|
||||
|
||||
types = [make_regex(x, error_accumulator)
|
||||
for x in to_list(trigger['event'])]
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import voluptuous as vs
|
||||
|
||||
from zuul.configloader import ZUUL_REGEX, make_regex # noqa
|
||||
from zuul.configloader import ZUUL_REGEX, make_regex, get_conf_attr # noqa
|
||||
|
||||
|
||||
def time_to_seconds(s):
|
||||
|
|
|
@ -77,6 +77,24 @@ class EncryptedPKCS1_OAEP:
|
|||
private_key).decode('utf8')
|
||||
|
||||
|
||||
class ZuulConfigKey(str):
|
||||
def __new__(cls, s, line):
|
||||
self = super().__new__(cls, s)
|
||||
self.line = line
|
||||
return self
|
||||
|
||||
def __copy__(self):
|
||||
return ZuulConfigKey(self, self.line)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return self.__copy__()
|
||||
|
||||
@classmethod
|
||||
def to_yaml(cls, dumper, data):
|
||||
return yaml.representer.SafeRepresenter.represent_str(
|
||||
dumper, str(data))
|
||||
|
||||
|
||||
def safe_load(stream, *args, **kwargs):
|
||||
return yaml.load(stream, *args, Loader=SafeLoader, **kwargs)
|
||||
|
||||
|
@ -102,6 +120,9 @@ EncryptedLoader.add_constructor(EncryptedPKCS1_OAEP.yaml_tag,
|
|||
EncryptedDumper.add_representer(
|
||||
types.MappingProxyType,
|
||||
yaml.representer.SafeRepresenter.represent_dict)
|
||||
EncryptedDumper.add_representer(
|
||||
ZuulConfigKey,
|
||||
ZuulConfigKey.to_yaml)
|
||||
|
||||
|
||||
def encrypted_dump(data, *args, **kwargs):
|
||||
|
|
|
@ -176,6 +176,25 @@ class ZuulMark:
|
|||
return (self.line == other.line and
|
||||
self.snippet == other.snippet)
|
||||
|
||||
line_snippet_context = 4
|
||||
|
||||
def getLineSnippet(self, line):
|
||||
start = max(line - self.line - self.line_snippet_context, 0)
|
||||
end = start + (self.line_snippet_context * 2) + 1
|
||||
all_lines = self.snippet.splitlines()
|
||||
lines = all_lines[start:end]
|
||||
if start > 0:
|
||||
lines.insert(0, '...')
|
||||
if end < len(all_lines):
|
||||
lines.append('...')
|
||||
return '\n'.join(lines)
|
||||
|
||||
def getLineLocation(self, line):
|
||||
return ' in "{name}", line {line}'.format(
|
||||
name=self.name,
|
||||
line=line + 1,
|
||||
)
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
|
|
Loading…
Reference in New Issue