3131
3232EXCLUDE_PATHS = ('libopencm3/' , 'FatFs/' )
3333
34+ # Disregard specific messages in a class
35+ POST_FILTER = {
36+ "whitespace/braces" : [
37+ '{ should almost always be at the end of the previous line' ,
38+ ],
39+ "whitespace/newline" : [
40+ 'An else should appear on the same line as the preceding }' ,
41+ ],
42+ }
43+
3444UNMATCHED_LINT_ERROR = "Unmatched Lint Error(s):\n "
3545LINT_ERROR = "Lint Error:\n "
3646
47+ URL_CACHE = {}
3748GITHUB_TOKEN = os .environ .get ('GITHUB_TOKEN' )
3849TRAVIS = os .environ .get ('TRAVIS' )
3950TRAVIS_PULL_REQUEST = os .environ .get ('TRAVIS_PULL_REQUEST' )
@@ -75,17 +86,52 @@ def main():
7586 if TRAVIS and not TRAVIS_PULL_REQUEST :
7687 # FIXME: This shouldn't be needed, but not sure why TRAVIS is so special
7788 return
78- check_depth ()
79- changed = get_changed_lines ()
89+ if TRAVIS :
90+ changed = get_changed_lines_from_pr ()
91+ else :
92+ changed = get_changed_lines_from_git ()
8093
8194 paths = filter_paths (args .path , changed , pwd )
8295 violations = run_lint (paths , changed )
8396 if GITHUB_TOKEN and TRAVIS_PULL_REQUEST and not args .skip_github :
8497 update_github_status (violations )
8598
86- def get_changed_lines ():
99+ def get_changed_lines_from_pr ():
100+ url = 'https://api.github.com/repos/{}/pulls/{}' .format (TRAVIS_REPO_SLUG , TRAVIS_PULL_REQUEST )
101+ diff = get_url (url , {"Accept" : "application/vnd.github.v3.diff" }).split ('\n ' )
102+ filename = None
103+ changed = {}
104+ file_pos = 0
105+ for line in diff :
106+ if line .startswith ("diff " ):
107+ filename = line .split (" " )[- 1 ][2 :]
108+ if filename not in changed :
109+ changed [filename ] = {}
110+ file_pos = 0
111+ continue
112+ if any (line .startswith (pat ) for pat in ["index" , "---" , "+++" ]):
113+ continue
114+ if line .startswith ("@@" ):
115+ match = re .search (" \+(\d+),(\d+) @@" , line )
116+ if match :
117+ file_pos = int (match .group (1 ))
118+ else :
119+ logging .error ("Found unparsable diff in %s: %s" , filename , line )
120+ file_pos = 0
121+ continue
122+ if file_pos <= 0 or line .startswith ("-" ):
123+ continue
124+ if line .startswith ("+" ):
125+ changed [filename ][file_pos ] = 1
126+ file_pos += 1
127+
128+ for filename in sorted (changed .keys ()):
129+ logging .debug ("%s: %s" , filename , sorted (changed [filename ].keys (), key = int ))
130+ return changed
131+
132+ def get_changed_lines_from_git ():
87133 changed = {}
88- master = TRAVIS_BRANCH or "master"
134+ master = "master"
89135 base = system (["git" , "merge-base" , "HEAD" , master ]).rstrip ()
90136 cmd = ["git" , "diff" , "--name-only" , "--diff-filter" , "AM" , base ]
91137 sha1s = ["000000000" ];
@@ -104,7 +150,7 @@ def get_changed_lines():
104150 if not match :
105151 continue
106152 changed [_file ][int (match .group (1 ))] = 1
107- logging .debug ("%s: %s" , _file , sorted (changed [_file ].keys ()))
153+ logging .debug ("%s: %s" , _file , sorted (changed [_file ].keys (), key = int ))
108154 return changed
109155
110156def filter_paths (paths , changed , pwd ):
@@ -145,20 +191,24 @@ def run_lint(paths, changed):
145191 _p = subprocess .Popen (cmd , shell = True , stdout = subprocess .PIPE )
146192 for line in _p .stdout :
147193 line = line .rstrip ()
148- match = re .search ("(\S+):(\d+):\s.* \[(\S+)\]\s\[\d\]$" , line )
194+ match = re .search ("(\S+):(\d+):\s+(.*\S)\s+ \[(\S+)\]\s\[\d\]$" , line )
149195 if match :
150196 filename = match .group (1 )
151197 linenum = int (match .group (2 ))
152- violation = match .group (3 )
198+ errstr = match .group (3 )
199+ err_class = match .group (4 )
200+ if err_class in POST_FILTER and errstr in POST_FILTER [err_class ]:
201+ # Ignore this error
202+ continue
153203 if not changed or linenum not in changed [filename ]:
154204 continue
155205 print line
156206 if filename not in errors :
157207 errors [filename ] = 0
158208 errors [filename ] += 1
159- if violation not in count :
160- count [violation ] = 0
161- count [violation ] += 1
209+ if err_class not in count :
210+ count [err_class ] = 0
211+ count [err_class ] += 1
162212 if filename not in violations :
163213 violations [filename ] = {}
164214 if linenum not in violations [filename ]:
@@ -282,11 +332,14 @@ def get_url(url, headers=None):
282332 logging .debug ("GET: " + url )
283333 if not headers :
284334 headers = {}
285- headers ['Authorization' ] = 'token ' + get_token ()
286- request = urllib2 .Request (url , None , headers )
287- res = urllib2 .urlopen (request )
288- raise_for_status (url , res )
289- return res .read ()
335+ key = (url , json .dumps (headers ))
336+ if key not in URL_CACHE :
337+ headers ['Authorization' ] = 'token ' + get_token ()
338+ request = urllib2 .Request (url , None , headers )
339+ res = urllib2 .urlopen (request )
340+ raise_for_status (url , res )
341+ URL_CACHE [key ] = res .read ()
342+ return URL_CACHE [key ]
290343
291344def get_token ():
292345 """Return github token"""
@@ -376,15 +429,5 @@ def system(cmd):
376429 else :
377430 return subprocess .check_output (cmd , shell = True )
378431
379- def check_depth ():
380- if TRAVIS and TRAVIS_PULL_REQUEST_SHA :
381- with open (os .devnull , 'w' ) as devnull :
382- if subprocess .call (["git" , "merge-base" , TRAVIS_PULL_REQUEST_SHA , TRAVIS_BRANCH ], stdout = devnull ):
383- # Current depth is not deep enough
384- logging .debug (system (["git" , "fetch" , "--deepen" , "100" ]))
385- if subprocess .call (["git" , "merge-base" , TRAVIS_PULL_REQUEST_SHA , TRAVIS_BRANCH ], stdout = devnull ):
386- logging .error ("Can't find common base for comparison. Aborting" )
387- sys .exit (1 )
388-
389432
390433main ()
0 commit comments