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

     1  #!/usr/bin/env bash
     2  
     3  # Copyright 2024 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>] [directory ...]"
    25     -t <revision>: Report changes in code up to and including this revision.
    26                    Default is the current working tree instead of a revision.
    27     -r <revision>: Report change in code added since this revision. Default is
    28                    the common base of origin/master and HEAD.
    29     [directory]:   Check one or more specific directory instead of everything.
    30  EOF
    31    exit 1
    32  }
    33  
    34  set -o errexit
    35  set -o nounset
    36  set -o pipefail
    37  
    38  KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
    39  source "${KUBE_ROOT}/hack/lib/init.sh"
    40  
    41  base=
    42  target=
    43  while getopts "r:t:" o; do
    44      case "${o}" in
    45          r)
    46              base="${OPTARG}"
    47              if [ ! "$base" ]; then
    48                  echo "ERROR: -${o} needs a non-empty parameter" >&2
    49                  echo >&2
    50                  usage
    51              fi
    52              ;;
    53         t)
    54              target="${OPTARG}"
    55              if [ ! "$target" ]; then
    56                  echo "ERROR: -${o} needs a non-empty parameter" >&2
    57                  echo >&2
    58                  usage
    59              fi
    60              ;;
    61          *)
    62              usage
    63              ;;
    64      esac
    65  done
    66  shift $((OPTIND - 1))
    67  
    68  # Check specific directory or everything.
    69  targets=("$@")
    70  if [ ${#targets[@]} -eq 0 ]; then
    71      # This lists all entries in the go.work file as absolute directory paths.
    72      kube::util::read-array targets < <(go list -f '{{.Dir}}' -m)
    73  fi
    74  
    75  # Sanitize paths:
    76  # - We need relative paths because we will invoke apidiff in
    77  #   different work trees.
    78  # - Must start with a dot.
    79  for (( i=0; i<${#targets[@]}; i++ )); do
    80      d="${targets[i]}"
    81      d=$(realpath -s --relative-to="$(pwd)" "${d}")
    82      if [ "${d}" != "." ]; then
    83          # sub-directories have to have a leading dot.
    84          d="./${d}"
    85      fi
    86      targets[i]="${d}"
    87  done
    88  
    89  # Must be a something that git can resolve to a commit.
    90  # "git rev-parse --verify" checks that and prints a detailed
    91  # error.
    92  if [ -n "${target}" ]; then
    93      target="$(git rev-parse --verify "${target}")"
    94  fi
    95  
    96  # Determine defaults.
    97  if [ -z "${base}" ]; then
    98      if ! base="$(git merge-base origin/master "${target:-HEAD}")"; then
    99          echo >&2 "Could not determine default base revision. -r must be used explicitly."
   100          exit 1
   101      fi
   102  fi
   103  base="$(git rev-parse --verify "${base}")"
   104  
   105  # Give some information about what's happening. Failures from "git describe" are ignored
   106  # silently, that's optional information.
   107  describe () {
   108      local rev="$1"
   109      local descr
   110      echo -n "$rev"
   111      if descr=$(git describe --tags "${rev}" 2>/dev/null); then
   112          echo -n " (= ${descr})"
   113      fi
   114      echo
   115  }
   116  echo "Checking $(if [ -n "${target}" ]; then describe "${target}"; else echo "current working tree"; fi) for API changes since $(describe "${base}")."
   117  
   118  kube::golang::setup_env
   119  kube::util::ensure-temp-dir
   120  
   121  # Install apidiff and make sure it's found.
   122  export GOBIN="${KUBE_TEMP}"
   123  PATH="${GOBIN}:${PATH}"
   124  echo "Installing apidiff into ${GOBIN}."
   125  go install golang.org/x/exp/cmd/apidiff@latest
   126  
   127  cd "${KUBE_ROOT}"
   128  
   129  # output_name targets a target directory and prints the base name of
   130  # an output file for that target.
   131  output_name () {
   132      what="$1"
   133  
   134      echo "${what}" | sed -e 's/[^a-zA-Z0-9_-]/_/g' -e 's/$/.out/'
   135  }
   136  
   137  # run invokes apidiff once per target and stores the output
   138  # file(s) in the given directory.
   139  run () {
   140      out="$1"
   141      mkdir -p "$out"
   142      for d in "${targets[@]}"; do
   143          apidiff -m -w "${out}/$(output_name "${d}")" "${d}"
   144      done
   145  }
   146  
   147  # runWorktree checks out a specific revision, then invokes run there.
   148  runWorktree () {
   149      local out="$1"
   150      local worktree="$2"
   151      local rev="$3"
   152  
   153      # Create a copy of the repo with the specific revision checked out.
   154      git worktree add -f -d "${worktree}" "${rev}"
   155      # Clean up the copy on exit.
   156      kube::util::trap_add "git worktree remove -f ${worktree}" EXIT
   157  
   158      # Ready for apidiff.
   159      (
   160          cd "${worktree}"
   161          run "${out}"
   162      )
   163  }
   164  
   165  # Dump old and new api state.
   166  if [ -z "${target}" ]; then
   167      run "${KUBE_TEMP}/after"
   168  else
   169      runWorktree "${KUBE_TEMP}/after" "${KUBE_TEMP}/target" "${target}"
   170  fi
   171  runWorktree "${KUBE_TEMP}/before" "${KUBE_TEMP}/base" "${base}"
   172  
   173  # Now produce a report. All changes get reported because exporting some API
   174  # unnecessarily might also be good to know, but the final exit code will only
   175  # be non-zero if there are incompatible changes.
   176  #
   177  # The report is Markdown-formatted and can be copied into a PR comment verbatim.
   178  res=0
   179  echo
   180  compare () {
   181      what="$1"
   182      before="$2"
   183      after="$3"
   184      changes=$(apidiff -m "${before}" "${after}" 2>&1 | grep -v -e "^Ignoring internal package") || true
   185      echo "## ${what}"
   186      if [ -z "$changes" ]; then
   187          echo "no changes"
   188      else
   189          echo "$changes"
   190          echo
   191      fi
   192      incompatible=$(apidiff -incompatible -m "${before}" "${after}" 2>&1) || true
   193      if [ -n "$incompatible" ]; then
   194          res=1
   195      fi
   196  }
   197  
   198  for d in "${targets[@]}"; do
   199      compare "${d}" "${KUBE_TEMP}/before/$(output_name "${d}")" "${KUBE_TEMP}/after/$(output_name "${d}")"
   200  done
   201  
   202  exit "$res"