Files
snapd/run-checks
Fred Lotter 9f5c55b0c1 run-checks: allow default GOPATH for local checks
In the past HACKING.md instructed developers to setup GOPATH explicitly.

This was removed some time ago:
github.com/snapcore/snapd/commit/5b9864a203e9ea26d743436a424699ec07a52ac7

The run-checks script will not work on a clean developer machine, even if
someone follows the HACKING.md instructions.

The problem is that run-checks does not support Go's default GOPATH

For example:

/home/foo> go env GOPATH
/home/foo/go

However, the internal GOPATH still respects externally set variables:

/home/foo> GOPATH="/home/foo/go:/home/foo/go2" go env GOPATH
/home/foo/go:/home/foo/go2

The run-checks script supported falling back to a hardcoded GOPATH location,
based on assumptions of where automated tests check out the repository.

For example:

The github actions repo location:

 ${{ github.workspace }}/src/github.com/snapcore/snapd

resulted in the fallback GOPATH to be:

GOPATH="${GOPATH:-$(realpath "$(dirname "$0")"/../../../../)}"

This was because the GOPATH was placed at the root of the github
action workspace.

However, the location is explicitly set in the environment variable
GOPATH before any call is made to run-checks as part of the job setup and
therefore not needed.

Other references of run-checks always set GOPATH before calling the script.

Changes:

- Use the Go provided "go env GOPATH" command to obtain GOPATH

- Remove the fallback logic for run-checks when determining GOPATH

- Add all possible bin paths from GOPATH to PATH

Signed-off-by: Fred Lotter <fred.lotter@canonical.com>
2023-05-15 11:05:05 +02:00

405 lines
13 KiB
Bash
Executable File

