Files
kubernetes/hack/apidiff.sh
Patrick Ohly 4bb5fe68cc hack: use "latest" apidiff
Pinning apidiff to a specific version shouldn't be necessary because, if past
experience in klog holds true, the latest version just works. This way we don't
have to remember to bump up the revision.

The downside of using "latest" is that a compromise of that version would give
an attacker the ability to run code in the CI and on developer machines.
2024-04-23 15:03:22 +02:00

155 lines
4.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Copyright 2024 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script checks the coding style for the Go language files using
# golangci-lint. Which checks are enabled depends on command line flags. The
# default is a minimal set of checks that all existing code passes without
# issues.
usage () {
cat <<EOF >&2
Usage: $0 [-r <revision>] [directory]"
-r <revision>: only report issues in code added since that revision. Default is
the common base of origin/master and HEAD.
[package]: check directory instead of everything
EOF
exit 1
}
set -o errexit
set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
source "${KUBE_ROOT}/hack/lib/init.sh"
base="$(git merge-base origin/master HEAD)"
while getopts "r:" o; do
case "${o}" in
r)
base="${OPTARG}"
if [ ! "$base" ]; then
echo "ERROR: -${o} needs a non-empty parameter" >&2
echo >&2
usage
fi
;;
*)
usage
;;
esac
done
shift $((OPTIND - 1))
# Check specific directory or everything.
targets=("$@")
if [ ${#targets[@]} -eq 0 ]; then
# This lists all entries in the go.work file as absolute directory paths.
kube::util::read-array targets < <(
go list -f '{{.Dir}}' -m | while read -r d; do
# We need relative paths because we will invoke apidiff in
# different work trees.
d="$(realpath -s --relative-to="$(pwd)" "${d}")"
if [ "${d}" != "." ]; then
# sub-directories have to have a leading dot.
d="./${d}"
fi
echo "${d}"
done
)
fi
# Must be a something that git can resolve to a commit.
# "git rev-parse --verify" checks that and prints a detailed
# error.
base="$(git rev-parse --verify "$base")"
kube::golang::setup_env
kube::util::ensure-temp-dir
# Install apidiff and make sure it's found.
export GOBIN="${KUBE_TEMP}"
PATH="${GOBIN}:${PATH}"
echo "installing apidiff into ${GOBIN}"
go install golang.org/x/exp/cmd/apidiff@latest
cd "${KUBE_ROOT}"
# output_name targets a target directory and prints the base name of
# an output file for that target.
output_name () {
what="$1"
echo "${what}" | sed -e 's/[^a-zA-Z0-9_-]/_/g' -e 's/$/.out/'
}
# run invokes apidiff once per target and stores the output
# file(s) in the given directory.
run () {
out="$1"
mkdir -p "$out"
for d in "${targets[@]}"; do
apidiff -m -w "${out}/$(output_name "${d}")" "${d}"
done
}
# First the current code.
run "${KUBE_TEMP}/after"
WORKTREE="${KUBE_TEMP}/worktree"
# Create a copy of the repo with the base checked out.
git worktree add -f -d "${WORKTREE}" "${base}"
# Clean up the copy on exit.
kube::util::trap_add "git worktree remove -f ${WORKTREE}" EXIT
# Base ready for apidiff.
(
cd "${WORKTREE}"
run "${KUBE_TEMP}/before"
)
# Now produce a report. All changes get reported because exporting some API
# unnecessarily might also be good to know, but the final exit code will only
# be non-zero if there are incompatible changes.
#
# The report is Markdown-formatted and can be copied into a PR comment verbatim.
res=0
echo
compare () {
what="$1"
before="$2"
after="$3"
changes=$(apidiff -m "${before}" "${after}" 2>&1 | grep -v -e "^Ignoring internal package") || true
echo "## ${what}"
if [ -z "$changes" ]; then
echo "no changes"
else
echo "$changes"
echo
fi
incompatible=$(apidiff -incompatible -m "${before}" "${after}" 2>&1) || true
if [ -n "$incompatible" ]; then
res=1
fi
}
for d in "${targets[@]}"; do
compare "${d}" "${KUBE_TEMP}/before/$(output_name "${d}")" "${KUBE_TEMP}/after/$(output_name "${d}")"
done
exit "$res"