verify-staticcheck.sh 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #!/usr/bin/env bash
  2. # Copyright 2014 The Kubernetes Authors.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. # This script lints each package by `staticcheck`.
  16. # Usage: `hack/verify-staticcheck.sh`.
  17. # NOTE: To ignore issues detected a package, add it to the
  18. # `.staticcheck_failures` blacklist.
  19. set -o errexit
  20. set -o nounset
  21. set -o pipefail
  22. KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
  23. source "${KUBE_ROOT}/hack/lib/init.sh"
  24. source "${KUBE_ROOT}/hack/lib/util.sh"
  25. kube::golang::verify_go_version
  26. FOCUS="${1:-}"
  27. FOCUS="${FOCUS%/}" # Remove the ending "/"
  28. # See https://staticcheck.io/docs/checks
  29. CHECKS=(
  30. "all"
  31. "-S1*" # Omit code simplifications for now.
  32. "-ST1*" # Mostly stylistic, redundant w/ golint
  33. )
  34. export IFS=','; checks="${CHECKS[*]}"; unset IFS
  35. # Packages to ignore due to bugs in staticcheck
  36. # NOTE: To ignore issues detected a package, add it to the .staticcheck_failures blacklist
  37. IGNORE=(
  38. "vendor/k8s.io/kubectl/pkg/cmd/edit/testdata" # golang/go#24854, dominikh/go-tools#565
  39. )
  40. export IFS='|'; ignore_pattern="^(${IGNORE[*]})\$"; unset IFS
  41. # Ensure that we find the binaries we build before anything else.
  42. export GOBIN="${KUBE_OUTPUT_BINPATH}"
  43. PATH="${GOBIN}:${PATH}"
  44. # Install staticcheck from vendor
  45. echo 'installing staticcheck from vendor'
  46. go install k8s.io/kubernetes/vendor/honnef.co/go/tools/cmd/staticcheck
  47. cd "${KUBE_ROOT}"
  48. # Check that the file is in alphabetical order
  49. failure_file="${KUBE_ROOT}/hack/.staticcheck_failures"
  50. kube::util::check-file-in-alphabetical-order "${failure_file}"
  51. all_packages=()
  52. while IFS='' read -r line; do
  53. # Prepend './' to get staticcheck to treat these as paths, not packages.
  54. all_packages+=("./$line")
  55. done < <( hack/make-rules/helpers/cache_go_dirs.sh "${KUBE_ROOT}/_tmp/all_go_dirs" |
  56. grep "^${FOCUS:-.}" |
  57. grep -vE "(third_party|generated|clientset_generated|/_)" |
  58. grep -vE "$ignore_pattern" )
  59. failing_packages=()
  60. if [[ -z $FOCUS ]]; then # Ignore failing_packages in FOCUS mode
  61. while IFS='' read -r line; do failing_packages+=("$line"); done < <(cat "$failure_file")
  62. fi
  63. errors=()
  64. not_failing=()
  65. while read -r error; do
  66. # Ignore compile errors caused by lack of files due to build tags.
  67. # TODO: Add verification for these directories.
  68. ignore_no_files="^-: build constraints exclude all Go files in .* \(compile\)"
  69. if [[ $error =~ $ignore_no_files ]]; then
  70. continue
  71. fi
  72. file="${error%%:*}"
  73. pkg="$(dirname "$file")"
  74. kube::util::array_contains "$pkg" "${failing_packages[@]}" && in_failing=$? || in_failing=$?
  75. if [[ "${in_failing}" -ne "0" ]]; then
  76. errors+=( "${error}" )
  77. elif [[ "${in_failing}" -eq "0" ]]; then
  78. really_failing+=( "$pkg" )
  79. fi
  80. done < <(staticcheck -checks "${checks}" "${all_packages[@]}" 2>/dev/null || true)
  81. export IFS=$'\n' # Expand ${really_failing[*]} to separate lines
  82. kube::util::read-array really_failing < <(sort -u <<<"${really_failing[*]}")
  83. unset IFS
  84. for pkg in "${failing_packages[@]}"; do
  85. if ! kube::util::array_contains "$pkg" "${really_failing[@]}"; then
  86. not_failing+=( "$pkg" )
  87. fi
  88. done
  89. # Check that all failing_packages actually still exist
  90. gone=()
  91. for p in "${failing_packages[@]}"; do
  92. if ! kube::util::array_contains "./$p" "${all_packages[@]}"; then
  93. gone+=( "$p" )
  94. fi
  95. done
  96. # Check to be sure all the packages that should pass check are.
  97. if [ ${#errors[@]} -eq 0 ]; then
  98. echo 'Congratulations! All Go source files have passed staticcheck.'
  99. else
  100. {
  101. echo "Errors from staticcheck:"
  102. for err in "${errors[@]}"; do
  103. echo "$err"
  104. done
  105. echo
  106. echo 'Please review the above warnings. You can test via:'
  107. echo ' hack/verify-staticcheck.sh <failing package>'
  108. echo 'If the above warnings do not make sense, you can exempt the line or file. See:'
  109. echo ' https://staticcheck.io/docs/#ignoring-problems'
  110. echo
  111. } >&2
  112. exit 1
  113. fi
  114. if [[ ${#not_failing[@]} -gt 0 ]]; then
  115. {
  116. echo "Some packages in hack/.staticcheck_failures are passing staticcheck. Please remove them."
  117. echo
  118. for p in "${not_failing[@]}"; do
  119. echo " $p"
  120. done
  121. echo
  122. } >&2
  123. exit 1
  124. fi
  125. if [[ ${#gone[@]} -gt 0 ]]; then
  126. {
  127. echo "Some packages in hack/.staticcheck_failures do not exist anymore. Please remove them."
  128. echo
  129. for p in "${gone[@]}"; do
  130. echo " $p"
  131. done
  132. echo
  133. } >&2
  134. exit 1
  135. fi