prfilter 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #!/usr/bin/env python3
  2. import collections
  3. import json
  4. import pathlib
  5. import subprocess
  6. import sys
  7. DEFAULT_REF = 'origin/master'
  8. THE_BIG_LIST_OF_NAUGHTY_FILES = list(map(pathlib.Path, [
  9. #
  10. # Problems with files listed here get lighter treatment: they are blocking
  11. # only when they are a part of the current pull request.
  12. #
  13. # for pylint
  14. 'Documentation/conf.py',
  15. 'LibOS/shim/test/apps/ltp/contrib/conf_lint.py',
  16. 'LibOS/shim/test/apps/ltp/contrib/conf_merge.py',
  17. 'LibOS/shim/test/apps/ltp/contrib/conf_missing.py',
  18. 'LibOS/shim/test/apps/ltp/contrib/conf_remove_must_pass.py',
  19. 'LibOS/shim/test/apps/ltp/contrib/has_own_main.py',
  20. 'LibOS/shim/test/apps/ltp/contrib/report.py',
  21. 'LibOS/shim/test/apps/ltp/runltp_xml.py',
  22. 'LibOS/shim/test/apps/python-scipy-insecure/scripts/test-numpy.py',
  23. 'LibOS/shim/test/apps/python-scipy-insecure/scripts/test-scipy.py',
  24. 'LibOS/shim/test/apps/python-simple/scripts/benchrun.py',
  25. 'LibOS/shim/test/apps/python-simple/scripts/dummy-web-server.py',
  26. 'LibOS/shim/test/apps/python-simple/scripts/fibonacci.py',
  27. 'LibOS/shim/test/apps/python-simple/scripts/helloworld.py',
  28. 'LibOS/shim/test/apps/python-simple/scripts/test-http.py',
  29. 'LibOS/shim/test/fs/test_fs.py',
  30. 'LibOS/shim/test/regression/test_libos.py',
  31. 'Pal/regression/test_pal.py',
  32. 'Pal/src/host/Linux-SGX/sgx-driver/link-intel-driver.py',
  33. 'Pal/src/host/Linux/pal-gdb.py',
  34. 'Scripts/regression.py',
  35. 'Tools',
  36. # for shellcheck
  37. 'LibOS/shim/test/apps/bash/scripts/bash_test.sh',
  38. 'LibOS/shim/test/apps/common_tools/benchmark-http.sh',
  39. 'LibOS/shim/test/apps/python-simple/run-tests.sh',
  40. 'LibOS/shim/test/native',
  41. 'Pal/src/host/FreeBSD/pal-gdb.template',
  42. 'Pal/src/host/Linux-SGX/debugger/gdb',
  43. 'Pal/src/host/Linux-SGX/sgx-driver/load.sh',
  44. 'Runtime/pal_loader',
  45. 'Scripts/clean-check',
  46. 'Scripts/clean-check-prepare',
  47. 'Scripts/clean-check-test-copy',
  48. 'Scripts/list-all-graphene.sh',
  49. 'Scripts/memusg',
  50. '.ci/run-pylint',
  51. '.ci/run-shellcheck',
  52. ]))
  53. def get_diff_ranges(ref=DEFAULT_REF):
  54. '''Get chunks affected by a merge request
  55. Args:
  56. ref (str): a reference to diff the HEAD against (default: origin/master)
  57. Returns:
  58. dict: a dict with filenames in keys and list of `(start, end)` ranges in
  59. values (start is inclusive, end is not, wrt :py:func:`range`)
  60. '''
  61. files = collections.defaultdict(list)
  62. data = subprocess.check_output(['git', 'diff', '-U0', ref, 'HEAD']).decode()
  63. path = None
  64. for line in data.split('\n'):
  65. if line.startswith('+++ '):
  66. path = (None if line == '+++ /dev/null'
  67. else line.split('/', maxsplit=1)[-1])
  68. continue
  69. if line.startswith('@@ '):
  70. # @@ -8,0 +9 @@ [class name or previous line or whatever]
  71. if path is None: # /dev/null
  72. continue
  73. _, _, plus, *_ = line.split()
  74. start, length, *_ = *(int(i) for i in plus[1:].split(',')), 1
  75. if not length:
  76. # remove-only chunk
  77. continue
  78. files[path].append((start, start + length))
  79. return files
  80. class Diff:
  81. '''A quick and dirty diff evaluator
  82. >>> diff = Diff()
  83. >>> (message['file'], message['line']) in diff
  84. True # or False
  85. >>> diff.message_is_important(message)
  86. True # or False
  87. The default diff is to the ``origin/master`` ref.
  88. '''
  89. # pylint: disable=too-few-public-methods
  90. def __init__(self, ref=DEFAULT_REF):
  91. self._files = get_diff_ranges(ref)
  92. def __contains__(self, pathline):
  93. path, line = pathline
  94. try:
  95. return any(start <= line < end for start, end in self._files[path])
  96. except KeyError:
  97. return False
  98. def message_is_important(self, message):
  99. path = pathlib.Path(message['path'])
  100. for i in THE_BIG_LIST_OF_NAUGHTY_FILES:
  101. try:
  102. path.relative_to(i)
  103. break
  104. except ValueError:
  105. # path is not .relative_to() the path from WHITELIST
  106. pass
  107. else:
  108. # not on whitelist: always complain
  109. return True
  110. if (message['path'], message['line']) in self:
  111. # on whitelist, but in diff: do complain
  112. return True
  113. # on whitelist and outside diff: don't complain
  114. return False
  115. def main():
  116. diff = Diff()
  117. with sys.stdin:
  118. pylint = json.load(sys.stdin)
  119. ret = 0
  120. for message in pylint:
  121. # shellcheck
  122. if 'path' not in message:
  123. message['path'] = message['file']
  124. if 'symbol' not in message:
  125. message['symbol'] = message['code']
  126. if diff.message_is_important(message):
  127. if not ret:
  128. print('MESSAGES AFFECTING THIS PR:')
  129. print('{path} +{line}:{column}: {symbol}: {message}'.format(
  130. **message))
  131. ret += 1
  132. return min(ret, 255)
  133. if __name__ == '__main__':
  134. sys.exit(main())