Skip to content

Commit eedf97f

Browse files
author
thread-liu
committed
[add] .c/.h file format check and license check
1 parent 08a4de5 commit eedf97f

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

.github/workflows/file_check.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Check File Format and License
2+
3+
on: [pull_request]
4+
5+
jobs:
6+
scancode_job:
7+
runs-on: ubuntu-latest
8+
name: Scan code format and license
9+
steps:
10+
- uses: actions/checkout@v2
11+
- name: Set up Python
12+
uses: actions/setup-python@master
13+
with:
14+
python-version: 3.8
15+
16+
- name: Check Format and License
17+
shell: bash
18+
run: |
19+
pip install click chardet
20+
python tools/file_check.py check 'https://github.com/RT-Thread/rt-thread' 'master'

tools/file_check.py

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

0 commit comments

Comments
 (0)