From a6550b332e7d417652fa43e29a8bf51bd67de759 Mon Sep 17 00:00:00 2001 From: Bowei Du Date: Thu, 13 Oct 2016 13:49:13 -0700 Subject: [PATCH] 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. --- .gitignore | 1 + hack/boilerplate/boilerplate.py | 37 +++++++++++++++++--- hack/boilerplate/boilerplate_test.py | 52 ++++++++++++++++++++++++++++ hack/boilerplate/test/fail.go | 19 ++++++++++ hack/boilerplate/test/fail.py | 17 +++++++++ hack/boilerplate/test/pass.go | 17 +++++++++ hack/boilerplate/test/pass.py | 17 +++++++++ hack/verify-boilerplate.sh | 21 ++++++++++- 8 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 hack/boilerplate/boilerplate_test.py create mode 100644 hack/boilerplate/test/fail.go create mode 100644 hack/boilerplate/test/fail.py create mode 100644 hack/boilerplate/test/pass.go create mode 100644 hack/boilerplate/test/pass.py diff --git a/.gitignore b/.gitignore index 1ceee6171e1..09c4f287a1e 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ kubernetes.tar.gz !\.drone\.sec /bazel-* +*.pyc diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py index b9e1edf433e..d0e41d74322 100755 --- a/hack/boilerplate/boilerplate.py +++ b/hack/boilerplate/boilerplate.py @@ -17,6 +17,7 @@ from __future__ import print_function import argparse +import difflib import glob import json import mmap @@ -25,16 +26,28 @@ import re import sys parser = argparse.ArgumentParser() -parser.add_argument("filenames", help="list of files to check, all files if unspecified", nargs='*') +parser.add_argument( + "filenames", + help="list of files to check, all files if unspecified", + nargs='*') rootdir = os.path.dirname(__file__) + "/../../" rootdir = os.path.abspath(rootdir) -parser.add_argument("--rootdir", default=rootdir, help="root directory to examine") +parser.add_argument( + "--rootdir", default=rootdir, help="root directory to examine") default_boilerplate_dir = os.path.join(rootdir, "hack/boilerplate") -parser.add_argument("--boilerplate-dir", default=default_boilerplate_dir) +parser.add_argument( + "--boilerplate-dir", default=default_boilerplate_dir) + +parser.add_argument( + "-v", "--verbose", + help="give verbose output regarding why a file does not pass", + action="store_true") + args = parser.parse_args() +verbose_out = sys.stderr if args.verbose else open("/dev/null", "w") def get_refs(): refs = {} @@ -52,7 +65,8 @@ def get_refs(): def file_passes(filename, refs, regexs): try: f = open(filename, 'r') - except: + except Exception as exc: + print("Unable to open %s: %s" % (filename, exc), file=verbose_out) return False data = f.read() @@ -79,6 +93,9 @@ def file_passes(filename, refs, regexs): # if our test file is smaller than the reference it surely fails! if len(ref) > len(data): + print('File %s smaller than reference (%d < %d)' % + (filename, len(data), len(ref)), + file=verbose_out) return False # trim our file to the same number of lines as the reference file @@ -87,6 +104,7 @@ def file_passes(filename, refs, regexs): p = regexs["year"] for d in data: if p.search(d): + print('File %s is missing the year' % filename, file=verbose_out) return False # Replace all occurrences of the regex "2016|2015|2014" with "YEAR" @@ -98,6 +116,12 @@ def file_passes(filename, refs, regexs): # if we don't match the reference at this point, fail if ref != data: + print("Header in %s does not match reference, diff:" % filename, file=verbose_out) + if args.verbose: + print(file=verbose_out) + for line in difflib.unified_diff(ref, data, 'reference', filename, lineterm=''): + print(line, file=verbose_out) + print(file=verbose_out) return False return True @@ -105,7 +129,8 @@ def file_passes(filename, refs, regexs): def file_extension(filename): return os.path.splitext(filename)[1].split(".")[-1].lower() -skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git', 'cluster/env.sh', "vendor", "test/e2e/generated/bindata.go"] +skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git', 'cluster/env.sh', + "vendor", "test/e2e/generated/bindata.go", "hack/boilerplate/test"] def normalize_files(files): newfiles = [] @@ -166,5 +191,7 @@ def main(): if not file_passes(filename, refs, regexs): print(filename, file=sys.stdout) + return 0 + if __name__ == "__main__": sys.exit(main()) diff --git a/hack/boilerplate/boilerplate_test.py b/hack/boilerplate/boilerplate_test.py new file mode 100644 index 00000000000..b8d5b8e9e0f --- /dev/null +++ b/hack/boilerplate/boilerplate_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# Copyright 2016 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import boilerplate +import unittest +import StringIO +import os +import sys + +class TestBoilerplate(unittest.TestCase): + """ + Note: run this test from the hack/boilerplate directory. + + $ python -m unittest boilerplate_test + """ + + def test_boilerplate(self): + os.chdir("test/") + + class Args(object): + def __init__(self): + self.filenames = [] + self.rootdir = "." + self.boilerplate_dir = "../" + self.verbose = True + + # capture stdout + old_stdout = sys.stdout + sys.stdout = StringIO.StringIO() + + boilerplate.args = Args() + ret = boilerplate.main() + + output = sorted(sys.stdout.getvalue().split()) + + sys.stdout = old_stdout + + self.assertEquals( + output, ['././fail.go', '././fail.py']) diff --git a/hack/boilerplate/test/fail.go b/hack/boilerplate/test/fail.go new file mode 100644 index 00000000000..59e79d659cb --- /dev/null +++ b/hack/boilerplate/test/fail.go @@ -0,0 +1,19 @@ +/* +Copyright 2014 The Kubernetes Authors. + +fail + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main diff --git a/hack/boilerplate/test/fail.py b/hack/boilerplate/test/fail.py new file mode 100644 index 00000000000..cbdd06ff8a1 --- /dev/null +++ b/hack/boilerplate/test/fail.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +# Copyright 2015 The Kubernetes Authors. +# +# failed +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/hack/boilerplate/test/pass.go b/hack/boilerplate/test/pass.go new file mode 100644 index 00000000000..6aa6311783b --- /dev/null +++ b/hack/boilerplate/test/pass.go @@ -0,0 +1,17 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main diff --git a/hack/boilerplate/test/pass.py b/hack/boilerplate/test/pass.py new file mode 100644 index 00000000000..5b7ce29a25c --- /dev/null +++ b/hack/boilerplate/test/pass.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +# Copyright 2015 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +True diff --git a/hack/verify-boilerplate.sh b/hack/verify-boilerplate.sh index 145a806bf52..e15edddb7da 100755 --- a/hack/verify-boilerplate.sh +++ b/hack/verify-boilerplate.sh @@ -19,10 +19,29 @@ set -o nounset set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. -boiler="${KUBE_ROOT}/hack/boilerplate/boilerplate.py" + +boilerDir="${KUBE_ROOT}/hack/boilerplate" +boiler="${boilerDir}/boilerplate.py" files_need_boilerplate=($(${boiler} "$@")) +# Run boilerplate.py unit tests +unitTestOut="$(mktemp)" +trap cleanup EXIT +cleanup() { + rm "${unitTestOut}" +} + +pushd "${boilerDir}" >/dev/null +if ! python -m unittest boilerplate_test 2>"${unitTestOut}"; then + echo "boilerplate_test.py failed" + echo + cat "${unitTestOut}" + exit 1 +fi +popd >/dev/null + +# Run boilerplate check if [[ ${#files_need_boilerplate[@]} -gt 0 ]]; then for file in "${files_need_boilerplate[@]}"; do echo "Boilerplate header is wrong for: ${file}"