Browse Source

[add] .c/.h file format check and license check

thread-liu 4 năm trước cách đây
mục cha
commit
eedf97fa66
2 tập tin đã thay đổi với 248 bổ sung0 xóa
  1. 20 0
      .github/workflows/file_check.yml
  2. 228 0
      tools/file_check.py

+ 20 - 0
.github/workflows/file_check.yml

@@ -0,0 +1,20 @@
+name: Check File Format and License
+
+on: [pull_request]
+
+jobs:
+  scancode_job:
+    runs-on: ubuntu-latest
+    name: Scan code format and license
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Python
+        uses: actions/setup-python@master
+        with:
+          python-version: 3.8
+      
+      - name: Check Format and License
+        shell: bash
+        run: |
+          pip install click chardet
+          python tools/file_check.py check 'https://github.com/RT-Thread/rt-thread' 'master'

+ 228 - 0
tools/file_check.py

@@ -0,0 +1,228 @@
+#
+# Copyright (c) 2006-2021, RT-Thread Development Team
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Change Logs:
+# Date           Author       Notes
+# 2021-04-01     LiuKang      the first version
+#
+
+import os
+import re
+import sys
+import click
+import chardet
+import logging
+import datetime
+
+
+def init_logger():
+    log_format = "[%(filename)s %(lineno)d %(levelname)s] %(message)s "
+    date_format = '%Y-%m-%d  %H:%M:%S %a '
+    logging.basicConfig(level=logging.INFO,
+                        format=log_format,
+                        datefmt=date_format,
+                        )
+
+class CheckOut:
+    def __init__(self, rtt_repo, rtt_branch):
+        self.root = os.getcwd()
+        self.rtt_repo = rtt_repo
+        self.rtt_branch = rtt_branch
+
+    def get_new_file(self):
+        file_list = list()
+        try:
+            os.system('git remote add rtt_repo {}'.format(self.rtt_repo))
+            os.system('git fetch rtt_repo')
+            os.system('git reset rtt_repo/{} --soft'.format(self.rtt_branch))
+            os.system('git status > git.txt')
+        except Exception as e:
+            logging.error(e)
+            return None
+        try:
+            with open('git.txt', 'r') as f:
+                file_lines = f.readlines()
+        except Exception as e:
+            logging.error(e)
+            return None
+        file_path = ''
+        for line in file_lines:
+            if 'new file' in line:
+                file_path = line.split('new file:')[1].strip()
+                logging.info('new file -> {}'.format(file_path))
+            elif 'deleted' in line:
+                logging.info('deleted file -> {}'.format(line.split('deleted:')[1].strip()))
+            elif 'modified' in line:
+                file_path = line.split('modified:')[1].strip()
+                logging.info('modified file -> {}'.format(file_path))
+            else:
+                continue
+
+            file_list.append(file_path)
+
+        return file_list
+
+
+class FormatCheck:
+    def __init__(self, file_list):
+        self.file_list = file_list
+
+    def __check_file(self, file_lines):
+        line_num = 1
+        check_result = False
+        for line in file_lines:
+            # check line start
+            line_start = line.replace(' ', '')
+            # find tab
+            if line_start.startswith('\t'):
+                logging.error("line[{}]: please use space replace tab at the start of this line.".format(line_num))
+                check_result = False
+            # check line end
+            lin_end = line.split('\n')[0]
+            if lin_end.endswith(' ') or lin_end.endswith('\t'):
+                logging.error("line[{}]: please delete extra space at the end of this line.".format(line_num))
+                check_result = False
+            line_num += 1
+
+        return check_result
+
+    def check(self):
+        logging.info("Start to check files format.")
+        if len(self.file_list) == 0:
+            logging.warning("There are no files to check license.")
+            return 0
+        encoding_check_result = True
+        format_check_result = True
+        for file_path in self.file_list:
+            file_lines = ''
+            code = ''
+            if file_path.endswith(".c") or file_path.endswith(".h"):
+                try:
+                    with open(file_path, 'r') as f:
+                        file = f.read()
+                        file_lines = f.readlines()
+                        # get file encoding
+                        code = chardet.detect(file)['encoding']
+                except Exception as e:
+                    logging.error(e)
+            else:
+                continue
+
+            if code != 'utf-8':
+                logging.error("[{0}]: encoding not utf-8, please format it.".format(file_path))
+                encoding_check_result = False
+            else:
+                logging.info('[{0}]: encoding check success.'.format(file_path))
+
+            format_check_result = self.__check_file(file_lines)    
+
+        if not encoding_check_result or not format_check_result:
+            logging.error("files format check fail.")
+            return False
+
+        logging.info("files format check success.")
+
+        return True
+
+
+class LicenseCheck:
+    def __init__(self, file_list):
+        self.file_list = file_list
+
+    def check(self):
+        current_year = datetime.date.today().year
+        logging.info("current year: {}".format(current_year))
+        if len(self.file_list) == 0:
+            logging.warning("There are no files to check license.")
+            return 0
+        logging.info("Start to check files license.")
+        check_result = True
+        for file_path in self.file_list:
+            if file_path.endswith(".c") or file_path.endswith(".h"):
+                try:
+                    with open(file_path, 'r') as f:
+                        file = f.readlines()
+                except Exception as e:
+                    logging.error(e)
+            else:
+                continue
+
+            if 'Copyright' in file[1] and 'SPDX-License-Identifier: Apache-2.0' in file[3]:
+                try:
+                    license_year = re.search(r'2006-\d{4}', file[1]).group()
+                    true_year = '2006-{}'.format(current_year)
+                    if license_year != true_year:
+                        logging.warning("[{0}]: license year: {} is not true: {}, please update.".format(file_path,
+                                                                                                        license_year,
+                                                                                                        true_year))
+                                                                                                
+                    else:
+                        logging.info("[{0}]: license check success.".format(file_path))
+                except Exception as e:
+                    logging.error(e)
+            
+            else:
+                logging.error("[{0}]: license check fail.".format(file_path))
+                check_result = False
+
+        return check_result
+
+
+@click.group()
+@click.pass_context
+def cli(ctx):
+    pass
+
+
+@cli.command()
+@click.option(
+    '--license',
+    "check_license",
+    required=False,
+    type=click.BOOL,
+    flag_value=True,
+    help="Enable File license check.",
+)
+@click.argument(
+    'repo',
+    nargs=1,
+    type=click.STRING,
+    default='https://github.com/RT-Thread/rt-thread',
+)
+@click.argument(
+    'branch',
+    nargs=1,
+    type=click.STRING,
+    default='master',
+)
+def check(check_license, repo, branch):
+    """
+    check files license and format.
+    """
+    init_logger()
+    # get modified files list
+    checkout = CheckOut(repo, branch)
+    file_list = checkout.get_new_file()
+    if file_list is None:
+        logging.error("checkout files fail")
+        sys.exit(1)
+
+    # check modified files format
+    format_check = FormatCheck(file_list)
+    format_check_result = format_check.check()
+    license_check_result = True
+    if check_license:
+        license_check = LicenseCheck(file_list)
+        license_check_result = license_check.check()
+
+    if not format_check_result or not license_check_result:
+        logging.error("file format check or license check fail.")
+        sys.exit(1)
+    logging.info("check success.")
+    sys.exit(0)
+
+
+if __name__ == '__main__':
+    cli()