You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FreeRTOS/.github/scripts/common/header_checker.py

154 lines
6.9 KiB
Python

#!/usr/bin/env python3
import os, sys, re
from argparse import ArgumentParser
from difflib import unified_diff
from json import load
from colorama import Fore, Style
def dprint(msg):
print('[DEBUG]: %s' % str(msg))
class HeaderChecker:
def __init__(self, header, padding=1000, ignored_files=[], ignored_ext=[], ignored_patterns=[]):
self.padding = padding
self.header = header
self.ignorePatternList = ignored_patterns.copy()
self.ignoreFileList = ignored_files.copy()
self.ignoreExtList = ignored_ext.copy()
def checkJSONList(self, path_json):
'''
This is particularly useful when ingesting output from other programs, like git actions
'''
assert os.path.exists(path_json), 'No such file: ' + path_json
# Get list of files to check from JSON file
with open(path_json) as file_json:
file_checklist = load(file_json)
assert isinstance(file_checklist, list), 'Expected list for singular JSON List entry'
# Accrue how how files fail the check
n_failed = 0
for path_file in file_checklist:
assert isinstance(path_file, str), 'Unexpected JSON format for ' + path_json
if os.path.exists(path_file) and not self.isValidFile(path_file):
n_failed += 1
return n_failed
def isValidFile(self, path):
assert os.path.exists(path), 'No such file: ' + path
print('-------------------------------------------------------------------------------------')
print('Checking file: %s...' % path, end='')
if self.isIgnoredFile(path) or os.path.isdir(path):
print('SKIP')
print('-------------------------------------------------------------------------------------')
return True
# Don't need entire file. Read sufficiently large chunk of file that should contain the header
with open(path, encoding='utf-8', errors='ignore') as file:
chunk = file.read(len(''.join(self.header)) + self.padding)
lines = [('%s\n' % l) for l in chunk.strip().splitlines()][:len(self.header)]
if self.header == lines:
print('PASS')
print('-------------------------------------------------------------------------------------')
return True
else:
print('FAIL')
print('File Delta: %s' % path)
print(*unified_diff(lines[:len(self.header)], self.header))
print('-------------------------------------------------------------------------------------')
return False
def ignoreExtension(self, *args):
for ext in args:
self.ignoreExtList.append(ext)
def ignoreFile(self, *args):
for f in args:
self.ignoreFileList.append(f)
def ignorePattern(self, *args):
for p in args:
self.ignorePatternList.append(re.compile(p))
def isIgnoredFile(self, path):
'''
There are multiple ways a file can be ignored. This is a catch all
'''
assert os.path.exists(path), 'No such file: ' + path
# Try simpler checks first
filename = os.path.split(path)[-1]
extension = os.path.splitext(filename)[-1]
if extension in self.ignoreExtList or filename in self.ignoreFileList:
return True
# Then iterate against regex patterns. In future consider Trie
for pattern in self.ignorePatternList:
if pattern.match(path):
return True
return False
def showHelp(self, path_config):
print(Fore.YELLOW)
print(
"\n\n"
"*************************************************************************************************\n"
"* FreeRTOS Header Check %s(FAILED)%s *\n"
"*************************************************************************************************\n"
"* *\n"
"* %sWe do NOT require that all files contain the FreeRTOS File Header (copyright + license).%s *\n"
"* While some files in this change-set don't adhere with the FreeRTOS File Header, *\n"
"* they can be omitted from this check as needed. *\n"
"* *\n"
"* The Git PR check sources its scripts from your fork. *\n"
"* For FreeRTOS/FreeRTOS, ignored files are listed in '.github/scripts/core_checker.py' *\n"
"* For FreeRTOS/FreeRTOS-Kernel, ignored files are listed in '.github/scripts/kernel_checker.py' *\n"
"* *\n"
"* Please fix any offending files that should have the FreeRTOS header, *\n"
"* or add new files to the ignore list as needed to make the check pass. *\n"
"* *\n"
"* %sInclude the required updates to the '*_checker.py' script in your PR to make the check pass.%s *\n"
"*************************************************************************************************\n"
"\n\n"
% (Fore.RED, Fore.YELLOW,
Fore.RED, Fore.YELLOW,
Fore.RED, Fore.YELLOW)
)
print(Style.RESET_ALL)
@staticmethod
def configArgParser():
parser = ArgumentParser(description='FreeRTOS file header checker. We expect a consistent header across all '
'first party files. The header includes current version number, copyright, '
'and FreeRTOS license.')
parser.add_argument('files_checked',
nargs = '+',
metavar = 'FILE_LIST',
help = 'Space separated list of files to check.')
parser.add_argument('-j', '--json',
default = False,
action = 'store_true',
help = 'Treat arguments json files that store a list of files to check.')
return parser
def processArgs(self, args):
n_failed = 0
if args.json:
for path in args.files_checked:
n_failed += self.checkJSONList(path)
else:
for path in args.files_checked:
n_failed += not self.isValidFile(path)
return n_failed