-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathlint
executable file
·274 lines (232 loc) · 7.16 KB
/
lint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#!/bin/bash
# This scipt lints files for common errors.
#
# For go files, it runs gofmt and go vet, and optionally golint and
# gocyclo, if they are installed.
#
# For shell files, it runs shfmt. If you don't have that installed, you can get
# it with:
# curl -fsSLo shfmt https://github.com/mvdan/sh/releases/download/v1.3.0/shfmt_v1.3.0_linux_amd64
# chmod +x shfmt
#
# With no arguments, it lints the current files staged
# for git commit. Or you can pass it explicit filenames
# (or directories) and it will lint them.
#
# To use this script automatically, run:
# ln -s ../../bin/lint .git/hooks/pre-commit
set -e
LINT_IGNORE_FILE=${LINT_IGNORE_FILE:-".lintignore"}
IGNORE_LINT_COMMENT=
IGNORE_SPELLINGS=
while true; do
case "$1" in
-nocomment)
IGNORE_LINT_COMMENT=1
shift 1
;;
-notestpackage)
# NOOP, still accepted for backwards compatibility
shift 1
;;
-ignorespelling)
IGNORE_SPELLINGS="$2,$IGNORE_SPELLINGS"
shift 2
;;
*)
break
;;
esac
done
spell_check() {
local filename="$1"
local lint_result=0
# misspell is completely optional. If you don't like it
# don't have it installed.
if ! type misspell >/dev/null 2>&1; then
return $lint_result
fi
if ! misspell -error -i "$IGNORE_SPELLINGS" "${filename}"; then
lint_result=1
fi
return $lint_result
}
lint_go() {
# This function is called on a whole directory containing Go files
local filename="$1"
local lint_result=0
if [ -n "$(gofmt -s -l "${filename}")" ]; then
lint_result=1
echo "${filename}: run gofmt -s -w ${filename}"
fi
go vet "${filename}" || lint_result=$?
# golint is completely optional. If you don't like it
# don't have it installed.
if type golint >/dev/null 2>&1; then
# golint doesn't set an exit code it seems
if [ -z "$IGNORE_LINT_COMMENT" ]; then
lintoutput=$(golint "${filename}")
else
lintoutput=$(golint "${filename}" | grep -vE 'comment|dot imports|ALL_CAPS')
fi
if [ -n "$lintoutput" ]; then
lint_result=1
echo "$lintoutput"
fi
fi
# gocyclo is completely optional. If you don't like it
# don't have it installed. Also never blocks a commit,
# it just warns.
if type gocyclo >/dev/null 2>&1; then
gocyclo -over 25 "${filename}" | while read -r line; do
echo "${filename}": higher than 25 cyclomatic complexity - "${line}"
done
fi
return $lint_result
}
lint_sh() {
local filename="$1"
local lint_result=0
# Skip shfmt validation, if not installed
if type shfmt >/dev/null 2>&1; then
if ! diff -u "${filename}" <(shfmt -i 4 "${filename}"); then
lint_result=1
echo "${filename}: run shfmt -i 4 -w ${filename}"
fi
fi
# the shellcheck is completely optional. If you don't like it
# don't have it installed.
if type shellcheck >/dev/null 2>&1; then
shellcheck "${filename}" || lint_result=1
fi
return $lint_result
}
lint_tf() {
local filename="$1"
local lint_result=0
if ! terraform fmt -check -diff "${filename}"; then
lint_result=1
echo "${filename}: run terraform fmt -write ${filename}"
fi
return $lint_result
}
lint_md() {
local filename="$1"
local lint_result=0
for i in '=======' '>>>>>>>'; do
if grep -q "${i}" "${filename}"; then
lint_result=1
echo "${filename}: bad merge/rebase!"
fi
done
return $lint_result
}
lint_py() {
local filename="$1"
local lint_result=0
if yapf --diff "${filename}" | grep -qE '^[+-]'; then
lint_result=1
echo "${filename} needs reformatting. Run: yapf --in-place ${filename}"
else
# Only run flake8 if yapf passes, since they pick up a lot of similar issues
flake8 "${filename}" || lint_result=1
fi
return $lint_result
}
lint() {
filename="$1"
ext="${filename##*\.}"
local lint_result=0
# Don't lint deleted files
if [ ! -f "$filename" ]; then
return
fi
# Don't lint specific files
case "$(basename "${filename}")" in
static.go) return ;;
coverage.html) return ;;
*.pb.go) return ;;
esac
mimetype=$(file --mime-type "${filename}" | awk '{print $2}')
case "$mimetype.$ext" in
text/x-shellscript.*) lint_sh "${filename}" || lint_result=1 ;;
*.go) ;; # done at directory level
*.tf) lint_tf "${filename}" || lint_result=1 ;;
*.md) lint_md "${filename}" || lint_result=1 ;;
*.py) lint_py "${filename}" || lint_result=1 ;;
esac
# we don't want to spell check tar balls, binaries, Makefile and json files
case "$mimetype.$ext" in
*.tar | *.gz | *.json) ;;
*.req | *.key | *.pem | *.crt) ;;
application/x-executable.*) ;;
text/x-makefile.*) ;;
*) spell_check "${filename}" || lint_result=1 ;;
esac
return $lint_result
}
lint_files() {
local lint_result=0
while read -r filename; do
lint "${filename}" || lint_result=1
done
return $lint_result
}
matches_any() {
local filename="$1"
local patterns="$2"
while read -r pattern; do
# shellcheck disable=SC2053
# Use the [[ operator without quotes on $pattern
# in order to "glob" the provided filename:
[[ "$filename" == $pattern ]] && return 0
done <<<"$patterns"
return 1
}
read_patterns() {
local patterns_file="$1"
if [ -n "$patterns_file" ] && [ -r "$patterns_file" ]; then
sed '/^#.*$/d ; /^\s*$/d' "$patterns_file" # Remove blank lines and comments.
fi
}
filter_out() {
local patterns="$1"
if [ -n "$patterns" ]; then
local filtered_out=()
while read -r filename; do
matches_any "$filename" "$patterns" && filtered_out+=("$filename") || echo "$filename"
done
[ -n "$DEBUG" ] && echo >&2 "> Files filtered out (i.e. NOT linted):" && printf >&2 '%s\n' "${filtered_out[@]}"
else
cat # No patterns provided: simply propagate stdin to stdout.
fi
}
lint_directory() {
local dirname="$1"
local lint_result=0
matches_any "$dirname" "$PATTERNS" && return 0
[ -n "$DEBUG" ] && echo >&2 "> Linting directory: $dirname"
# This test is just checking if there are any Go files in the directory
if compgen -G "$dirname/*.go" >/dev/null; then
lint_go "${dirname}" || lint_result=1
fi
find "$dirname" -maxdepth 1 | filter_out "$PATTERNS" | lint_files || lint_result=1
return $lint_result
}
lint_directories() {
local lint_result=0
while read -r dirname; do
lint_directory "${dirname}" || lint_result=1
done
exit $lint_result
}
list_directories() {
find "$@" \( -name vendor -o -name .git -o -name .cache -o -name .pkg \) -prune -o -type d
}
PATTERNS=$(read_patterns "$LINT_IGNORE_FILE")
[ -n "$DEBUG" ] && echo >&2 "> Filters:" && echo >&2 "$PATTERNS"
if [ $# = 1 ] && [ -f "$1" ]; then
lint "$1"
else
list_directories "$@" | lint_directories
fi