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"