file_check.py 7.2 KB

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