1
+ #!/usr/bin/env python
2
+
3
+ # Copyright 2019 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 difflib
21
+ import glob
22
+ import json
23
+ import mmap
24
+ import os
25
+ import re
26
+ import sys
27
+ from datetime import date
28
+
29
+ parser = argparse .ArgumentParser ()
30
+ parser .add_argument (
31
+ "filenames" ,
32
+ help = "list of files to check, all files if unspecified" ,
33
+ nargs = '*' )
34
+
35
+ # Rootdir defaults to the directory **above** the repo-infra dir.
36
+ rootdir = os .path .dirname (__file__ ) + "./../../../"
37
+ rootdir = os .path .abspath (rootdir )
38
+ parser .add_argument (
39
+ "--rootdir" , default = rootdir , help = "root directory to examine" )
40
+
41
+ default_boilerplate_dir = os .path .join (rootdir , "csi-driver-nfs/hack/boilerplate" )
42
+
43
+ parser .add_argument (
44
+ "--boilerplate-dir" , default = default_boilerplate_dir )
45
+
46
+ parser .add_argument (
47
+ "-v" , "--verbose" ,
48
+ help = "give verbose output regarding why a file does not pass" ,
49
+ action = "store_true" )
50
+
51
+ args = parser .parse_args ()
52
+
53
+ verbose_out = sys .stderr if args .verbose else open ("/dev/null" , "w" )
54
+
55
+ def get_refs ():
56
+ refs = {}
57
+
58
+ for path in glob .glob (os .path .join (args .boilerplate_dir , "boilerplate.*.txt" )):
59
+ extension = os .path .basename (path ).split ("." )[1 ]
60
+
61
+ ref_file = open (path , 'r' )
62
+ ref = ref_file .read ().splitlines ()
63
+ ref_file .close ()
64
+ refs [extension ] = ref
65
+
66
+ return refs
67
+
68
+ def file_passes (filename , refs , regexs ):
69
+ try :
70
+ f = open (filename , 'r' )
71
+ except Exception as exc :
72
+ print ("Unable to open %s: %s" % (filename , exc ), file = verbose_out )
73
+ return False
74
+
75
+ data = f .read ()
76
+ f .close ()
77
+
78
+ basename = os .path .basename (filename )
79
+ extension = file_extension (filename )
80
+ if extension != "" :
81
+ ref = refs [extension ]
82
+ else :
83
+ ref = refs [basename ]
84
+
85
+ # remove build tags from the top of Go files
86
+ if extension == "go" :
87
+ p = regexs ["go_build_constraints" ]
88
+ (data , found ) = p .subn ("" , data , 1 )
89
+
90
+ # remove shebang from the top of shell files
91
+ if extension == "sh" or extension == "py" :
92
+ p = regexs ["shebang" ]
93
+ (data , found ) = p .subn ("" , data , 1 )
94
+
95
+ data = data .splitlines ()
96
+
97
+ # if our test file is smaller than the reference it surely fails!
98
+ if len (ref ) > len (data ):
99
+ print ('File %s smaller than reference (%d < %d)' %
100
+ (filename , len (data ), len (ref )),
101
+ file = verbose_out )
102
+ return False
103
+
104
+ # trim our file to the same number of lines as the reference file
105
+ data = data [:len (ref )]
106
+
107
+ p = regexs ["year" ]
108
+ for d in data :
109
+ if p .search (d ):
110
+ print ('File %s is missing the year' % filename , file = verbose_out )
111
+ return False
112
+
113
+ # Replace all occurrences of the regex "CURRENT_YEAR|...|2016|2015|2014" with "YEAR"
114
+ p = regexs ["date" ]
115
+ for i , d in enumerate (data ):
116
+ (data [i ], found ) = p .subn ('YEAR' , d )
117
+ if found != 0 :
118
+ break
119
+
120
+ # if we don't match the reference at this point, fail
121
+ if ref != data :
122
+ print ("Header in %s does not match reference, diff:" % filename , file = verbose_out )
123
+ if args .verbose :
124
+ print (file = verbose_out )
125
+ for line in difflib .unified_diff (ref , data , 'reference' , filename , lineterm = '' ):
126
+ print (line , file = verbose_out )
127
+ print (file = verbose_out )
128
+ return False
129
+
130
+ return True
131
+
132
+ def file_extension (filename ):
133
+ return os .path .splitext (filename )[1 ].split ("." )[- 1 ].lower ()
134
+
135
+ skipped_dirs = ['Godeps' , 'third_party' , '_gopath' , '_output' , '.git' ,
136
+ 'cluster/env.sh' , 'vendor' , 'test/e2e/generated/bindata.go' ,
137
+ 'repo-infra/verify/boilerplate/test' , '.glide' ]
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
+ return newfiles
146
+
147
+ def get_files (extensions ):
148
+ files = []
149
+ if len (args .filenames ) > 0 :
150
+ files = args .filenames
151
+ else :
152
+ for root , dirs , walkfiles in os .walk (args .rootdir ):
153
+ # don't visit certain dirs. This is just a performance improvement
154
+ # as we would prune these later in normalize_files(). But doing it
155
+ # cuts down the amount of filesystem walking we do and cuts down
156
+ # the size of the file list
157
+ for d in skipped_dirs :
158
+ if d in dirs :
159
+ dirs .remove (d )
160
+
161
+ for name in walkfiles :
162
+ pathname = os .path .join (root , name )
163
+ files .append (pathname )
164
+
165
+ files = normalize_files (files )
166
+
167
+ outfiles = []
168
+ for pathname in files :
169
+ basename = os .path .basename (pathname )
170
+ extension = file_extension (pathname )
171
+ if extension in extensions or basename in extensions :
172
+ outfiles .append (pathname )
173
+ return outfiles
174
+
175
+ def get_regexs ():
176
+ regexs = {}
177
+ # Search for "YEAR" which exists in the boilerplate, but shouldn't in the real thing
178
+ regexs ["year" ] = re .compile ( 'YEAR' )
179
+ # dates can be 2014, 2015, 2016, ..., CURRENT_YEAR, company holder names can be anything
180
+ years = range (2014 , date .today ().year + 1 )
181
+ regexs ["date" ] = re .compile ( '(%s)' % "|" .join (map (lambda l : str (l ), years )) )
182
+ # strip // +build \n\n build constraints
183
+ regexs ["go_build_constraints" ] = re .compile (r"^(// \+build.*\n)+\n" , re .MULTILINE )
184
+ # strip #!.* from shell scripts
185
+ regexs ["shebang" ] = re .compile (r"^(#!.*\n)\n*" , re .MULTILINE )
186
+ return regexs
187
+
188
+
189
+
190
+ def main ():
191
+ regexs = get_regexs ()
192
+ refs = get_refs ()
193
+ filenames = get_files (refs .keys ())
194
+
195
+ for filename in filenames :
196
+ if not file_passes (filename , refs , regexs ):
197
+ print (filename , file = sys .stdout )
198
+
199
+ return 0
200
+
201
+ if __name__ == "__main__" :
202
+ sys .exit (main ())
0 commit comments