#!/bin/bash -eu
export LANG=C.UTF-8
export LANGUAGE=en
if command -v goctest >/dev/null; then
goctest="goctest"
else
goctest="go test"
fi
COVERMODE=${COVERMODE:-atomic}
COVERAGE_SUFFIX=${GO_BUILD_TAGS:-notags}
COVERAGE_OUT=${COVERAGE_OUT:-.coverage/coverage-$COVERAGE_SUFFIX.cov}
CHANGED_FILES=${CHANGED_FILES:-""}
if [ -z "${GITHUB_WORKFLOW:-}" ]; then
# when *not* running inside github, ensure we use go-1.18 by default
export PATH=/usr/lib/go-1.18/bin:"${PATH}"
fi
# add workaround for https://github.com/golang/go/issues/24449
if [ "$(uname -m)" = "s390x" ]; then
if go version | grep -q go1.10; then
echo "covermode 'atomic' crashes on s390x with go1.10, reseting "
echo "to 'set'. see https://github.com/golang/go/issues/24449"
COVERMODE="set"
fi
fi
# If GOPATH is set in the shell environment, the path will be reflected
# in $(go env GOPATH). If no shell path was set, Go will default the internal
# GOPATH to $HOME/go. Note that GOPATH may contain a colon delimited set
# of paths, so in order to run any binary from any of the installed GOPATHs
# we must add all the possible bin locations.
GOBINS=$(go env GOPATH | sed 's|:|/bin:|g' | sed 's|.*|\0/bin|g')
export PATH="$PATH:$GOBINS"
short=
STATIC=
UNIT=
case "${1:-all}" in
all)
STATIC=1
UNIT=1
;;
--static)
STATIC=1
;;
--unit)
UNIT=1
;;
--short-unit)
UNIT=1
short=1
;;
*)
echo "Wrong flag ${1}. To run a single suite use --static, --unit."
exit 1
esac
CURRENTTRAP="true"
EXIT_CODE=99
store_exit_code() {
EXIT_CODE=$?
}
exit_with_exit_code() {
exit $EXIT_CODE
}
addtrap() {
CURRENTTRAP="$CURRENTTRAP ; $1"
# shellcheck disable=SC2064
trap "store_exit_code; $CURRENTTRAP ; exit_with_exit_code" EXIT
}
goinstall() {
pkg="$1"
version="${2:-}"
# go1.18+ will no longer build/install packages. Here "go install"
# must be used but it will only fetch remote packages if the @latest
# (or similar syntax is used). Instead of checking the version we
# check if the "go install" help mentions this new feature.
if go help install | grep -q @latest; then
go install "${pkg}"@"${version:=latest}"
else
go get -u "${pkg}"@"${version:=latest}"
fi
}
endmsg() {
if [ $EXIT_CODE -eq 0 ]; then
p="success.txt"
m="All good, what could possibly go wrong."
else
p="failure.txt"
m="Crushing failure and despair."
fi
echo
if [ -t 1 ] && [ -z "$STATIC" ]; then
cat "data/$p"
else
echo "$m"
fi
}
addtrap endmsg
missing_interface_spread_test() {
snap_yaml="tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml"
core_snap_yaml="tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml"
classic_snap_yaml="tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml"
for iface in $(go run ./tests/lib/list-interfaces.go) ; do
search="plugs: \\[ $iface \\]"
case "$iface" in
bool-file|gpio|pwm|dsp|netlink-driver|hidraw|i2c|iio|serial-port|spi)
# skip gadget provided interfaces for now
continue
;;
dbus|content)
search="interface: $iface"
;;
autopilot)
search='plugs: \[ autopilot-introspection \]'
;;
esac
if ! grep -q "$search" "$snap_yaml" ; then
echo "Missing high-level test for interface '$iface'. Please add to:"
echo "* $snap_yaml"
echo "* $core_snap_yaml (if needed)"
echo "* $classic_snap_yaml (if needed)"
exit 1
fi
done
}
if [ "$STATIC" = 1 ]; then
./get-deps.sh
# Run static tests.
echo Checking docs
./mdlint.py ./*.md ./**/*.md
# XXX: remove once we can use an action, see workflows/test.yaml for
# details why we still use this script
if [ -n "${GITHUB_PULL_REQUEST:-}" ] && [ "${GITHUB_PULL_REQUEST:-}" != "false" ]; then
echo Checking pull request summary
./check-pr-title.py "$GITHUB_PULL_REQUEST"
fi
# check commit author/committer name for unicode
./check-commit-email.py
if [ -z "${SKIP_GOFMT:-}" ]; then
echo Checking formatting
fmt=""
for dir in $(go list -f '{{.Dir}}' ./... | grep -v '/\(c-\)\?vendor/' ); do
# skip vendor packages
s="$(${GOFMT:-gofmt} -s -d "$dir" || true)"
if [ -n "$s" ]; then
fmt="$s\\n$fmt"
fi
done
if [ -n "$fmt" ]; then
echo "Formatting wrong in following files:"
# shellcheck disable=SC2001
echo "$fmt" | sed -e 's/\\n/\n/g'
exit 1
fi
fi
# go vet
echo Running vet
go list ./... | grep -v '/vendor/' | xargs go vet
echo 'Checking for usages of http.Status*'
got=""
for dir in $(go list -f '{{.Dir}}' ./... | grep -v '/vendor/' ); do
s="$(grep -nP 'http\.Status(?!Text)' "$dir"/*.go || true)"
if [ -n "$s" ]; then
got="$s\\n$got"
fi
done
if [ -n "$got" ]; then
echo 'Usages of http.Status*, we prefer the numeric values directly:'
echo "$got"
exit 1
fi
echo "Checking for direct usages of math/rand"
got=""
for dir in $(go list -f '{{.Dir}}' ./... | grep -v '/vendor/' ); do
# shellcheck disable=SC2063
s="$(grep -nP --exclude '*_test.go' --exclude 'randutil/*.go' math/rand "$dir"/*.go || true)"
if [ -n "$s" ]; then
got="$s\\n$got"
fi
done
if [ -n "$got" ]; then
echo 'Direct usages of math/rand, we prefer randutil:'
echo "$got"
exit 1
fi
if command -v shellcheck >/dev/null; then
exclude_tools_path=tests/lib/external/snapd-testing-tools
echo "Checking shell scripts..."
if [ -n "$CHANGED_FILES" ]; then
echo "Checking just the changed bash files"
echo "Changed files: $CHANGED_FILES"
# shellcheck disable=SC2086
INITIAL_FILES="$( file -N $CHANGED_FILES | awk -F": " '$2~/shell.script/{print $1}' )"
else
echo "Checking all the bash files"
INITIAL_FILES="$( ( git ls-files -z 2>/dev/null || find . \( -name .git -o -name vendor -o -name c-vendor \) -prune -o -print0) | xargs -0 file -N | awk -F": " '$2~/shell.script/{print $1}' )"
fi
echo "Filtering files"
FILTERED_FILES=
for file in $INITIAL_FILES; do
if ! echo "$file" | grep -q "$exclude_tools_path"; then
FILTERED_FILES="$FILTERED_FILES $file"
fi
done
if [ -n "$FILTERED_FILES" ]; then
echo "Running shellcheck for files: $FILTERED_FILES"
# shellcheck disable=SC2086
shellcheck -x $FILTERED_FILES
else
echo "Skipping shellcheck, no files to check"
fi
regexp='GOPATH(?!%%:\*)(?!:)[^= ]*/'
if grep -qPr --exclude HACKING.md --exclude 'Makefile.*' --exclude-dir .git --exclude-dir vendor "$regexp"; then
echo "Using GOPATH as if it were a single entry and not a list:"
grep -PHrn -C1 --color=auto --exclude HACKING.md --exclude 'Makefile.*' --exclude-dir .git --exclude-dir vendor "$regexp"
echo "Use GOHOME, or {GOPATH%%:*}, instead."
exit 1
fi
unset regexp
# also run spread-shellcheck
FILTERED_FILES="spread.yaml"
if [ -n "$CHANGED_FILES" ]; then
# shellcheck disable=SC2086
for changed_file in $CHANGED_FILES; do
if [[ $changed_file == */task.yaml ]]; then
FILTERED_FILES="$FILTERED_FILES $changed_file"
fi
done
else
FILTERED_FILES="$FILTERED_FILES tests"
fi
# XXX: exclude core20-preseed test as its environment block confuses shellcheck, and it's not possible to disable shellcheck there.
# shellcheck disable=SC2086
./tests/lib/external/snapd-testing-tools/utils/spread-shellcheck $FILTERED_FILES --exclude "$exclude_tools_path" --exclude "tests/nested/manual/core20-preseed"
fi
if [ -z "${SKIP_MISSPELL-}" ]; then
echo "Checking spelling errors"
if ! command -v misspell >/dev/null; then
goinstall github.com/client9/misspell/cmd/misspell
fi
# FIXME: auter is only misspelled in the changelog so we should fix there
# PROCES is used in the seccomp tests (PRIO_PROCES{,S,SS})
# exportfs is used in the nfs-support test
# becuase because of misspell in upstream steam rules (PR#12657)
MISSPELL_IGNORE="auther,PROCES,PROCESSS,proces,processs,exportfs,becuase"
git ls-files -z -- . ':!:./po' ':!:./vendor' ':!:./c-vendor' ':!:./cmd/libsnap-confine-private/bpf/vendor' |
xargs -0 misspell -error -i "$MISSPELL_IGNORE"
fi
if [ -z "${SKIP_INEFFASSIGN-}" ]; then
if dpkg --compare-versions "$(go version | awk '$3 ~ /^go[0-9]/ {print substr($3, 3)}')" ge 1.12; then
echo "Checking for ineffective assignments"
if ! command -v ineffassign >/dev/null; then
goinstall github.com/gordonklaus/ineffassign
fi
# ineffassign knows about ignoring vendor/ \o/
ineffassign ./...
fi
fi
echo "Checking for naked returns"
if ! command -v nakedret >/dev/null; then
goinstall github.com/alexkohler/nakedret v1.0.1
fi
got=$(go list ./... | grep -v '/osutil/udev/' | grep -v '/vendor/' | xargs nakedret 2>&1)
if [ -n "$got" ]; then
echo "$got"
if [ -z "${SKIP_NAKEDRET:-}" ]; then
exit 1
else
echo "Ignoring nakedret errors as requested"
fi
fi
echo "Checking all interfaces have minimal spread test"
missing_interface_spread_test
echo "Checking for incorrect multiline strings in spread tests"
badmultiline=$(find tests -name 'task.yaml' -print0 -o -name 'spread.yaml' -print0 | \
xargs -0 grep -R -n -E '(restore*|prepare*|execute|debug):\s*$' || true)
if [ -n "$badmultiline" ]; then
echo "Incorrect multiline strings at the following locations:"
echo "$badmultiline"
exit 1
fi
echo "Checking for potentially incorrect use of MATCH -v"
badMATCH=$(find tests -name 'task.yaml' -print0 -o -name 'spread.yaml' -print0 | \
xargs -0 grep -R -n -E 'MATCH +-v' || true)
if [ -n "$badMATCH" ]; then
echo "Potentially incorrect use of MATCH -v at the following locations:"
echo "$badMATCH"
exit 1
fi
# FIXME: re-add staticcheck with a matching version for the used go-version
if [ -z "${SKIP_TESTS_FORMAT_CHECK-}" ] || [ "$SKIP_TESTS_FORMAT_CHECK" = 0 ]; then
echo "Checking tests formatting"
./tests/lib/external/snapd-testing-tools/utils/check-test-format ./tests
fi
echo "Checking for usages of !=, == or Equals with ErrNoState"
if got=$(grep -n -R -E "(\!=|==|Equals,) (state\.)?ErrNoState" --include=*.go) ; then
echo "Don't use equality checks with ErrNoState, use errors.Is() instead"
echo "$got"
exit 1
fi
fi
if [ "$UNIT" = 1 ]; then
./get-deps.sh
echo "Show go version"
command -v go
go version
tags=
if [ -n "${GO_BUILD_TAGS-}" ]; then
echo "Using build tags: $GO_BUILD_TAGS"
tags="-tags $GO_BUILD_TAGS"
fi
echo Building
# shellcheck disable=SC2086
go build -v $tags github.com/snapcore/snapd/...
# tests
echo Running tests from "$PWD"
if [ "$short" = 1 ]; then
# shellcheck disable=SC2046,SC2086
GOTRACEBACK=1 $goctest $tags -short -timeout 5m $(go list ./... | grep -v '/vendor/' )
else
coverage=""
if [ -z "${SKIP_COVERAGE-}" ]; then
coverage="-coverprofile=$COVERAGE_OUT -covermode=$COVERMODE"
# Prepare the coverage output profile.
mkdir -p "$(dirname "$COVERAGE_OUT")"
echo "mode: $COVERMODE" > "$COVERAGE_OUT"
else
echo "Skipping test coverage"
fi
# shellcheck disable=SC2046,SC2086
GOTRACEBACK=1 $goctest $tags -timeout 5m $coverage $(go list ./... | grep -v '/vendor/' )
fi
# python unit test for mountinfo.query and version-compare
command -v python2 && python2 ./tests/lib/tools/mountinfo.query --run-unit-tests
command -v python3 && python3 ./tests/lib/tools/mountinfo.query --run-unit-tests
command -v python2 && python2 ./tests/lib/tools/version-compare --run-unit-tests
command -v python3 && python3 ./tests/lib/tools/version-compare --run-unit-tests
fi
UNCLEAN="$(git status -s|grep '^??')" || true
SKIP_UNCLEAN=${SKIP_UNCLEAN=}
if [ -n "$UNCLEAN" ] && [ -z "$SKIP_UNCLEAN" ]; then
cat <<EOF
There are files left in the git tree after the tests:
$UNCLEAN
EOF
exit 1
fi
if [ -n "${SKIP_DIRTY_CHECK-}" ]; then
exit 0
fi
# XXX: re-enable after vendor/vendor.json is removed
# if git describe --always --dirty | grep -q dirty; then
# echo "Build tree is dirty"
# git diff
# exit 1
# fi