From d1eb30ff9da1d67f63a1ebc4432e63ab206f8fef Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Mon, 18 May 2020 11:44:22 -0400 Subject: [PATCH] Add apidiff script to check go signature changes Signed-off-by: Davanum Srinivas --- .github/workflows/test.yml | 24 +++++++ hack/verify-apidiff.sh | 125 +++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100755 hack/verify-apidiff.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ec3a0eec..151f8e38 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,3 +30,27 @@ jobs: docker run --rm -v `pwd`:/go/src/k8s.io/klog -w /go/src/k8s.io/klog \ golangci/golangci-lint:v1.23.8 golangci-lint run --disable-all -v \ -E govet -E misspell -E gofmt -E ineffassign -E golint + apidiff: + runs-on: ubuntu-latest + if: github.base_ref + steps: + - name: Install Go + uses: actions/setup-go@v1 + with: + go-version: 1.13.x + - name: Add GOBIN to PATH + run: echo "::add-path::$(go env GOPATH)/bin" + - name: Install dependencies + run: go get golang.org/x/exp/cmd/apidiff + - name: Checkout old code + uses: actions/checkout@v2 + with: + ref: ${{ github.base_ref }} + path: "old" + - name: Checkout new code + uses: actions/checkout@v2 + with: + path: "new" + - name: APIDiff + run: ./hack/verify-apidiff.sh -d ../old + working-directory: "new" diff --git a/hack/verify-apidiff.sh b/hack/verify-apidiff.sh new file mode 100755 index 00000000..c49370bd --- /dev/null +++ b/hack/verify-apidiff.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash + +# Copyright 2020 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. + +set -o errexit +set -o nounset +set -o pipefail + +function usage { + local script="$(basename $0)" + + echo >&2 "Usage: ${script} [-r | -d ] + +This script should be run at the root of a module. + +-r + Compare the exported API of the local working copy with the + exported API of the local repo at the specified branch or tag. + +-d + Compare the exported API of the local working copy with the + exported API of the specified directory, which should point + to the root of a different version of the same module. + +Examples: + ${script} -r master + ${script} -r v1.10.0 + ${script} -r release-1.10 + ${script} -d /path/to/historical/version +" + exit 1 +} + +ref="" +dir="" +while getopts r:d: o +do case "$o" in + r) ref="$OPTARG";; + d) dir="$OPTARG";; + [?]) usage;; + esac +done + +# If REF and DIR are empty, print usage and error +if [[ -z "${ref}" && -z "${dir}" ]]; then + usage; +fi +# If REF and DIR are both set, print usage and error +if [[ -n "${ref}" && -n "${dir}" ]]; then + usage; +fi + +if ! which apidiff > /dev/null; then + echo "Installing golang.org/x/exp/cmd/apidiff..." + pushd "${TMPDIR:-/tmp}" > /dev/null + go get golang.org/x/exp/cmd/apidiff + popd > /dev/null +fi + +output=$(mktemp -d -t "apidiff.output.XXXX") +cleanup_output () { rm -fr "${output}"; } +trap cleanup_output EXIT + +# If ref is set, clone . to temp dir at $ref, and set $dir to the temp dir +clone="" +base="${dir}" +if [[ -n "${ref}" ]]; then + base="${ref}" + clone=$(mktemp -d -t "apidiff.clone.XXXX") + cleanup_clone_and_output () { rm -fr "${clone}"; cleanup_output; } + trap cleanup_clone_and_output EXIT + git clone . -q --no-tags -b "${ref}" "${clone}" + dir="${clone}" +fi + +pushd "${dir}" >/dev/null + echo "Inspecting API of ${base}..." + go list ./... > packages.txt + for pkg in $(cat packages.txt); do + mkdir -p "${output}/${pkg}" + apidiff -w "${output}/${pkg}/apidiff.output" "${pkg}" + done +popd >/dev/null + +retval=0 + +echo "Comparing with ${base}..." +for pkg in $(go list ./...); do + # New packages are ok + if [ ! -f "${output}/${pkg}/apidiff.output" ]; then + continue + fi + + # Check for incompatible changes to previous packages + incompatible=$(apidiff -incompatible "${output}/${pkg}/apidiff.output" "${pkg}") + if [[ -n "${incompatible}" ]]; then + echo >&2 "FAIL: ${pkg} contains incompatible changes: +${incompatible} +" + retval=1 + fi +done + +# Check for removed packages +removed=$(comm -23 "${dir}/packages.txt" <(go list ./...)) +if [[ -n "${removed}" ]]; then + echo >&2 "FAIL: removed packages: +${removed} +" + retval=1 +fi + +exit $retval