Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make output easier to parse #22

Open
dflock opened this issue May 4, 2017 · 1 comment
Open

Make output easier to parse #22

dflock opened this issue May 4, 2017 · 1 comment

Comments

@dflock
Copy link
Contributor

dflock commented May 4, 2017

I created a little script to wrap ansible-review and post-process the output. This was harder than it might have been, for two reasons:

  1. ansible-review splits the output for some things over two lines, like this:
WARN: Best practice "Playbooks should not contain logic (vars, tasks, handlers)" not met:
ansible/playbook.yml:10: [EXTRA0008] tasks should not be required in a play

.. but not others. Most parsing tools/code/logic etc... is simpler line-by-line, so this makes it more awkward. I ended up simplifying my life by joining them back together using sed:

$ git ls-files ansible/. | xargs ansible-review 2>/dev/null | sed -n '/met:/ {N; s/[\r\n]/ /g; p}'

This has a (mostly happy) side effect of removing several "non-standard" (see below) messages, like this:

WARN: Couldn't classify file ansible/ansible.cfg
  1. Not all "Best practice..." messages follow a standard output format. For example, most of the messages I'm interested in match this regex:
regex = r'^WARN: (.*) not met: (.*?):(.*?): (.*)$'

However, the message output in #21 doesn't:

WARN: Best practice "Same variable defined in siblings" not met:
ansible/group_vars/test.yml:Inventory is broken:
Attempted to read "/home/duncan/dev/tools/provisioning/ansible/ansible.log" as ini file:
/home/duncan/dev/tools/provisioning/ansible/ansible.log:1:
Expected key=value host variable assignment, got: 16:47:41,708

it goes off the rails after the filename, putting Inventory is broken where the line number would go - i.e. matching the regex, but putting the "wrong" values in.

I could use some extra code to detect these different messages, but in the case of couldn't classify & the #21 messages, they're basically problems with ansible-review (from my point of view), so I just filtered them out instead.


Here's the script, if you're interested:

"""
Post process the output from Ansible Review.

This script runs Ansible Review, captures the output, then
post-processes it into a Github formated markdown Checklist, which
it then outputs to stdout.

It uses the blessed module for output, so it has nice formatting
in the console, but is pipe aware, so you don't get control chars
in piped output.
"""

import os
import re
import subprocess

from blessed import Terminal
from operator import itemgetter

# Get the output from ansible-review, and collapse the two-line output into one line:
# when a line ends in `met:`, the next line is a continuation, so collapse into one line
# Skip group_vars - this outputs a non-standard msg about inventory file being broken
# see here: https://github.com/willthames/ansible-review/issues/21
cmd = r"git ls-files ansible/. | grep -v group_vars | xargs ansible-review 2>/dev/null | sed -n '/met:/ {N; s/[\r\n]/ /g; p}'"

try:
    output = subprocess.check_output(
        cmd,
        stderr=subprocess.STDOUT,
        shell=True
    ).split('\n')
except subprocess.CalledProcessError as e:
    output = e.output.split('\n')

t = Terminal()
output_lines = []


class WarningLine:
    regex = r'^WARN: (.*) not met: (.*?):(.*?): (.*)$'
    text, filename, line, rule = range(1, 5)


for line in output:
    # Convert the string line into an output_item dict, then add
    # it to the output_lines list.
    if line.startswith('WARN: '):
        # It's a warning item
        match = re.search(WarningLine.regex, line)
        if match and len(match.groups()) == 4:
            output_lines.append({
                'log_level': t.yellow('WARN'),
                'file': match.group(WarningLine.filename),
                'line': int(match.group(WarningLine.line)),
                'text': match.group(WarningLine.text),
                'rule': match.group(WarningLine.rule),
            })


# The output from ansible-review is sorted by filename, but it's nicer
# if we sort it by filename & line_no
sorted_output = sorted(output_lines, key=itemgetter('file', 'line'))

#
# Print the output_lines list, to the console, in markdown format
#
print(t.dim('# ') + t.bold('Ansible Review Checklist'))

curr_file = ''

for line in sorted_output:
    if line['file'] != curr_file:
        curr_file = line['file']
        print(t.dim('\n## ') + t.bold(curr_file))

    print t.dim('- []') + ' {l[log_level]}: Line: {l[line]:>3}: {l[text]}, rule: {l[rule]}'.format(l=line)

and here's an example of the console output:

screenshot_2017-05-04_16-03-50

which can be piped to a file, then opened in a markdown editor that understands github formatted markdown (like Abricotine) and used as a checklist:

screenshot_2017-05-04_16-10-12


I did attempt to get ansible-review to run from source, and add this as a feature (where it would probably be simpler to implement) - but couldn't figure out how to do that: #20

@webknjaz
Copy link
Contributor

Hm.. This looks similar to #74

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants