k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/hack/verify-golangci-lint.sh (about)

     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  
    22  usage () {
    23    cat <<EOF >&2
    24  Usage: $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
    35  EOF
    36    exit 1
    37  }
    38  
    39  set -o errexit
    40  set -o nounset
    41  set -o pipefail
    42  
    43  KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
    44  source "${KUBE_ROOT}/hack/lib/init.sh"
    45  source "${KUBE_ROOT}/hack/lib/util.sh"
    46  
    47  kube::golang::setup_env
    48  export GOBIN="${KUBE_OUTPUT_BIN}"
    49  
    50  kube::util::require-jq
    51  
    52  invocation=(./hack/verify-golangci-lint.sh "$@")
    53  golangci=("${GOBIN}/golangci-lint" run)
    54  golangci_config="${KUBE_ROOT}/hack/golangci.yaml"
    55  base=
    56  strict=
    57  hints=
    58  githubactions=
    59  while 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
    94  done
    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.
   105  if tty -s; then
   106      golangci+=(--color=always)
   107  fi
   108  
   109  if [ "$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")
   115  fi
   116  
   117  # Filter out arguments that start with "-" and move them to the run flags.
   118  shift $((OPTIND-1))
   119  targets=()
   120  for arg; do
   121    if [[ "${arg}" == -* ]]; then
   122      golangci+=("${arg}")
   123    else
   124      targets+=("${arg}")
   125    fi
   126  done
   127  
   128  # Install golangci-lint
   129  echo "installing golangci-lint and logcheck plugin from hack/tools into ${GOBIN}"
   130  go -C "${KUBE_ROOT}/hack/tools" install github.com/golangci/golangci-lint/cmd/golangci-lint
   131  if [ "${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
   136  fi
   137  
   138  if [ "${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}")
   152  fi
   153  
   154  cd "${KUBE_ROOT}"
   155  
   156  res=0
   157  run () {
   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.
   169  run
   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.
   174  if [ "$githubactions" ]; then
   175    golangci+=("--out-format=github-actions")
   176    run >"$githubactions" 2>&1
   177  fi
   178  
   179  # print a message based on the result
   180  if [ "$res" -eq 0 ]; then
   181    echo 'Congratulations! All files are passing lint :-)'
   182  else
   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
   219  fi
   220  
   221  # preserve the result
   222  exit "$res"