Skip to content

Commit a6550b3

Browse files
committed
Add verbose mode to boilerplate
hack/verify-boilerplate.sh -v will now print out why the file does not match along with a diff if possible. Note: boilerplate.py now has a unit test that is run along with hack/verify-boilerplate.sh.
1 parent 373be74 commit a6550b3

File tree

8 files changed

+175
-6
lines changed

8 files changed

+175
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,4 @@ kubernetes.tar.gz
117117
!\.drone\.sec
118118

119119
/bazel-*
120+
*.pyc

hack/boilerplate/boilerplate.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from __future__ import print_function
1818

1919
import argparse
20+
import difflib
2021
import glob
2122
import json
2223
import mmap
@@ -25,16 +26,28 @@
2526
import sys
2627

2728
parser = argparse.ArgumentParser()
28-
parser.add_argument("filenames", help="list of files to check, all files if unspecified", nargs='*')
29+
parser.add_argument(
30+
"filenames",
31+
help="list of files to check, all files if unspecified",
32+
nargs='*')
2933

3034
rootdir = os.path.dirname(__file__) + "/../../"
3135
rootdir = os.path.abspath(rootdir)
32-
parser.add_argument("--rootdir", default=rootdir, help="root directory to examine")
36+
parser.add_argument(
37+
"--rootdir", default=rootdir, help="root directory to examine")
3338

3439
default_boilerplate_dir = os.path.join(rootdir, "hack/boilerplate")
35-
parser.add_argument("--boilerplate-dir", default=default_boilerplate_dir)
40+
parser.add_argument(
41+
"--boilerplate-dir", default=default_boilerplate_dir)
42+
43+
parser.add_argument(
44+
"-v", "--verbose",
45+
help="give verbose output regarding why a file does not pass",
46+
action="store_true")
47+
3648
args = parser.parse_args()
3749

50+
verbose_out = sys.stderr if args.verbose else open("/dev/null", "w")
3851

3952
def get_refs():
4053
refs = {}
@@ -52,7 +65,8 @@ def get_refs():
5265
def file_passes(filename, refs, regexs):
5366
try:
5467
f = open(filename, 'r')
55-
except:
68+
except Exception as exc:
69+
print("Unable to open %s: %s" % (filename, exc), file=verbose_out)
5670
return False
5771

5872
data = f.read()
@@ -79,6 +93,9 @@ def file_passes(filename, refs, regexs):
7993

8094
# if our test file is smaller than the reference it surely fails!
8195
if len(ref) > len(data):
96+
print('File %s smaller than reference (%d < %d)' %
97+
(filename, len(data), len(ref)),
98+
file=verbose_out)
8299
return False
83100

84101
# trim our file to the same number of lines as the reference file
@@ -87,6 +104,7 @@ def file_passes(filename, refs, regexs):
87104
p = regexs["year"]
88105
for d in data:
89106
if p.search(d):
107+
print('File %s is missing the year' % filename, file=verbose_out)
90108
return False
91109

92110
# Replace all occurrences of the regex "2016|2015|2014" with "YEAR"
@@ -98,14 +116,21 @@ def file_passes(filename, refs, regexs):
98116

99117
# if we don't match the reference at this point, fail
100118
if ref != data:
119+
print("Header in %s does not match reference, diff:" % filename, file=verbose_out)
120+
if args.verbose:
121+
print(file=verbose_out)
122+
for line in difflib.unified_diff(ref, data, 'reference', filename, lineterm=''):
123+
print(line, file=verbose_out)
124+
print(file=verbose_out)
101125
return False
102126

103127
return True
104128

105129
def file_extension(filename):
106130
return os.path.splitext(filename)[1].split(".")[-1].lower()
107131

108-
skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git', 'cluster/env.sh', "vendor", "test/e2e/generated/bindata.go"]
132+
skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git', 'cluster/env.sh',
133+
"vendor", "test/e2e/generated/bindata.go", "hack/boilerplate/test"]
109134

110135
def normalize_files(files):
111136
newfiles = []
@@ -166,5 +191,7 @@ def main():
166191
if not file_passes(filename, refs, regexs):
167192
print(filename, file=sys.stdout)
168193

194+
return 0
195+
169196
if __name__ == "__main__":
170197
sys.exit(main())

hack/boilerplate/boilerplate_test.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2016 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+
import boilerplate
18+
import unittest
19+
import StringIO
20+
import os
21+
import sys
22+
23+
class TestBoilerplate(unittest.TestCase):
24+
"""
25+
Note: run this test from the hack/boilerplate directory.
26+
27+
$ python -m unittest boilerplate_test
28+
"""
29+
30+
def test_boilerplate(self):
31+
os.chdir("test/")
32+
33+
class Args(object):
34+
def __init__(self):
35+
self.filenames = []
36+
self.rootdir = "."
37+
self.boilerplate_dir = "../"
38+
self.verbose = True
39+
40+
# capture stdout
41+
old_stdout = sys.stdout
42+
sys.stdout = StringIO.StringIO()
43+
44+
boilerplate.args = Args()
45+
ret = boilerplate.main()
46+
47+
output = sorted(sys.stdout.getvalue().split())
48+
49+
sys.stdout = old_stdout
50+
51+
self.assertEquals(
52+
output, ['././fail.go', '././fail.py'])

hack/boilerplate/test/fail.go

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

hack/boilerplate/test/fail.py

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

hack/boilerplate/test/pass.go

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

hack/boilerplate/test/pass.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
True

hack/verify-boilerplate.sh

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,29 @@ set -o nounset
1919
set -o pipefail
2020

2121
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
22-
boiler="${KUBE_ROOT}/hack/boilerplate/boilerplate.py"
22+
23+
boilerDir="${KUBE_ROOT}/hack/boilerplate"
24+
boiler="${boilerDir}/boilerplate.py"
2325

2426
files_need_boilerplate=($(${boiler} "$@"))
2527

28+
# Run boilerplate.py unit tests
29+
unitTestOut="$(mktemp)"
30+
trap cleanup EXIT
31+
cleanup() {
32+
rm "${unitTestOut}"
33+
}
34+
35+
pushd "${boilerDir}" >/dev/null
36+
if ! python -m unittest boilerplate_test 2>"${unitTestOut}"; then
37+
echo "boilerplate_test.py failed"
38+
echo
39+
cat "${unitTestOut}"
40+
exit 1
41+
fi
42+
popd >/dev/null
43+
44+
# Run boilerplate check
2645
if [[ ${#files_need_boilerplate[@]} -gt 0 ]]; then
2746
for file in "${files_need_boilerplate[@]}"; do
2847
echo "Boilerplate header is wrong for: ${file}"

0 commit comments

Comments
 (0)