#!/usr/bin/env python3 import collections import json import subprocess import sys DEFAULT_REF = 'origin/master' def get_diff_ranges(ref=DEFAULT_REF): '''Get chunks affected by a merge request Args: ref (str): a reference to diff the HEAD against (default: origin/master) Returns: dict: a dict with filenames in keys and list of `(start, end)` ranges in values (start is inclusive, end is not, wrt :py:func:`range`) ''' files = collections.defaultdict(list) data = subprocess.check_output(['git', 'diff', '-U0', ref, 'HEAD']).decode() path = None for line in data.split('\n'): if line.startswith('+++ '): path = (None if line == '+++ /dev/null' else line.split('/', maxsplit=1)[-1]) continue if line.startswith('@@ '): # @@ -8,0 +9 @@ [class name or previous line or whatever] if path is None: # /dev/null continue _, _, plus, *_ = line.split() start, length, *_ = *(int(i) for i in plus[1:].split(',')), 1 if not length: # remove-only chunk continue files[path].append((start, start + length)) return files class Diff: '''A quick and dirty diff evaluator >>> diff = Diff() >>> (message['file'], message['line']) in diff True # or False The default diff is to the ``origin/master`` ref. ''' def __init__(self, ref=DEFAULT_REF): self._files = get_diff_ranges(ref) def __contains__(self, pathline): path, line = pathline try: return any(start <= line < end for start, end in self._files[path]) except KeyError: return False def main(): diff = Diff() with sys.stdin: pylint = json.load(sys.stdin) ret = 0 for message in pylint: # shellcheck if 'path' not in message: message['path'] = message['file'] if 'symbol' not in message: message['symbol'] = message['code'] if (message['path'], message['line']) in diff: if not ret: print('MESSAGES AFFECTING THIS PR:') print('{path} +{line}:{column}: {symbol}: {message}'.format( **message)) ret += 1 return min(ret, 255) if __name__ == '__main__': sys.exit(main())