...
1#!/usr/bin/env bash
2
3# Copyright 2021 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# This script checks the coding style for the Go language files using
18# golangci-lint. Which checks are enabled depends on command line flags. The
19# default is a minimal set of checks that all existing code passes without
20# issues.
21
22usage () {
23 cat <<EOF >&2
24Usage: $0 [-r <revision>|-a] [-s] [-c none|<config>] [-- <golangci-lint run flags>] [packages]"
25 -r <revision>: only report issues in code added since that revision
26 -a: automatically select the common base of origin/master and HEAD
27 as revision
28 -s: select a strict configuration for new code
29 -n: in addition to strict checking, also enable hints (aka nits) that may are may not
30 be useful
31 -g <github action file>: also write results with --out-format=github-actions
32 to a separate file
33 -c <config|"none">: use the specified configuration or none instead of the default hack/golangci.yaml
34 [packages]: check specific packages or directories instead of everything
35EOF
36 exit 1
37}
38
39set -o errexit
40set -o nounset
41set -o pipefail
42
43KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
44source "${KUBE_ROOT}/hack/lib/init.sh"
45source "${KUBE_ROOT}/hack/lib/util.sh"
46
47kube::golang::setup_env
48export GOBIN="${KUBE_OUTPUT_BIN}"
49
50kube::util::require-jq
51
52invocation=(./hack/verify-golangci-lint.sh "$@")
53golangci=("${GOBIN}/golangci-lint" run)
54golangci_config="${KUBE_ROOT}/hack/golangci.yaml"
55base=
56strict=
57hints=
58githubactions=
59while getopts "ar:sng:c:" o; do
60 case "${o}" in
61 a)
62 base="$(git merge-base origin/master HEAD)"
63 ;;
64 r)
65 base="${OPTARG}"
66 if [ ! "$base" ]; then
67 echo "ERROR: -c needs a non-empty parameter" >&2
68 echo >&2
69 usage
70 fi
71 ;;
72 s)
73 golangci_config="${KUBE_ROOT}/hack/golangci-strict.yaml"
74 strict=1
75 ;;
76 n)
77 golangci_config="${KUBE_ROOT}/hack/golangci-hints.yaml"
78 hints=1
79 ;;
80 g)
81 githubactions="${OPTARG}"
82 ;;
83 c)
84 if [ "${OPTARG}" = "none" ]; then
85 golangci_config=""
86 else
87 golangci_config="${OPTARG}"
88 fi
89 ;;
90 *)
91 usage
92 ;;
93 esac
94done
95
96# Below the output of golangci-lint is going to be piped into sed to add
97# a prefix to each output line. This helps make the output more visible
98# in the Prow log viewer ("error" is a key word there) and ensures that
99# only those lines get included as failure message in a JUnit file
100# by "make verify".
101#
102# The downside is that the automatic detection whether to colorize output
103# doesn't work anymore, so here we force it ourselves when connected to
104# a tty.
105if tty -s; then
106 golangci+=(--color=always)
107fi
108
109if [ "$base" ]; then
110 # Must be a something that git can resolve to a commit.
111 # "git rev-parse --verify" checks that and prints a detailed
112 # error.
113 base="$(git rev-parse --verify "$base")"
114 golangci+=(--new --new-from-rev="$base")
115fi
116
117# Filter out arguments that start with "-" and move them to the run flags.
118shift $((OPTIND-1))
119targets=()
120for arg; do
121 if [[ "${arg}" == -* ]]; then
122 golangci+=("${arg}")
123 else
124 targets+=("${arg}")
125 fi
126done
127
128# Install golangci-lint
129echo "installing golangci-lint and logcheck plugin from hack/tools into ${GOBIN}"
130go -C "${KUBE_ROOT}/hack/tools" install github.com/golangci/golangci-lint/cmd/golangci-lint
131if [ "${golangci_config}" ]; then
132 # This cannot be used without a config.
133 # This uses `go build` because `go install -buildmode=plugin` doesn't work
134 # (on purpose: https://github.com/golang/go/issues/64964).
135 go -C "${KUBE_ROOT}/hack/tools" build -o "${GOBIN}/logcheck.so" -buildmode=plugin sigs.k8s.io/logtools/logcheck/plugin
136fi
137
138if [ "${golangci_config}" ]; then
139 # The relative path to _output/local/bin only works if that actually is the
140 # GOBIN. If not, then we have to make a temporary copy of the config and
141 # replace the path with an absolute one. This could be done also
142 # unconditionally, but the invocation that is printed below is nicer if we
143 # don't to do it when not required.
144 if grep -q 'path: ../_output/local/bin/' "${golangci_config}" &&
145 [ "${GOBIN}" != "${KUBE_ROOT}/_output/local/bin" ]; then
146 kube::util::ensure-temp-dir
147 patched_golangci_config="${KUBE_TEMP}/$(basename "${golangci_config}")"
148 sed -e "s;path: ../_output/local/bin/;path: ${GOBIN}/;" "${golangci_config}" >"${patched_golangci_config}"
149 golangci_config="${patched_golangci_config}"
150 fi
151 golangci+=(--config="${golangci_config}")
152fi
153
154cd "${KUBE_ROOT}"
155
156res=0
157run () {
158 if [[ "${#targets[@]}" -eq 0 ]]; then
159 # Doing it this way is MUCH faster than simply saying "all", and there doesn't
160 # seem to be a simpler way to express "this whole workspace".
161 kube::util::read-array targets < <(
162 go work edit -json | jq -r '.Use[].DiskPath + "/..."'
163 )
164 fi
165 echo "running ${golangci[*]} ${targets[*]}" >&2
166 "${golangci[@]}" "${targets[@]}" 2>&1 | sed -e 's;^;ERROR: ;' >&2 || res=$?
167}
168# First run with normal output.
169run
170
171# Then optionally do it again with github-actions as format.
172# Because golangci-lint caches results, this is faster than the
173# initial invocation.
174if [ "$githubactions" ]; then
175 golangci+=("--out-format=github-actions")
176 run >"$githubactions" 2>&1
177fi
178
179# print a message based on the result
180if [ "$res" -eq 0 ]; then
181 echo 'Congratulations! All files are passing lint :-)'
182else
183 {
184 echo
185 echo "Please review the above warnings. You can test via \"${invocation[*]}\""
186 echo 'If the above warnings do not make sense, you can exempt this warning with a comment'
187 echo ' (if your reviewer is okay with it).'
188 if [ "$strict" ]; then
189 echo
190 echo 'golangci-strict.yaml was used as configuration. Warnings must be fixed in'
191 echo 'new or modified code.'
192 elif [ "$hints" ]; then
193 echo
194 echo 'golangci-hints.yaml was used as configuration. Some of the reported issues may'
195 echo 'have to be fixed while others can be ignored, depending on the circumstances'
196 echo 'and/or personal preferences. To determine which issues have to be fixed, check'
197 echo 'the report that uses golangci-strict.yaml (= pull-kubernetes-verify-lint).'
198 fi
199 if [ "$strict" ] || [ "$hints" ]; then
200 echo
201 echo 'If you feel that this warns about issues that should be ignored by default,'
202 echo 'then please discuss with your reviewer and propose'
203 echo 'a change for hack/golangci.yaml.in as part of your PR.'
204 echo
205 echo 'Please do not create PRs which fix these issues in existing code just'
206 echo 'because the linter warns about them. Often they are harmless and not'
207 echo 'worth the cost associated with a PR (time required to review, code churn).'
208 echo 'Instead, propose to fix certain linter issues in an issue first and'
209 echo 'discuss there with maintainers. PRs are welcome if they address a real'
210 echo 'problem, which then needs to be explained in the PR.'
211 fi
212 echo
213 echo 'In general please prefer to fix the error, we have already disabled specific lints'
214 echo ' that the project chooses to ignore.'
215 echo 'See: https://golangci-lint.run/usage/false-positives/'
216 echo
217 } >&2
218 exit 1
219fi
220
221# preserve the result
222exit "$res"
View as plain text