file_check.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #
  2. # Copyright (c) 2006-2021, RT-Thread Development Team
  3. #
  4. # SPDX-License-Identifier: Apache-2.0
  5. #
  6. # Change Logs:
  7. # Date Author Notes
  8. # 2021-04-01 LiuKang the first version
  9. #
  10. import os
  11. import re
  12. import sys
  13. import click
  14. import yaml
  15. import chardet
  16. import logging
  17. import datetime
  18. def init_logger():
  19. log_format = "[%(filename)s %(lineno)d %(levelname)s] %(message)s "
  20. date_format = '%Y-%m-%d %H:%M:%S %a '
  21. logging.basicConfig(level=logging.INFO,
  22. format=log_format,
  23. datefmt=date_format,
  24. )
  25. class CheckOut:
  26. def __init__(self, rtt_repo, rtt_branch):
  27. self.root = os.getcwd()
  28. self.rtt_repo = rtt_repo
  29. self.rtt_branch = rtt_branch
  30. def __exclude_file(self, file_path):
  31. ignore_file_path = os.path.join(self.root, 'ignore_format.yml')
  32. try:
  33. with open(ignore_file_path) as f:
  34. ignore_config = yaml.safe_load(f.read())
  35. file_ignore = ignore_config.get("file_path", [])
  36. dir_ignore = ignore_config.get("dir_path", [])
  37. except Exception as e:
  38. logging.error(e)
  39. return 1
  40. if file_path in file_ignore:
  41. return 0
  42. file_dir_path = os.path.dirname(file_path)
  43. if file_dir_path in dir_ignore:
  44. return 0
  45. return 1
  46. def get_new_file(self):
  47. file_list = list()
  48. try:
  49. os.system('git remote add rtt_repo {} 1>/dev/null'.format(self.rtt_repo))
  50. os.system('git fetch rtt_repo 1>/dev/null')
  51. os.system('git reset rtt_repo/{} --soft 1>/dev/null'.format(self.rtt_branch))
  52. os.system('git status > git.txt')
  53. except Exception as e:
  54. logging.error(e)
  55. return None
  56. try:
  57. with open('git.txt', 'r') as f:
  58. file_lines = f.readlines()
  59. except Exception as e:
  60. logging.error(e)
  61. return None
  62. file_path = ''
  63. for line in file_lines:
  64. if 'new file' in line:
  65. file_path = line.split('new file:')[1].strip()
  66. logging.info('new file -> {}'.format(file_path))
  67. elif 'deleted' in line:
  68. logging.info('deleted file -> {}'.format(line.split('deleted:')[1].strip()))
  69. elif 'modified' in line:
  70. file_path = line.split('modified:')[1].strip()
  71. logging.info('modified file -> {}'.format(file_path))
  72. else:
  73. continue
  74. result = self.__exclude_file(file_path)
  75. if result != 0:
  76. file_list.append(file_path)
  77. return file_list
  78. class FormatCheck:
  79. def __init__(self, file_list):
  80. self.file_list = file_list
  81. def __check_file(self, file_lines):
  82. line_num = 1
  83. check_result = False
  84. for line in file_lines:
  85. # check line start
  86. line_start = line.replace(' ', '')
  87. # find tab
  88. if line_start.startswith('\t'):
  89. logging.error("line[{}]: please use space replace tab at the start of this line.".format(line_num))
  90. check_result = False
  91. # check line end
  92. lin_end = line.split('\n')[0]
  93. if lin_end.endswith(' ') or lin_end.endswith('\t'):
  94. logging.error("line[{}]: please delete extra space at the end of this line.".format(line_num))
  95. check_result = False
  96. line_num += 1
  97. return check_result
  98. def check(self):
  99. logging.info("Start to check files format.")
  100. if len(self.file_list) == 0:
  101. logging.warning("There are no files to check license.")
  102. return 0
  103. encoding_check_result = True
  104. format_check_result = True
  105. for file_path in self.file_list:
  106. code = ''
  107. if file_path.endswith(".c") or file_path.endswith(".h"):
  108. try:
  109. with open(file_path, 'rb') as f:
  110. file = f.read()
  111. # get file encoding
  112. code = chardet.detect(file)['encoding']
  113. except Exception as e:
  114. logging.error(e)
  115. else:
  116. continue
  117. if code != 'utf-8':
  118. logging.error("[{0}]: encoding not utf-8, please format it.".format(file_path))
  119. encoding_check_result = False
  120. else:
  121. logging.info('[{0}]: encoding check success.'.format(file_path))
  122. with open(file_path, 'r') as f:
  123. file_lines = f.readlines()
  124. format_check_result = self.__check_file(file_lines)
  125. if not encoding_check_result or not format_check_result:
  126. logging.error("files format check fail.")
  127. return False
  128. logging.info("files format check success.")
  129. return True
  130. class LicenseCheck:
  131. def __init__(self, file_list):
  132. self.file_list = file_list
  133. def check(self):
  134. current_year = datetime.date.today().year
  135. logging.info("current year: {}".format(current_year))
  136. if len(self.file_list) == 0:
  137. logging.warning("There are no files to check license.")
  138. return 0
  139. logging.info("Start to check files license.")
  140. check_result = True
  141. for file_path in self.file_list:
  142. if file_path.endswith(".c") or file_path.endswith(".h"):
  143. try:
  144. with open(file_path, 'r') as f:
  145. file = f.readlines()
  146. except Exception as e:
  147. logging.error(e)
  148. else:
  149. continue
  150. if 'Copyright' in file[1] and 'SPDX-License-Identifier: Apache-2.0' in file[3]:
  151. try:
  152. license_year = re.search(r'2006-\d{4}', file[1]).group()
  153. true_year = '2006-{}'.format(current_year)
  154. if license_year != true_year:
  155. logging.warning("[{0}]: license year: {} is not true: {}, please update.".format(file_path,
  156. license_year,
  157. true_year))
  158. else:
  159. logging.info("[{0}]: license check success.".format(file_path))
  160. except Exception as e:
  161. logging.error(e)
  162. else:
  163. logging.error("[{0}]: license check fail.".format(file_path))
  164. check_result = False
  165. return check_result
  166. @click.group()
  167. @click.pass_context
  168. def cli(ctx):
  169. pass
  170. @cli.command()
  171. @click.option(
  172. '--license',
  173. "check_license",
  174. required=False,
  175. type=click.BOOL,
  176. flag_value=True,
  177. help="Enable File license check.",
  178. )
  179. @click.argument(
  180. 'repo',
  181. nargs=1,
  182. type=click.STRING,
  183. default='https://github.com/RT-Thread/rt-thread',
  184. )
  185. @click.argument(
  186. 'branch',
  187. nargs=1,
  188. type=click.STRING,
  189. default='master',
  190. )
  191. def check(check_license, repo, branch):
  192. """
  193. check files license and format.
  194. """
  195. init_logger()
  196. # get modified files list
  197. checkout = CheckOut(repo, branch)
  198. file_list = checkout.get_new_file()
  199. if file_list is None:
  200. logging.error("checkout files fail")
  201. sys.exit(1)
  202. # check modified files format
  203. format_check = FormatCheck(file_list)
  204. format_check_result = format_check.check()
  205. license_check_result = True
  206. if check_license:
  207. license_check = LicenseCheck(file_list)
  208. license_check_result = license_check.check()
  209. if not format_check_result or not license_check_result:
  210. logging.error("file format check or license check fail.")
  211. sys.exit(1)
  212. logging.info("check success.")
  213. sys.exit(0)
  214. if __name__ == '__main__':
  215. cli()