#!/usr/bin/env python import os import re import sys import ansible.errors import ansible.utils.template import ansiblelint import ansiblelint.utils import jinja2 def log_warn(msg): print "warning: %s" % msg def log_error(msg): print "error: %s" % msg def lint_ansible(file_path): rules = ansiblelint.RulesCollection() playbooks = set([file_path]) runner = ansiblelint.Runner(rules, playbooks, [], ['ANSIBLE0002' ,'ANSIBLE0003']) errors = [] try: matches = runner.run() except ansible.errors.AnsibleYAMLValidationFailed as e: error_str = str(e) m = re.match(r".*Note: The error may actually appear before this position: line (\d+), column (\d+).*", error_str, re.DOTALL) line = m.group(1) errors.append({ 'message': 'failed to validate yml file %s' % e, 'filename': file_path, 'linenumber': line, }) return errors except IOError as e: errors.append({ 'message': 'failed to include another yml file %s' % e, 'filename': file_path, 'linenumber': '0', }) return errors except Exception as e: errors.append({ 'message': 'unknown error happend %s' % e, 'filename': file_path, 'linenumber': '0', }) return errors for match in matches: errors.append({ 'message': match.message, 'filename': match.filename, 'linenumber': match.linenumber, }) return errors def lint_jinja(file_path): loader = jinja2.FileSystemLoader([os.path.dirname(file_path)]) environment = jinja2.Environment(trim_blocks=True, loader=loader) environment.filters.update(ansible.utils.template._get_filters()) errors = [] try: data = file(file_path).read() if type(data) == str: data = unicode(data, 'utf-8') environment.from_string(data).render(func=lambda:None) except jinja2.exceptions.TemplateSyntaxError as e: errors.append({ 'message': e.message, 'filename': file_path, 'linenumber': e.lineno, }) except (jinja2.exceptions.UndefinedError, TypeError) as e: # Ignore undefined errors pass return errors if __name__ == '__main__': for path in sys.argv[1:]: if not os.path.isfile(path): continue errors = None if re.match(r"(.*/templates/.*|.*\.j2$)", path): errors = lint_jinja(path) elif re.match(r".*\.yml$", path): errors = lint_ansible(path) if errors is None: continue for error in errors: message = "[{0}:{1}] {2}".format( error['filename'], error['linenumber'], error['message']) log_error(message)