2016-12-14 08:44:18 +00:00
|
|
|
#!/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:
|
2017-07-28 13:20:45 +00:00
|
|
|
# go get -u gopkg.in/mvdan/sh.v1/cmd/shfmt
|
2016-12-14 08:44:18 +00:00
|
|
|
#
|
|
|
|
# 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
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
LINT_IGNORE_FILE=${LINT_IGNORE_FILE:-".lintignore"}
|
|
|
|
|
2016-12-14 08:44:18 +00:00
|
|
|
IGNORE_LINT_COMMENT=
|
|
|
|
IGNORE_SPELLINGS=
|
2017-07-28 13:20:45 +00:00
|
|
|
PARALLEL=
|
2016-12-14 08:44:18 +00:00
|
|
|
while true; do
|
|
|
|
case "$1" in
|
|
|
|
-nocomment)
|
|
|
|
IGNORE_LINT_COMMENT=1
|
|
|
|
shift 1
|
|
|
|
;;
|
|
|
|
-notestpackage)
|
2017-07-28 13:20:45 +00:00
|
|
|
# NOOP, still accepted for backwards compatibility
|
2016-12-14 08:44:18 +00:00
|
|
|
shift 1
|
|
|
|
;;
|
|
|
|
-ignorespelling)
|
|
|
|
IGNORE_SPELLINGS="$2,$IGNORE_SPELLINGS"
|
|
|
|
shift 2
|
|
|
|
;;
|
2017-07-28 13:20:45 +00:00
|
|
|
-p)
|
|
|
|
PARALLEL=1
|
|
|
|
shift 1
|
|
|
|
;;
|
2016-12-14 08:44:18 +00:00
|
|
|
*)
|
|
|
|
break
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
|
|
|
spell_check() {
|
|
|
|
local filename="$1"
|
|
|
|
local lint_result=0
|
|
|
|
|
|
|
|
# we don't want to spell check tar balls, binaries, Makefile and json files
|
|
|
|
if file "$filename" | grep executable >/dev/null 2>&1; then
|
|
|
|
return $lint_result
|
|
|
|
fi
|
|
|
|
if [[ $filename == *".tar" || $filename == *".gz" || $filename == *".json" || $(basename "$filename") == "Makefile" ]]; then
|
|
|
|
return $lint_result
|
|
|
|
fi
|
|
|
|
|
|
|
|
# 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() {
|
|
|
|
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 tool 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
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
if ! diff -u "${filename}" <(shfmt -i 4 "${filename}"); then
|
2016-12-14 08:44:18 +00:00
|
|
|
lint_result=1
|
|
|
|
echo "${filename}: run shfmt -i 4 -w ${filename}"
|
|
|
|
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
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
if ! diff -u <(hclfmt "${filename}") "${filename}"; then
|
2016-12-14 08:44:18 +00:00
|
|
|
lint_result=1
|
|
|
|
echo "${filename}: run hclfmt -w ${filename}"
|
|
|
|
fi
|
|
|
|
|
|
|
|
return $lint_result
|
|
|
|
}
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
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}: 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
|
|
|
|
}
|
|
|
|
|
2016-12-14 08:44:18 +00:00
|
|
|
lint() {
|
|
|
|
filename="$1"
|
|
|
|
ext="${filename##*\.}"
|
|
|
|
local lint_result=0
|
|
|
|
|
|
|
|
# Don't lint deleted files
|
|
|
|
if [ ! -f "$filename" ]; then
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
# Don't lint specific files
|
2016-12-14 08:44:18 +00:00
|
|
|
case "$(basename "${filename}")" in
|
|
|
|
static.go) return ;;
|
|
|
|
coverage.html) return ;;
|
2017-07-28 13:20:45 +00:00
|
|
|
*.pb.go) return ;;
|
2016-12-14 08:44:18 +00:00
|
|
|
esac
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
if [[ "$(file --mime-type "${filename}" | awk '{print $2}')" == "text/x-shellscript" ]]; then
|
2016-12-14 08:44:18 +00:00
|
|
|
ext="sh"
|
|
|
|
fi
|
|
|
|
|
|
|
|
case "$ext" in
|
|
|
|
go) lint_go "${filename}" || lint_result=1 ;;
|
|
|
|
sh) lint_sh "${filename}" || lint_result=1 ;;
|
|
|
|
tf) lint_tf "${filename}" || lint_result=1 ;;
|
2017-07-28 13:20:45 +00:00
|
|
|
md) lint_md "${filename}" || lint_result=1 ;;
|
|
|
|
py) lint_py "${filename}" || lint_result=1 ;;
|
2016-12-14 08:44:18 +00:00
|
|
|
esac
|
|
|
|
|
|
|
|
spell_check "${filename}" || lint_result=1
|
|
|
|
|
|
|
|
return $lint_result
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_files() {
|
|
|
|
local lint_result=0
|
|
|
|
while read -r filename; do
|
|
|
|
lint "${filename}" || lint_result=1
|
|
|
|
done
|
|
|
|
exit $lint_result
|
|
|
|
}
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
filter_out() {
|
|
|
|
local patterns_file="$1"
|
|
|
|
if [ -n "$patterns_file" ] && [ -r "$patterns_file" ]; then
|
|
|
|
local patterns
|
|
|
|
patterns=$(sed '/^#.*$/d ; /^\s*$/d' "$patterns_file") # Remove blank lines and comments before we start iterating.
|
|
|
|
[ -n "$DEBUG" ] && echo >&2 "> Filters:" && echo >&2 "$patterns"
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-12-14 08:44:18 +00:00
|
|
|
list_files() {
|
|
|
|
if [ $# -gt 0 ]; then
|
2017-07-28 13:20:45 +00:00
|
|
|
find "$@" | grep -vE '(^|/)vendor/'
|
2016-12-14 08:44:18 +00:00
|
|
|
else
|
2017-07-28 13:20:45 +00:00
|
|
|
git ls-files --exclude-standard | grep -vE '(^|/)vendor/'
|
2016-12-14 08:44:18 +00:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2017-07-28 13:20:45 +00:00
|
|
|
if [ $# = 1 ] && [ -f "$1" ]; then
|
|
|
|
lint "$1"
|
|
|
|
elif [ -n "$PARALLEL" ]; then
|
|
|
|
list_files "$@" | filter_out "$LINT_IGNORE_FILE" | xargs -n1 -P16 "$0"
|
|
|
|
else
|
|
|
|
list_files "$@" | filter_out "$LINT_IGNORE_FILE" | lint_files
|
|
|
|
fi
|