mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-16 01:50:39 +00:00
refactor: drop default standards version and rename to rules
This commit is contained in:
parent
7f5c2a51b1
commit
137e559d57
@ -7,8 +7,8 @@ import sys
|
||||
|
||||
from ansiblelater import LOG, __version__, logger
|
||||
from ansiblelater.candidate import Candidate
|
||||
from ansiblelater.rule import SingleRules
|
||||
from ansiblelater.settings import Settings
|
||||
from ansiblelater.standard import SingleStandards
|
||||
|
||||
|
||||
def main():
|
||||
@ -22,33 +22,33 @@ def main():
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--rules-dir",
|
||||
dest="rules.standards",
|
||||
metavar="RULES",
|
||||
dest="rules.dir",
|
||||
metavar="DIR",
|
||||
action="append",
|
||||
help="directory of standard rules",
|
||||
help="directory of rules",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-B",
|
||||
"--no-buildin",
|
||||
dest="rules.buildin",
|
||||
"--no-builtin",
|
||||
dest="rules.builtin",
|
||||
action="store_false",
|
||||
help="disables build-in standard rules",
|
||||
help="disables built-in rules",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--standards",
|
||||
dest="rules.filter",
|
||||
metavar="FILTER",
|
||||
"-i",
|
||||
"--include-rules",
|
||||
dest="rules.include_filter",
|
||||
metavar="TAGS",
|
||||
action="append",
|
||||
help="limit standards to given ID's",
|
||||
help="limit rules to given id/tags",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-x",
|
||||
"--exclude-standards",
|
||||
"--exclude-rules",
|
||||
dest="rules.exclude_filter",
|
||||
metavar="EXCLUDE_FILTER",
|
||||
metavar="TAGS",
|
||||
action="append",
|
||||
help="exclude standards by given ID's",
|
||||
help="exclude rules by given it/tags",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", dest="logging.level", action="append_const", const=-1, help="increase log level"
|
||||
@ -65,7 +65,7 @@ def main():
|
||||
config = settings.config
|
||||
|
||||
logger.update_logger(LOG, config["logging"]["level"], config["logging"]["json"])
|
||||
SingleStandards(config["rules"]["standards"])
|
||||
SingleRules(config["rules"]["dir"])
|
||||
|
||||
workers = max(multiprocessing.cpu_count() - 2, 2)
|
||||
p = multiprocessing.Pool(workers)
|
||||
|
@ -3,14 +3,12 @@
|
||||
import codecs
|
||||
import copy
|
||||
import os
|
||||
import re
|
||||
|
||||
from ansible.plugins.loader import module_loader
|
||||
from packaging.version import Version
|
||||
|
||||
from ansiblelater import LOG, utils
|
||||
from ansiblelater import LOG
|
||||
from ansiblelater.logger import flag_extra
|
||||
from ansiblelater.standard import SingleStandards, StandardBase
|
||||
from ansiblelater.rule import RuleBase, SingleRules
|
||||
|
||||
|
||||
class Candidate:
|
||||
@ -21,7 +19,7 @@ class Candidate:
|
||||
bundled with necessary meta informations for rule processing.
|
||||
"""
|
||||
|
||||
def __init__(self, filename, settings={}, standards=[]): # noqa
|
||||
def __init__(self, filename, settings={}, rules=[]): # noqa
|
||||
self.path = filename
|
||||
self.binary = False
|
||||
self.vault = False
|
||||
@ -37,163 +35,114 @@ class Candidate:
|
||||
except UnicodeDecodeError:
|
||||
self.binary = True
|
||||
|
||||
def _get_version(self):
|
||||
name = type(self).__name__
|
||||
path = self.path
|
||||
version = None
|
||||
config_version = self.config["rules"]["version"].strip()
|
||||
|
||||
if config_version:
|
||||
version_config_re = re.compile(r"([\d.]+)")
|
||||
match = version_config_re.match(config_version)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
|
||||
if not self.binary:
|
||||
if isinstance(self, RoleFile):
|
||||
parentdir = os.path.dirname(os.path.abspath(self.path))
|
||||
while parentdir != os.path.dirname(parentdir):
|
||||
meta_file = os.path.join(parentdir, "meta", "main.yml")
|
||||
if os.path.exists(meta_file):
|
||||
path = meta_file
|
||||
break
|
||||
parentdir = os.path.dirname(parentdir)
|
||||
|
||||
version_file_re = re.compile(r"^# Standards:\s*([\d.]+)")
|
||||
with codecs.open(path, mode="rb", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
match = version_file_re.match(line)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
|
||||
if version:
|
||||
LOG.info(f"{name} {path} declares standards version {version}")
|
||||
|
||||
return version
|
||||
|
||||
def _filter_standards(self):
|
||||
target_standards = []
|
||||
includes = self.config["rules"]["filter"]
|
||||
def _filter_rules(self):
|
||||
target_rules = []
|
||||
includes = self.config["rules"]["include_filter"]
|
||||
excludes = self.config["rules"]["exclude_filter"]
|
||||
|
||||
if len(includes) == 0:
|
||||
includes = [s.sid for s in self.standards]
|
||||
includes = [s.sid for s in self.rules]
|
||||
|
||||
for standard in self.standards:
|
||||
if standard.sid in includes and standard.sid not in excludes:
|
||||
target_standards.append(standard)
|
||||
for rule in self.rules:
|
||||
if rule.sid in includes and rule.sid not in excludes:
|
||||
target_rules.append(rule)
|
||||
|
||||
return target_standards
|
||||
return target_rules
|
||||
|
||||
def review(self):
|
||||
errors = 0
|
||||
self.standards = SingleStandards(self.config["rules"]["standards"]).rules
|
||||
self.version_config = self._get_version()
|
||||
self.version = self.version_config or utils.standards_latest(self.standards)
|
||||
self.rules = SingleRules(self.config["rules"]["dir"]).rules
|
||||
|
||||
for standard in self._filter_standards():
|
||||
if type(self).__name__.lower() not in standard.types:
|
||||
for rule in self._filter_rules():
|
||||
if type(self).__name__.lower() not in rule.types:
|
||||
continue
|
||||
|
||||
result = standard.check(self, self.config)
|
||||
result = rule.check(self, self.config)
|
||||
|
||||
if not result:
|
||||
LOG.error(
|
||||
f"Standard '{standard.sid}' returns an empty result object. Check failed!"
|
||||
)
|
||||
LOG.error(f"rule '{rule.sid}' returns an empty result object. Check failed!")
|
||||
continue
|
||||
|
||||
labels = {
|
||||
"tag": "review",
|
||||
"standard": standard.description,
|
||||
"rule": rule.description,
|
||||
"file": self.path,
|
||||
"passed": True,
|
||||
}
|
||||
|
||||
if standard.sid and standard.sid.strip():
|
||||
labels["sid"] = standard.sid
|
||||
if rule.sid and rule.sid.strip():
|
||||
labels["sid"] = rule.sid
|
||||
|
||||
for err in result.errors:
|
||||
err_labels = copy.copy(labels)
|
||||
err_labels["passed"] = False
|
||||
|
||||
sid = self._format_id(standard.sid)
|
||||
sid = self._format_id(rule.sid)
|
||||
path = self.path
|
||||
description = standard.description
|
||||
description = rule.description
|
||||
|
||||
if isinstance(err, StandardBase.Error):
|
||||
if isinstance(err, RuleBase.Error):
|
||||
err_labels.update(err.to_dict())
|
||||
|
||||
if not standard.version:
|
||||
LOG.warning(
|
||||
f"{sid}Best practice '{description}' not met:\n{path}:{err}",
|
||||
extra=flag_extra(err_labels),
|
||||
)
|
||||
elif Version(standard.version) > Version(self.version):
|
||||
LOG.warning(
|
||||
f"{sid}Future standard '{description}' not met:\n{path}:{err}",
|
||||
extra=flag_extra(err_labels),
|
||||
)
|
||||
else:
|
||||
msg = f"{sid}Standard '{description}' not met:\n{path}:{err}"
|
||||
msg = f"{sid}rule '{description}' not met:\n{path}:{err}"
|
||||
|
||||
if standard.sid not in self.config["rules"]["warning_filter"]:
|
||||
LOG.error(msg, extra=flag_extra(err_labels))
|
||||
errors = errors + 1
|
||||
else:
|
||||
LOG.warning(msg, extra=flag_extra(err_labels))
|
||||
if rule.sid not in self.config["rules"]["warning_filter"]:
|
||||
LOG.error(msg, extra=flag_extra(err_labels))
|
||||
errors = errors + 1
|
||||
else:
|
||||
LOG.warning(msg, extra=flag_extra(err_labels))
|
||||
|
||||
return errors
|
||||
|
||||
@staticmethod
|
||||
def classify(filename, settings={}, standards=[]): # noqa
|
||||
def classify(filename, settings={}, rules=[]): # noqa
|
||||
parentdir = os.path.basename(os.path.dirname(filename))
|
||||
basename = os.path.basename(filename)
|
||||
ext = os.path.splitext(filename)[1][1:]
|
||||
|
||||
if parentdir in ["tasks"]:
|
||||
return Task(filename, settings, standards)
|
||||
return Task(filename, settings, rules)
|
||||
if parentdir in ["handlers"]:
|
||||
return Handler(filename, settings, standards)
|
||||
return Handler(filename, settings, rules)
|
||||
if parentdir in ["vars", "defaults"]:
|
||||
return RoleVars(filename, settings, standards)
|
||||
return RoleVars(filename, settings, rules)
|
||||
if "group_vars" in filename.split(os.sep):
|
||||
return GroupVars(filename, settings, standards)
|
||||
return GroupVars(filename, settings, rules)
|
||||
if "host_vars" in filename.split(os.sep):
|
||||
return HostVars(filename, settings, standards)
|
||||
return HostVars(filename, settings, rules)
|
||||
if parentdir in ["meta"] and "main" in basename:
|
||||
return Meta(filename, settings, standards)
|
||||
return Meta(filename, settings, rules)
|
||||
if parentdir in ["meta"] and "argument_specs" in basename:
|
||||
return ArgumentSpecs(filename, settings, standards)
|
||||
return ArgumentSpecs(filename, settings, rules)
|
||||
if parentdir in [
|
||||
"library",
|
||||
"lookup_plugins",
|
||||
"callback_plugins",
|
||||
"filter_plugins",
|
||||
] or filename.endswith(".py"):
|
||||
return Code(filename, settings, standards)
|
||||
return Code(filename, settings, rules)
|
||||
if basename == "inventory" or basename == "hosts" or parentdir in ["inventories"]:
|
||||
return Inventory(filename, settings, standards)
|
||||
return Inventory(filename, settings, rules)
|
||||
if "rolesfile" in basename or ("requirements" in basename and ext in ["yaml", "yml"]):
|
||||
return Rolesfile(filename, settings, standards)
|
||||
return Rolesfile(filename, settings, rules)
|
||||
if "Makefile" in basename:
|
||||
return Makefile(filename, settings, standards)
|
||||
return Makefile(filename, settings, rules)
|
||||
if "templates" in filename.split(os.sep) or basename.endswith(".j2"):
|
||||
return Template(filename, settings, standards)
|
||||
return Template(filename, settings, rules)
|
||||
if "files" in filename.split(os.sep):
|
||||
return File(filename, settings, standards)
|
||||
return File(filename, settings, rules)
|
||||
if basename.endswith(".yml") or basename.endswith(".yaml"):
|
||||
return Playbook(filename, settings, standards)
|
||||
return Playbook(filename, settings, rules)
|
||||
if "README" in basename:
|
||||
return Doc(filename, settings, standards)
|
||||
return Doc(filename, settings, rules)
|
||||
return None
|
||||
|
||||
def _format_id(self, standard_id):
|
||||
sid = standard_id.strip()
|
||||
def _format_id(self, rule_id):
|
||||
sid = rule_id.strip()
|
||||
if sid:
|
||||
standard_id = f"[{sid}] "
|
||||
rule_id = f"[{sid}] "
|
||||
|
||||
return standard_id
|
||||
return rule_id
|
||||
|
||||
def __repr__(self):
|
||||
return f"{type(self).__name__} ({self.path})"
|
||||
@ -205,8 +154,8 @@ class Candidate:
|
||||
class RoleFile(Candidate):
|
||||
"""Object classified as Ansible role file."""
|
||||
|
||||
def __init__(self, filename, settings={}, standards=[]): # noqa
|
||||
super().__init__(filename, settings, standards)
|
||||
def __init__(self, filename, settings={}, rules=[]): # noqa
|
||||
super().__init__(filename, settings, rules)
|
||||
|
||||
parentdir = os.path.dirname(os.path.abspath(filename))
|
||||
while parentdir != os.path.dirname(parentdir):
|
||||
@ -226,16 +175,16 @@ class Playbook(Candidate):
|
||||
class Task(RoleFile):
|
||||
"""Object classified as Ansible task file."""
|
||||
|
||||
def __init__(self, filename, settings={}, standards=[]): # noqa
|
||||
super().__init__(filename, settings, standards)
|
||||
def __init__(self, filename, settings={}, rules=[]): # noqa
|
||||
super().__init__(filename, settings, rules)
|
||||
self.filetype = "tasks"
|
||||
|
||||
|
||||
class Handler(RoleFile):
|
||||
"""Object classified as Ansible handler file."""
|
||||
|
||||
def __init__(self, filename, settings={}, standards=[]): # noqa
|
||||
super().__init__(filename, settings, standards)
|
||||
def __init__(self, filename, settings={}, rules=[]): # noqa
|
||||
super().__init__(filename, settings, rules)
|
||||
self.filetype = "handlers"
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
"""Standard definition."""
|
||||
"""Rule definition."""
|
||||
|
||||
import copy
|
||||
import importlib
|
||||
@ -27,22 +27,21 @@ from ansiblelater.utils.yamlhelper import (
|
||||
)
|
||||
|
||||
|
||||
class StandardMeta(type):
|
||||
class RuleMeta(type):
|
||||
def __call__(cls, *args):
|
||||
mcls = type.__call__(cls, *args)
|
||||
mcls.sid = cls.sid
|
||||
mcls.description = getattr(cls, "description", "__unknown__")
|
||||
mcls.helptext = getattr(cls, "helptext", "")
|
||||
mcls.version = getattr(cls, "version", None)
|
||||
mcls.types = getattr(cls, "types", [])
|
||||
return mcls
|
||||
|
||||
|
||||
class StandardExtendedMeta(StandardMeta, ABCMeta):
|
||||
class RuleExtendedMeta(RuleMeta, ABCMeta):
|
||||
pass
|
||||
|
||||
|
||||
class StandardBase(metaclass=StandardExtendedMeta):
|
||||
class RuleBase(metaclass=RuleExtendedMeta):
|
||||
SHELL_PIPE_CHARS = "&|<>;$\n*[]{}?"
|
||||
|
||||
@property
|
||||
@ -55,7 +54,7 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return f"Standard: {self.description} (version: {self.version}, types: {self.types})"
|
||||
return f"Rule: {self.description} (types: {self.types})"
|
||||
|
||||
@staticmethod
|
||||
def get_tasks(candidate, settings): # noqa
|
||||
@ -69,11 +68,11 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
except LaterError as ex:
|
||||
e = ex.original
|
||||
errors.append(
|
||||
StandardBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
RuleBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
)
|
||||
candidate.faulty = True
|
||||
except LaterAnsibleError as e:
|
||||
errors.append(StandardBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
errors.append(RuleBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
candidate.faulty = True
|
||||
|
||||
return yamllines, errors
|
||||
@ -93,11 +92,11 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
except LaterError as ex:
|
||||
e = ex.original
|
||||
errors.append(
|
||||
StandardBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
RuleBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
)
|
||||
candidate.faulty = True
|
||||
except LaterAnsibleError as e:
|
||||
errors.append(StandardBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
errors.append(RuleBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
candidate.faulty = True
|
||||
|
||||
return tasks, errors
|
||||
@ -115,11 +114,11 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
except LaterError as ex:
|
||||
e = ex.original
|
||||
errors.append(
|
||||
StandardBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
RuleBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
)
|
||||
candidate.faulty = True
|
||||
except LaterAnsibleError as e:
|
||||
errors.append(StandardBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
errors.append(RuleBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
candidate.faulty = True
|
||||
|
||||
return normalized, errors
|
||||
@ -159,11 +158,11 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
except LaterError as ex:
|
||||
e = ex.original
|
||||
errors.append(
|
||||
StandardBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
RuleBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
)
|
||||
candidate.faulty = True
|
||||
except LaterAnsibleError as e:
|
||||
errors.append(StandardBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
errors.append(RuleBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
candidate.faulty = True
|
||||
|
||||
return normalized, errors
|
||||
@ -184,11 +183,11 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
except LaterError as ex:
|
||||
e = ex.original
|
||||
errors.append(
|
||||
StandardBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
RuleBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
)
|
||||
candidate.faulty = True
|
||||
except LaterAnsibleError as e:
|
||||
errors.append(StandardBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
errors.append(RuleBase.Error(e.line, f"syntax error: {e.message}"))
|
||||
candidate.faulty = True
|
||||
|
||||
return yamllines, errors
|
||||
@ -210,7 +209,7 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
content = yaml.safe_load(f)
|
||||
except yaml.YAMLError as e:
|
||||
errors.append(
|
||||
StandardBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
RuleBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
)
|
||||
candidate.faulty = True
|
||||
|
||||
@ -224,14 +223,14 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
try:
|
||||
with open(candidate.path, encoding="utf-8") as f:
|
||||
for problem in linter.run(f, YamlLintConfig(options)):
|
||||
errors.append(StandardBase.Error(problem.line, problem.desc))
|
||||
errors.append(RuleBase.Error(problem.line, problem.desc))
|
||||
except yaml.YAMLError as e:
|
||||
errors.append(
|
||||
StandardBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
RuleBase.Error(e.problem_mark.line + 1, f"syntax error: {e.problem}")
|
||||
)
|
||||
candidate.faulty = True
|
||||
except (TypeError, ValueError) as e:
|
||||
errors.append(StandardBase.Error(None, f"yamllint error: {e}"))
|
||||
errors.append(RuleBase.Error(None, f"yamllint error: {e}"))
|
||||
candidate.faulty = True
|
||||
|
||||
return errors
|
||||
@ -302,7 +301,7 @@ class StandardBase(metaclass=StandardExtendedMeta):
|
||||
return "\n".join([f"{self.candidate}:{error}" for error in self.errors])
|
||||
|
||||
|
||||
class StandardLoader:
|
||||
class RulesLoader:
|
||||
def __init__(self, source):
|
||||
self.rules = []
|
||||
|
||||
@ -331,10 +330,7 @@ class StandardLoader:
|
||||
|
||||
def _is_plugin(self, obj):
|
||||
return (
|
||||
inspect.isclass(obj)
|
||||
and issubclass(obj, StandardBase)
|
||||
and obj is not StandardBase
|
||||
and not None
|
||||
inspect.isclass(obj) and issubclass(obj, RuleBase) and obj is not RuleBase and not None
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
@ -343,11 +339,11 @@ class StandardLoader:
|
||||
all_std = len(normalized_std)
|
||||
if all_std != unique_std:
|
||||
sysexit_with_message(
|
||||
"Detect duplicate ID's in standards definition. Please use unique ID's only."
|
||||
"Found duplicate tags in rules definition. Please use unique tags only."
|
||||
)
|
||||
|
||||
|
||||
class SingleStandards(StandardLoader, metaclass=Singleton):
|
||||
class SingleRules(RulesLoader, metaclass=Singleton):
|
||||
"""Singleton config class."""
|
||||
|
||||
pass
|
@ -1,11 +1,10 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckBecomeUser(StandardBase):
|
||||
class CheckBecomeUser(RuleBase):
|
||||
sid = "ANSIBLE0015"
|
||||
description = "Become should be combined with become_user"
|
||||
helptext = "the task has `become` enabled but `become_user` is missing"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,14 +1,13 @@
|
||||
import re
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
from ansiblelater.utils import count_spaces
|
||||
|
||||
|
||||
class CheckBracesSpaces(StandardBase):
|
||||
class CheckBracesSpaces(RuleBase):
|
||||
sid = "ANSIBLE0004"
|
||||
description = "YAML should use consistent number of spaces around variables"
|
||||
helptext = "no suitable numbers of spaces (min: {min} max: {max})"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -18,14 +18,13 @@
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckChangedInWhen(StandardBase):
|
||||
class CheckChangedInWhen(RuleBase):
|
||||
sid = "ANSIBLE0026"
|
||||
description = "Use handlers instead of `when: changed`"
|
||||
helptext = "tasks using `when: result.changed` setting are effectively acting as a handler"
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,14 +1,13 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckCommandHasChanges(StandardBase):
|
||||
class CheckCommandHasChanges(RuleBase):
|
||||
sid = "ANSIBLE0011"
|
||||
description = "Commands should be idempotent"
|
||||
helptext = (
|
||||
"commands should only read while using `changed_when` or try to be "
|
||||
"idempotent while using controls like `creates`, `removes` or `when`"
|
||||
)
|
||||
version = "0.1"
|
||||
types = ["playbook", "task"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -20,14 +20,13 @@
|
||||
|
||||
import os
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckCommandInsteadOfArgument(StandardBase):
|
||||
class CheckCommandInsteadOfArgument(RuleBase):
|
||||
sid = "ANSIBLE0017"
|
||||
description = "Commands should not be used in place of module arguments"
|
||||
helptext = "{exec} used in place of file modules argument {arg}"
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
import os
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckCommandInsteadOfModule(StandardBase):
|
||||
class CheckCommandInsteadOfModule(RuleBase):
|
||||
sid = "ANSIBLE0008"
|
||||
description = "Commands should not be used in place of modules"
|
||||
helptext = "{exec} command used in place of {module} module"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,14 +1,13 @@
|
||||
import re
|
||||
|
||||
from ansiblelater.candidate import Template
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckCompareToEmptyString(StandardBase):
|
||||
class CheckCompareToEmptyString(RuleBase):
|
||||
sid = "ANSIBLE0012"
|
||||
description = 'Don\'t compare to empty string ""'
|
||||
helptext = "use `when: var` rather than `when: var !=` (or conversely `when: not var`)"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "template"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,14 +1,13 @@
|
||||
import re
|
||||
|
||||
from ansiblelater.candidate import Template
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckCompareToLiteralBool(StandardBase):
|
||||
class CheckCompareToLiteralBool(RuleBase):
|
||||
sid = "ANSIBLE0013"
|
||||
description = "Don't compare to True or False"
|
||||
helptext = "use `when: var` rather than `when: var == True` (or conversely `when: not var`)"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,11 +1,10 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckDeprecated(StandardBase):
|
||||
class CheckDeprecated(RuleBase):
|
||||
sid = "ANSIBLE9999"
|
||||
description = "Deprecated features should not be used"
|
||||
helptext = "'{old}' is deprecated and should not be used anymore. Use '{new}' instead."
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -20,18 +20,17 @@
|
||||
|
||||
import os
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
from ansiblelater.utils import has_glob, has_jinja
|
||||
|
||||
|
||||
class CheckDeprecatedBareVars(StandardBase):
|
||||
class CheckDeprecatedBareVars(RuleBase):
|
||||
sid = "ANSIBLE0027"
|
||||
description = "Deprecated bare variables in loops must not be used"
|
||||
helptext = (
|
||||
"bare var '{barevar}' in '{loop_type}' must use full var syntax '{{{{ {barevar} }}}}' "
|
||||
"or be converted to a list"
|
||||
)
|
||||
version = "0.3"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -19,17 +19,16 @@
|
||||
# THE SOFTWARE.
|
||||
import re
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckFilePermissionMissing(StandardBase):
|
||||
class CheckFilePermissionMissing(RuleBase):
|
||||
sid = "ANSIBLE0018"
|
||||
description = "File permissions unset or incorrect"
|
||||
helptext = (
|
||||
"`mode` parameter should set permissions explicitly (e.g. `mode: 0644`) "
|
||||
"to avoid unexpected file permissions"
|
||||
)
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
_modules = {
|
||||
|
@ -18,14 +18,13 @@
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckFilePermissionOctal(StandardBase):
|
||||
class CheckFilePermissionOctal(RuleBase):
|
||||
sid = "ANSIBLE0019"
|
||||
description = "Octal file permissions must contain leading zero or be a string"
|
||||
helptext = "numeric file permissions without leading zero can behave in unexpected ways"
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
import re
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckFilterSeparation(StandardBase):
|
||||
class CheckFilterSeparation(RuleBase):
|
||||
sid = "ANSIBLE0016"
|
||||
description = "Jinja2 filters should be separated with spaces"
|
||||
helptext = "no suitable numbers of spaces (required: 1)"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -18,14 +18,13 @@
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckGitHasVersion(StandardBase):
|
||||
class CheckGitHasVersion(RuleBase):
|
||||
sid = "ANSIBLE0020"
|
||||
description = "Git checkouts should use explicit version"
|
||||
helptext = "git checkouts should point to an explicit commit or tag, not `latest`"
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,11 +1,10 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckInstallUseLatest(StandardBase):
|
||||
class CheckInstallUseLatest(RuleBase):
|
||||
sid = "ANSIBLE0009"
|
||||
description = "Package installs should use present, not latest"
|
||||
helptext = "package installs should use `state=present` with or without a version"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
import re
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckLiteralBoolFormat(StandardBase):
|
||||
class CheckLiteralBoolFormat(RuleBase):
|
||||
sid = "ANSIBLE0014"
|
||||
description = "Literal bools should be consistent"
|
||||
helptext = "literal bools should be written as `{bools}`"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
# Copyright (c) 2016, Tsukinowa Inc. <info@tsukinowa.jp>
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckLocalAction(StandardBase):
|
||||
class CheckLocalAction(RuleBase):
|
||||
sid = "ANSIBLE0024"
|
||||
description = "Don't use local_action"
|
||||
helptext = "`delegate_to: localhost` should be used instead of `local_action`"
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,14 +1,13 @@
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
from nested_lookup import nested_lookup
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckMetaChangeFromDefault(StandardBase):
|
||||
class CheckMetaChangeFromDefault(RuleBase):
|
||||
sid = "ANSIBLE0021"
|
||||
description = "Roles meta/main.yml default values should be changed"
|
||||
helptext = "meta/main.yml default values should be changed for: `{field}`"
|
||||
version = "0.2"
|
||||
types = ["meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
from nested_lookup import nested_lookup
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckMetaMain(StandardBase):
|
||||
class CheckMetaMain(RuleBase):
|
||||
sid = "ANSIBLE0002"
|
||||
description = "Roles must contain suitable meta/main.yml"
|
||||
helptext = "file should contain `{key}` key"
|
||||
version = "0.1"
|
||||
types = ["meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckNameFormat(StandardBase):
|
||||
class CheckNameFormat(RuleBase):
|
||||
sid = "ANSIBLE0007"
|
||||
description = "Name of tasks and handlers must be formatted"
|
||||
helptext = "name '{name}' should start with uppercase"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,11 +1,10 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckNamedTask(StandardBase):
|
||||
class CheckNamedTask(RuleBase):
|
||||
sid = "ANSIBLE0006"
|
||||
description = "Tasks and handlers must be named"
|
||||
helptext = "module '{module}' used without or empty `name` attribute"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,11 +1,10 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckNativeYaml(StandardBase):
|
||||
class CheckNativeYaml(RuleBase):
|
||||
sid = "LINT0008"
|
||||
description = "Use YAML format for tasks and handlers rather than key=value"
|
||||
helptext = "task arguments appear to be in key value rather than YAML format"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -21,17 +21,16 @@
|
||||
# THE SOFTWARE.
|
||||
import re
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckNestedJinja(StandardBase):
|
||||
class CheckNestedJinja(RuleBase):
|
||||
sid = "ANSIBLE0023"
|
||||
description = "Don't use nested Jinja2 pattern"
|
||||
helptext = (
|
||||
"there should not be any nested jinja pattern "
|
||||
"like `{{ list_one + {{ list_two | max }} }}`"
|
||||
)
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
# Copyright (c) 2016, Tsukinowa Inc. <info@tsukinowa.jp>
|
||||
# Copyright (c) 2018, Ansible Project
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckRelativeRolePaths(StandardBase):
|
||||
class CheckRelativeRolePaths(RuleBase):
|
||||
sid = "ANSIBLE0025"
|
||||
description = "Don't use a relative path in a role"
|
||||
helptext = "`copy` and `template` modules don't need relative path for `src`"
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
from ansible.parsing.yaml.objects import AnsibleMapping
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckScmInSrc(StandardBase):
|
||||
class CheckScmInSrc(RuleBase):
|
||||
sid = "ANSIBLE0005"
|
||||
description = "Use `scm:` key rather than `src: scm+url`"
|
||||
helptext = "usage of `src: scm+url` not recommended"
|
||||
version = "0.1"
|
||||
types = ["rolesfile"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,11 +1,10 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckShellInsteadCommand(StandardBase):
|
||||
class CheckShellInsteadCommand(RuleBase):
|
||||
sid = "ANSIBLE0010"
|
||||
description = "Shell should only be used when essential"
|
||||
helptext = "shell should only be used when piping, redirecting or chaining commands"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,14 +1,13 @@
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckTaskSeparation(StandardBase):
|
||||
class CheckTaskSeparation(RuleBase):
|
||||
sid = "ANSIBLE0001"
|
||||
description = "Single tasks should be separated by empty line"
|
||||
helptext = "missing task separation (required: 1 empty line)"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckUniqueNamedTask(StandardBase):
|
||||
class CheckUniqueNamedTask(RuleBase):
|
||||
sid = "ANSIBLE0003"
|
||||
description = "Tasks and handlers must be uniquely named within a single file"
|
||||
helptext = "name '{name}' appears multiple times"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,16 +0,0 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
|
||||
|
||||
class CheckVersion(StandardBase):
|
||||
sid = "ANSIBLE9998"
|
||||
description = "Standards version should be pinned"
|
||||
helptext = "Standards version not set. Using latest standards version {version}"
|
||||
types = ["task", "handler", "rolevars", "meta", "template", "file", "playbook"]
|
||||
|
||||
def check(self, candidate, settings): # noqa
|
||||
errors = []
|
||||
|
||||
if not candidate.version_config:
|
||||
errors.append(self.Error(None, self.helptext.format(version=candidate.version)))
|
||||
|
||||
return self.Result(candidate.path, errors)
|
@ -1,13 +1,12 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckWhenFormat(StandardBase):
|
||||
class CheckWhenFormat(RuleBase):
|
||||
sid = "ANSIBLE0022"
|
||||
description = "Don't use Jinja2 in when"
|
||||
helptext = (
|
||||
"`when` is a raw Jinja2 expression, redundant {{ }} " "should be removed from variable(s)"
|
||||
)
|
||||
version = "0.2"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,10 +1,9 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlColons(StandardBase):
|
||||
class CheckYamlColons(RuleBase):
|
||||
sid = "LINT0005"
|
||||
description = "YAML should use consistent number of spaces around colons"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,10 +1,9 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlDocumentEnd(StandardBase):
|
||||
class CheckYamlDocumentEnd(RuleBase):
|
||||
sid = "LINT0009"
|
||||
description = "YAML should contain document end marker"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,10 +1,9 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlDocumentStart(StandardBase):
|
||||
class CheckYamlDocumentStart(RuleBase):
|
||||
sid = "LINT0004"
|
||||
description = "YAML should contain document start marker"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,10 +1,9 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlEmptyLines(StandardBase):
|
||||
class CheckYamlEmptyLines(RuleBase):
|
||||
sid = "LINT0001"
|
||||
description = "YAML should not contain unnecessarily empty lines"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,13 +1,12 @@
|
||||
import os
|
||||
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlFile(StandardBase):
|
||||
class CheckYamlFile(RuleBase):
|
||||
sid = "LINT0006"
|
||||
description = "Roles file should be in yaml format"
|
||||
helptext = "file does not have a .yml extension"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,11 +1,10 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlHasContent(StandardBase):
|
||||
class CheckYamlHasContent(RuleBase):
|
||||
sid = "LINT0007"
|
||||
description = "Files should contain useful content"
|
||||
helptext = "the file appears to have no useful content"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "defaults", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,10 +1,9 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlHyphens(StandardBase):
|
||||
class CheckYamlHyphens(RuleBase):
|
||||
sid = "LINT0003"
|
||||
description = "YAML should use consistent number of spaces after hyphens"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -1,10 +1,9 @@
|
||||
from ansiblelater.standard import StandardBase
|
||||
from ansiblelater.rule import RuleBase
|
||||
|
||||
|
||||
class CheckYamlIndent(StandardBase):
|
||||
class CheckYamlIndent(RuleBase):
|
||||
sid = "LINT0002"
|
||||
description = "YAML should not contain unnecessarily empty lines"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars", "meta"]
|
||||
|
||||
def check(self, candidate, settings):
|
||||
|
@ -104,13 +104,13 @@ class Settings:
|
||||
if f not in defaults["ansible"]["custom_modules"]:
|
||||
defaults["ansible"]["custom_modules"].append(f)
|
||||
|
||||
if defaults["rules"]["buildin"]:
|
||||
defaults["rules"]["standards"].append(
|
||||
if defaults["rules"]["builtin"]:
|
||||
defaults["rules"]["dir"].append(
|
||||
os.path.join(resource_filename("ansiblelater", "rules"))
|
||||
)
|
||||
|
||||
defaults["rules"]["standards"] = [
|
||||
os.path.relpath(os.path.normpath(p)) for p in defaults["rules"]["standards"]
|
||||
defaults["rules"]["dir"] = [
|
||||
os.path.relpath(os.path.normpath(p)) for p in defaults["rules"]["dir"]
|
||||
]
|
||||
|
||||
return defaults
|
||||
@ -118,9 +118,9 @@ class Settings:
|
||||
def _get_defaults(self):
|
||||
defaults = {
|
||||
"rules": {
|
||||
"buildin": True,
|
||||
"standards": [],
|
||||
"filter": [],
|
||||
"builtin": True,
|
||||
"dir": [],
|
||||
"include_filter": [],
|
||||
"exclude_filter": [],
|
||||
"warning_filter": [
|
||||
"ANSIBLE9999",
|
||||
@ -128,7 +128,6 @@ class Settings:
|
||||
],
|
||||
"ignore_dotfiles": True,
|
||||
"exclude_files": [],
|
||||
"version": "",
|
||||
},
|
||||
"logging": {
|
||||
"level": "WARNING",
|
||||
|
@ -6,7 +6,6 @@ import sys
|
||||
from contextlib import suppress
|
||||
|
||||
import yaml
|
||||
from packaging.version import Version
|
||||
|
||||
from ansiblelater import logger
|
||||
|
||||
@ -35,12 +34,6 @@ def count_spaces(c_string):
|
||||
return (leading_spaces, trailing_spaces)
|
||||
|
||||
|
||||
def standards_latest(standards):
|
||||
return max(
|
||||
[standard.version for standard in standards if standard.version] or ["0.1"], key=Version
|
||||
)
|
||||
|
||||
|
||||
def lines_ranges(lines_spec):
|
||||
if not lines_spec:
|
||||
return None
|
||||
|
@ -1,18 +1,17 @@
|
||||
---
|
||||
title: Minimal standard checks
|
||||
title: Write a rule
|
||||
---
|
||||
|
||||
A typical standards check will look like:
|
||||
A typical rule check will look like:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- spellchecker-disable -->
|
||||
{{< highlight Python "linenos=table" >}}
|
||||
class CheckBecomeUser(StandardBase):
|
||||
class CheckBecomeUser(RuleBase):
|
||||
|
||||
sid = "ANSIBLE0015"
|
||||
description = "Become should be combined with become_user"
|
||||
helptext = "the task has `become` enabled but `become_user` is missing"
|
||||
version = "0.1"
|
||||
types = ["playbook", "task", "handler"]
|
||||
|
||||
def check(self, candidate, settings):
|
@ -8,28 +8,27 @@ You can get all available CLI options by running `ansible-later --help`:
|
||||
<!-- spellchecker-disable -->
|
||||
{{< highlight Shell "linenos=table" >}}
|
||||
$ ansible-later --help
|
||||
usage: ansible-later [-h] [-c CONFIG_FILE] [-r RULES.STANDARDS]
|
||||
[-s RULES.FILTER] [-v] [-q] [--version]
|
||||
[rules.files [rules.files ...]]
|
||||
usage: ansible-later [-h] [-c CONFIG] [-r DIR] [-B] [-i TAGS] [-x TAGS] [-v] [-q] [-V] [rules.files ...]
|
||||
|
||||
Validate Ansible files against best practice guideline
|
||||
|
||||
positional arguments:
|
||||
rules.files
|
||||
|
||||
optional arguments:
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-c CONFIG_FILE, --config CONFIG_FILE
|
||||
location of configuration file
|
||||
-r RULES.STANDARDS, --rules RULES.STANDARDS
|
||||
location of standards rules
|
||||
-s RULES.FILTER, --standards RULES.FILTER
|
||||
limit standards to given ID's
|
||||
-x RULES.EXCLUDE_FILTER, --exclude-standards RULES.EXCLUDE_FILTER
|
||||
exclude standards by given ID's
|
||||
-c CONFIG, --config CONFIG
|
||||
path to configuration file
|
||||
-r DIR, --rules-dir DIR
|
||||
directory of rules
|
||||
-B, --no-builtin disables built-in rules
|
||||
-i TAGS, --include-rules TAGS
|
||||
limit rules to given id/tags
|
||||
-x TAGS, --exclude-rules TAGS
|
||||
exclude rules by given it/tags
|
||||
-v increase log level
|
||||
-q decrease log level
|
||||
--version show program's version number and exit
|
||||
-V, --version show program's version number and exit
|
||||
{{< /highlight >}}
|
||||
<!-- spellchecker-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
@ -58,8 +58,8 @@ logging:
|
||||
|
||||
# Global settings for all defined rules
|
||||
rules:
|
||||
# Disable build-in rules if required
|
||||
buildin: True
|
||||
# Disable built-in rules if required
|
||||
builtin: True
|
||||
|
||||
# List of files to exclude
|
||||
exclude_files: []
|
||||
@ -75,8 +75,7 @@ rules:
|
||||
exclude_filter: []
|
||||
|
||||
# List of rule ID's that should be displayed as a warning instead of an error. By default,
|
||||
# only rules whose version is higher than the current default version are marked as warnings.
|
||||
# This list allows to degrade errors to warnings for each rule.
|
||||
# no rules are marked as warnings. This list allows to degrade errors to warnings for each rule.
|
||||
warning_filter:
|
||||
- "ANSIBLE9999"
|
||||
- "ANSIBLE9998"
|
||||
@ -85,12 +84,8 @@ rules:
|
||||
# You can disable this setting and handle dotfiles by yourself with `exclude_files`.
|
||||
ignore_dotfiles: True
|
||||
|
||||
# List of directories to load standard rules from (defaults to build-in)
|
||||
standards: []
|
||||
|
||||
# Standard version to use. Standard version set in a roles meta file
|
||||
# or playbook will takes precedence.
|
||||
version:
|
||||
# List of directories to load rules from (defaults to built-in)
|
||||
dir: []
|
||||
|
||||
# Block to control included yamllint rules.
|
||||
# See https://yamllint.readthedocs.io/en/stable/rules.html
|
||||
|
@ -2,7 +2,7 @@
|
||||
title: Included rules
|
||||
---
|
||||
|
||||
Reviews are useless without some rules or standards to check against. ansible-later comes with a set of built-in checks, which are explained in the following table.
|
||||
Reviews are useless without some rules to check against. `ansible-later` comes with a set of built-in checks, which are explained in the following table.
|
||||
|
||||
| Rule | ID | Description | Parameter |
|
||||
| ----------------------------- | ----------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
@ -42,5 +42,4 @@ Reviews are useless without some rules or standards to check against. ansible-la
|
||||
| CheckRelativeRolePaths | ANSIBLE0025 | Don't use a relative path in a role. | |
|
||||
| CheckChangedInWhen | ANSIBLE0026 | Use handlers instead of `when: changed`. | |
|
||||
| CheckChangedInWhen | ANSIBLE0027 | Deprecated bare variables in loops must not be used. | |
|
||||
| CheckVersion | ANSIBLE9998 | Standards version should be pinned. | |
|
||||
| CheckDeprecated | ANSIBLE9999 | Deprecated features of `ansible-later` should not be used. | |
|
||||
|
@ -23,5 +23,5 @@ main:
|
||||
sub:
|
||||
- name: Candidates
|
||||
ref: "/build_rules/candidates"
|
||||
- name: Standards checks
|
||||
ref: "/build_rules/standards_check"
|
||||
- name: Rules
|
||||
ref: "/build_rules/rule"
|
||||
|
Loading…
Reference in New Issue
Block a user