From 440e42daf22e07f352fda9b2a49634bc18e07d76 Mon Sep 17 00:00:00 2001 From: Paul Weil Date: Wed, 6 Apr 2016 12:14:28 -0400 Subject: [PATCH] add basic building and testing scripts --- .gitignore | 1 + .travis.yml | 17 ++++++ Makefile | 57 ++++++++++++++++++ hack/build-go.sh | 17 ++++++ hack/common.sh | 85 +++++++++++++++++++++++++++ hack/install-tools.sh | 24 ++++++++ hack/test-go.sh | 131 ++++++++++++++++++++++++++++++++++++++++++ hack/util.sh | 84 +++++++++++++++++++++++++++ hack/verify-gofmt.sh | 29 ++++++++++ 9 files changed, 445 insertions(+) create mode 100644 .travis.yml create mode 100644 Makefile create mode 100755 hack/build-go.sh create mode 100755 hack/common.sh create mode 100755 hack/install-tools.sh create mode 100755 hack/test-go.sh create mode 100755 hack/util.sh create mode 100755 hack/verify-gofmt.sh diff --git a/.gitignore b/.gitignore index 407f51d..15cec27 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +/_output image-inspector diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5c37357 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: go + +go: + - 1.5.3 + - 1.6 + +install: + - export PATH=$GOPATH/bin:./_tools/etcd/bin:$PATH + - make install-travis + +script: + - make verify test-unit + +notifications: + irc: "chat.freenode.net#openshift-dev" + +sudo: false diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e99f86e --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +# Old-skool build tools. +# +# Targets (see each target for more information): +# all: Build code. +# build: Build code. +# test-unit: Run unit tests. +# clean: Clean up. + +OUT_DIR = _output +OUT_PKG_DIR = Godeps/_workspace/pkg + +# Build code. +# +# Example: +# make +# make all +all build: + hack/build-go.sh +.PHONY: all build + +# Remove all build artifacts. +# +# Example: +# make clean +clean: + rm -rf $(OUT_DIR) $(OUT_PKG_DIR) +.PHONY: clean + +# Verify code conventions are properly setup. +# +# Example: +# make verify +verify: build + hack/verify-gofmt.sh +.PHONY: verify + +# Run unit tests. +# +# Args: +# WHAT: Directory names to test. All *_test.go files under these +# directories will be run. If not specified, "everything" will be tested. +# TESTS: Same as WHAT. +# GOFLAGS: Extra flags to pass to 'go' when building. +# TESTFLAGS: Extra flags that should only be passed to hack/test-go.sh +# +# Example: +# make test-unit +# make test-unit WHAT=pkg/build GOFLAGS=-v +test-unit: + GOTEST_FLAGS="$(TESTFLAGS)" hack/test-go.sh $(WHAT) $(TESTS) +.PHONY: test-unit + +# Install travis dependencies +# +install-travis: + hack/install-tools.sh +.PHONY: install-travis diff --git a/hack/build-go.sh b/hack/build-go.sh new file mode 100755 index 0000000..dbdb317 --- /dev/null +++ b/hack/build-go.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# This script sets up a go workspace locally and builds all go components. + +set -o errexit +set -o nounset +set -o pipefail + +STARTTIME=$(date +%s) +CODE_ROOT=$(dirname "${BASH_SOURCE}")/.. +source "${CODE_ROOT}/hack/util.sh" +source "${CODE_ROOT}/hack/common.sh" +ii::log::install_errexit + +ii::build::build_binaries "$@" + +ret=$?; ENDTIME=$(date +%s); echo "$0 took $(($ENDTIME - $STARTTIME)) seconds"; exit "$ret" diff --git a/hack/common.sh b/hack/common.sh new file mode 100755 index 0000000..9bc2990 --- /dev/null +++ b/hack/common.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# The root of the build/dist directory +readonly II_ROOT=$( + unset CDPATH + ii_root=$(dirname "${BASH_SOURCE}")/.. + + cd "${ii_root}" + ii_root=`pwd` + if [ -h "${ii_root}" ]; then + readlink "${ii_root}" + else + pwd + fi +) + +readonly II_GOPATH=$( + unset CDPATH + cd ${II_ROOT}/../../../.. + pwd +) + +readonly II_GO_PACKAGE=github.com/simon3z/image-inspector +readonly II_OUTPUT_SUBPATH="${II_OUTPUT_SUBPATH:-_output/local}" +readonly II_OUTPUT="${II_ROOT}/${II_OUTPUT_SUBPATH}" +readonly II_OUTPUT_BINPATH="${II_OUTPUT}/bin" + +# ii::build::setup_env will check that the `go` commands is available in +# ${PATH}. If not running on Travis, it will also check that the Go version is +# good enough for the webdav code requirements (1.5+). +# +# Output Vars: +# export GOPATH - A modified GOPATH to our created tree along with extra +# stuff. +# export GOBIN - This is actively unset if already set as we want binaries +# placed in a predictable place. +ii::build::setup_env() { + if [[ -z "$(which go)" ]]; then + cat </dev/null 2>&1 + git fetch + git checkout c262de870b618eed648983aa994b03bc04641c72 +popd >/dev/null 2>&1 + +# Re-install using this version of the tool +go install golang.org/x/tools/cmd/vet + + +ret=$?; ENDTIME=$(date +%s); echo "$0 took $(($ENDTIME - $STARTTIME)) seconds"; exit "$ret" diff --git a/hack/test-go.sh b/hack/test-go.sh new file mode 100755 index 0000000..85a0ad5 --- /dev/null +++ b/hack/test-go.sh @@ -0,0 +1,131 @@ +#!/bin/bash +# +# This script runs Go language unit tests for the image-inspector repository. Arguments to this script +# are parsed as a list of packages to test until the first argument starting with '-' or '--' is +# found. That argument and all following arguments are interpreted as flags to be passed directly +# to `go test`. If no arguments are given, then "all" packages are tested. +# +# Coverage reports and jUnit XML reports can be generated by this script as well, but both cannot +# be generated at once. +# +# This script consumes the following parameters as environment variables: +# - DRY_RUN: prints all packages that would be tested with the args that would be used and exits +# - TIMEOUT: the timeout for any one unit test (default '60s') +# - DETECT_RACES: toggles the 'go test' race detector (defaults '-race') +# - COVERAGE_SPEC: a set of flags for 'go test' that specify the coverage behavior (default '-cover -covermode=atomic') +# - GOTEST_FLAGS: any other flags to be sent to 'go test' +set -o errexit +set -o nounset +set -o pipefail + +function exit_trap() { + local return_code=$? + echo "[DEBUG] Exit trap handler got return code ${return_code}" + + end_time=$(date +%s) + + if [[ "${return_code}" -eq "0" ]]; then + verb="succeeded" + else + verb="failed" + fi + + echo "$0 ${verb} after $((${end_time} - ${start_time})) seconds" + exit "${return_code}" +} + +trap exit_trap EXIT + +start_time=$(date +%s) +CODE_ROOT=$(dirname "${BASH_SOURCE}")/.. +source "${CODE_ROOT}/hack/common.sh" +source "${CODE_ROOT}/hack/util.sh" +cd "${CODE_ROOT}" +ii::log::install_errexit +ii::build::setup_env + +# Internalize environment variables we consume and default if they're not set +dry_run="${DRY_RUN:-}" +test_timeout="${TIMEOUT:-120s}" +detect_races="${DETECT_RACES:-true}" +coverage_spec="${COVERAGE_SPEC:--cover -covermode atomic}" +gotest_flags="${GOTEST_FLAGS:-}" + +# determine if user wanted verbosity +verbose= +if [[ "${gotest_flags}" =~ -v( |$) ]]; then + verbose=true +fi + +# Build arguments for 'go test' +if [[ -z "${verbose}" ]]; then + gotest_flags+=" -v" +fi + +if [[ "${detect_races}" == "true" ]]; then + gotest_flags+=" -race" +fi + +# check to see if user has not disabled coverage mode +if [[ -n "${coverage_spec}" ]]; then + # if we have a coverage spec set, we add it. '-race' implies '-cover -covermode atomic' + # but specifying both at the same time does not lead to an error so we can add both specs + gotest_flags+=" ${coverage_spec}" +fi + +# check to see if user has not disabled test timeouts +if [[ -n "${test_timeout}" ]]; then + gotest_flags+=" -timeout ${test_timeout}" +fi + +# list_test_packages_under lists all packages containing Golang test files that we want to run as unit tests +# under the given base dir in the OpenShift Origin tree +function list_test_packages_under() { + local basedir=$@ + + # we do not quote ${basedir} to allow for multiple arguments to be passed in as well as to allow for + # arguments that use expansion, e.g. paths containing brace expansion or wildcards + find ${basedir} -not \( \ + \( \ + -path 'Godeps' \ + -o -path '*_output' \ + -o -path '*.git' \ + -o -path '*Godeps/*' \ + -o -path '*test/*' \ + \) -prune \ + \) -name '*_test.go' | xargs -n1 dirname | sort -u | xargs -n1 printf "${II_GO_PACKAGE}/%s\n" +} + +# Break up the positional arguments into packages that need to be tested and arguments that need to be passed to `go test` +package_args= +for arg in "$@"; do + if [[ "${arg}" =~ -.* ]]; then + # we found an arg that begins with a dash, so we stop interpreting arguments + # henceforth as packages and instead interpret them as flags to give to `go test` + break + fi + # an arg found before the first flag is a package + package_args+=" ${arg}" + shift +done +gotest_flags+=" $*" + +# Determine packages to test +godeps_package_prefix="Godeps/_workspace/src/" +test_packages= +if [[ -n "${package_args}" ]]; then + for package in ${package_args}; do + # If we're trying to recursively test a package under Godeps, strip the Godeps prefix so go test can find the packages correctly + if [[ "${package}" == "${godeps_package_prefix}"*"/..." ]]; then + test_packages="${test_packages} ${package:${#godeps_package_prefix}}" + else + test_packages="${test_packages} ${II_GO_PACKAGE}/${package}" + fi + done +else + # If no packages are given to test, we need to generate a list of all packages with unit tests + ii_test_packages="$(list_test_packages_under '*')" + test_packages="${ii_test_packages}" +fi + +go test ${gotest_flags} ${test_packages} diff --git a/hack/util.sh b/hack/util.sh new file mode 100755 index 0000000..bfa2b3f --- /dev/null +++ b/hack/util.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# Provides simple utility functions + +# Handler for when we exit automatically on an error. +# Borrowed from https://gist.github.com/ahendrix/7030300 +ii::log::errexit() { + local err="${PIPESTATUS[@]}" + + # If the shell we are in doesn't have errexit set (common in subshells) then + # don't dump stacks. + set +o | grep -qe "-o errexit" || return + + set +o xtrace + local code="${1:-1}" + ii::log::error_exit "'${BASH_COMMAND}' exited with status $err" "${1:-1}" 1 +} + +ii::log::install_errexit() { + # trap ERR to provide an error handler whenever a command exits nonzero this + # is a more verbose version of set -o errexit + trap 'ii::log::errexit' ERR + + # setting errtrace allows our ERR trap handler to be propagated to functions, + # expansions and subshells + set -o errtrace +} + +# Log an error and exit. +# Args: +# $1 Message to log with the error +# $2 The error code to return +# $3 The number of stack frames to skip when printing. +ii::log::error_exit() { + local message="${1:-}" + local code="${2:-1}" + local stack_skip="${3:-0}" + stack_skip=$((stack_skip + 1)) + + local source_file=${BASH_SOURCE[$stack_skip]} + local source_line=${BASH_LINENO[$((stack_skip - 1))]} + echo "!!! Error in ${source_file}:${source_line}" >&2 + [[ -z ${1-} ]] || { + echo " ${1}" >&2 + } + + ii::log::stack $stack_skip + + echo "Exiting with status ${code}" >&2 + exit "${code}" +} + +# Print out the stack trace +# +# Args: +# $1 The number of stack frames to skip when printing. +ii::log::stack() { + local stack_skip=${1:-0} + stack_skip=$((stack_skip + 1)) + if [[ ${#FUNCNAME[@]} -gt $stack_skip ]]; then + echo "Call stack:" >&2 + local i + for ((i=1 ; i <= ${#FUNCNAME[@]} - $stack_skip ; i++)) + do + local frame_no=$((i - 1 + stack_skip)) + local source_file=${BASH_SOURCE[$frame_no]} + local source_lineno=${BASH_LINENO[$((frame_no - 1))]} + local funcname=${FUNCNAME[$frame_no]} + echo " $i: ${source_file}:${source_lineno} ${funcname}(...)" >&2 + done + fi +} + +find_files() { + find . -not \( \ + \( \ + -wholename './_output' \ + -o -wholename './_tools' \ + -o -wholename './.*' \ + -o -wholename '*/Godeps/*' \ + \) -prune \ + \) -name '*.go' | sort -u +} + diff --git a/hack/verify-gofmt.sh b/hack/verify-gofmt.sh new file mode 100755 index 0000000..69d7031 --- /dev/null +++ b/hack/verify-gofmt.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# GoFmt apparently is changing @ head... + +set -o errexit +set -o nounset +set -o pipefail + +GO_VERSION=($(go version)) + +if [[ -z $(echo "${GO_VERSION[2]}" | grep -E 'go1.4|go1.5') ]]; then + echo "Unknown go version '${GO_VERSION}', skipping gofmt." >&2 + exit 0 +fi + +CODE_ROOT=$(dirname "${BASH_SOURCE}")/.. +source "${CODE_ROOT}/hack/common.sh" +source "${CODE_ROOT}/hack/util.sh" + +cd "${CODE_ROOT}" + +bad_files=$(find_files | xargs gofmt -s -l) +if [[ -n "${bad_files}" ]]; then + echo "!!! gofmt needs to be run on the following files: " >&2 + echo "${bad_files}" + echo "Try running 'gofmt -s -d [path]'" >&2 + echo "Or autocorrect with 'hack/verify-gofmt.sh | xargs -n 1 gofmt -s -w'" >&2 + exit 1 +fi