Skip to content
This repository was archived by the owner on Mar 13, 2022. It is now read-only.

Commit 9fa71c2

Browse files
committed
Add verify-boilerplate script
1 parent 879ab01 commit 9fa71c2

File tree

5 files changed

+273
-0
lines changed

5 files changed

+273
-0
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ install:
3131

3232
script:
3333
- ./run_tox.sh tox
34+
- ./hack/verify-boilerplate.sh
3435

hack/boilerplate/boilerplate.py

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2015 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from __future__ import print_function
18+
19+
import argparse
20+
import datetime
21+
import difflib
22+
import glob
23+
import os
24+
import re
25+
import sys
26+
27+
parser = argparse.ArgumentParser()
28+
parser.add_argument(
29+
"filenames",
30+
help="list of files to check, all files if unspecified",
31+
nargs='*')
32+
33+
rootdir = os.path.dirname(__file__) + "/../../"
34+
rootdir = os.path.abspath(rootdir)
35+
parser.add_argument(
36+
"--rootdir", default=rootdir, help="root directory to examine")
37+
38+
default_boilerplate_dir = os.path.join(rootdir, "hack/boilerplate")
39+
parser.add_argument(
40+
"--boilerplate-dir", default=default_boilerplate_dir)
41+
42+
parser.add_argument(
43+
"-v", "--verbose",
44+
help="give verbose output regarding why a file does not pass",
45+
action="store_true")
46+
47+
args = parser.parse_args()
48+
49+
verbose_out = sys.stderr if args.verbose else open("/dev/null", "w")
50+
51+
52+
def get_refs():
53+
refs = {}
54+
55+
for path in glob.glob(os.path.join(args.boilerplate_dir, "boilerplate.*.txt")):
56+
extension = os.path.basename(path).split(".")[1]
57+
58+
ref_file = open(path, 'r')
59+
ref = ref_file.read().splitlines()
60+
ref_file.close()
61+
refs[extension] = ref
62+
63+
return refs
64+
65+
66+
def file_passes(filename, refs, regexs):
67+
try:
68+
f = open(filename, 'r')
69+
except Exception as exc:
70+
print("Unable to open %s: %s" % (filename, exc), file=verbose_out)
71+
return False
72+
73+
data = f.read()
74+
f.close()
75+
76+
basename = os.path.basename(filename)
77+
extension = file_extension(filename)
78+
79+
if extension != "":
80+
ref = refs[extension]
81+
else:
82+
ref = refs[basename]
83+
84+
# remove extra content from the top of files
85+
if extension == "sh":
86+
p = regexs["shebang"]
87+
(data, found) = p.subn("", data, 1)
88+
89+
data = data.splitlines()
90+
91+
# if our test file is smaller than the reference it surely fails!
92+
if len(ref) > len(data):
93+
print('File %s smaller than reference (%d < %d)' %
94+
(filename, len(data), len(ref)),
95+
file=verbose_out)
96+
return False
97+
98+
# trim our file to the same number of lines as the reference file
99+
data = data[:len(ref)]
100+
101+
p = regexs["year"]
102+
for d in data:
103+
if p.search(d):
104+
print('File %s has the YEAR field, but missing the year of date' %
105+
filename, file=verbose_out)
106+
return False
107+
108+
# Replace all occurrences of the regex "2014|2015|2016|2017|2018" with "YEAR"
109+
p = regexs["date"]
110+
for i, d in enumerate(data):
111+
(data[i], found) = p.subn('YEAR', d)
112+
if found != 0:
113+
break
114+
115+
# if we don't match the reference at this point, fail
116+
if ref != data:
117+
print("Header in %s does not match reference, diff:" %
118+
filename, file=verbose_out)
119+
if args.verbose:
120+
print(file=verbose_out)
121+
for line in difflib.unified_diff(ref, data, 'reference', filename, lineterm=''):
122+
print(line, file=verbose_out)
123+
print(file=verbose_out)
124+
return False
125+
126+
return True
127+
128+
129+
def file_extension(filename):
130+
return os.path.splitext(filename)[1].split(".")[-1].lower()
131+
132+
133+
skipped_dirs = ['.git']
134+
135+
# list all the files contain 'DO NOT EDIT', but are not generated
136+
skipped_ungenerated_files = ['hack/boilerplate/boilerplate.py']
137+
138+
139+
def normalize_files(files):
140+
newfiles = []
141+
for pathname in files:
142+
if any(x in pathname for x in skipped_dirs):
143+
continue
144+
newfiles.append(pathname)
145+
for i, pathname in enumerate(newfiles):
146+
if not os.path.isabs(pathname):
147+
newfiles[i] = os.path.join(args.rootdir, pathname)
148+
return newfiles
149+
150+
151+
def get_files(extensions):
152+
files = []
153+
if len(args.filenames) > 0:
154+
files = args.filenames
155+
else:
156+
for root, dirs, walkfiles in os.walk(args.rootdir):
157+
# don't visit certain dirs. This is just a performance improvement
158+
# as we would prune these later in normalize_files(). But doing it
159+
# cuts down the amount of filesystem walking we do and cuts down
160+
# the size of the file list
161+
for d in skipped_dirs:
162+
if d in dirs:
163+
dirs.remove(d)
164+
165+
for name in walkfiles:
166+
pathname = os.path.join(root, name)
167+
files.append(pathname)
168+
169+
files = normalize_files(files)
170+
outfiles = []
171+
for pathname in files:
172+
basename = os.path.basename(pathname)
173+
extension = file_extension(pathname)
174+
if extension in extensions or basename in extensions:
175+
outfiles.append(pathname)
176+
return outfiles
177+
178+
179+
def get_dates():
180+
years = datetime.datetime.now().year
181+
return '(%s)' % '|'.join((str(year) for year in range(2014, years+1)))
182+
183+
184+
def get_regexs():
185+
regexs = {}
186+
# Search for "YEAR" which exists in the boilerplate, but shouldn't in the real thing
187+
regexs["year"] = re.compile('YEAR')
188+
# get_dates return 2014, 2015, 2016, 2017, or 2018 until the current year as a regex like: "(2014|2015|2016|2017|2018)";
189+
# company holder names can be anything
190+
regexs["date"] = re.compile(get_dates())
191+
# strip #!.* from shell scripts
192+
regexs["shebang"] = re.compile(r"^(#!.*\n)\n*", re.MULTILINE)
193+
return regexs
194+
195+
196+
def main():
197+
regexs = get_regexs()
198+
refs = get_refs()
199+
filenames = get_files(refs.keys())
200+
201+
for filename in filenames:
202+
if not file_passes(filename, refs, regexs):
203+
print(filename, file=sys.stdout)
204+
205+
return 0
206+
207+
208+
if __name__ == "__main__":
209+
sys.exit(main())

hack/boilerplate/boilerplate.py.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright YEAR The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.

hack/boilerplate/boilerplate.sh.txt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright YEAR The Kubernetes Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.

hack/verify-boilerplate.sh

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2014 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
set -o errexit
18+
set -o nounset
19+
set -o pipefail
20+
21+
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
22+
23+
boilerDir="${KUBE_ROOT}/hack/boilerplate"
24+
boiler="${boilerDir}/boilerplate.py"
25+
26+
files_need_boilerplate=($(${boiler} "$@"))
27+
28+
# Run boilerplate check
29+
if [[ ${#files_need_boilerplate[@]} -gt 0 ]]; then
30+
for file in "${files_need_boilerplate[@]}"; do
31+
echo "Boilerplate header is wrong for: ${file}" >&2
32+
done
33+
34+
exit 1
35+
fi

0 commit comments

Comments
 (0)