From 6e503a37820a6d0f522184eaabb209e87fc5590c Mon Sep 17 00:00:00 2001 From: Jonathan Yu Date: Thu, 9 Sep 2021 03:21:59 +0000 Subject: [PATCH] chore: run automatic build and test --- .github/workflows/build.yaml | 50 +++++++++++++++++++ scripts/install_deps.sh | 61 +++++++++++++++++++++++ scripts/lib.sh | 94 ++++++++++++++++++++++++++++++++++++ scripts/test_go.sh | 59 ++++++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 .github/workflows/build.yaml create mode 100755 scripts/install_deps.sh create mode 100644 scripts/lib.sh create mode 100755 scripts/test_go.sh diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..68fcdb6 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,50 @@ +name: Build + +on: + push: + branches: + - main + + pull_request: + branches: + - main + + workflow_dispatch: + +permissions: + actions: none + checks: none + contents: read + deployments: none + issues: none + packages: none + pull-requests: none + repository-projects: none + security-events: none + statuses: none + +jobs: + build: + name: Build + runs-on: ubuntu-20.04 + steps: + - name: Cancel Previous Runs + if: github.event_type == 'pull_request' + uses: styfle/cancel-workflow-action@0.9.1 + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: '^1.16.7' + + - name: Install dependencies + run: ./scripts/install_deps.sh + + - name: Lint + run: make lint + + - name: Tests + run: ./scripts/test_go.sh diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh new file mode 100755 index 0000000..c322933 --- /dev/null +++ b/scripts/install_deps.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# +# This script installs dependencies to /usr/local/bin. + +set -euo pipefail +PROJECT_ROOT=$(git rev-parse --show-toplevel) +cd "$PROJECT_ROOT" +source "./scripts/lib.sh" + +TMPDIR=$(mktemp -d) +TMPBIN="${TMPDIR}/bin" +BINDIR="/usr/local/bin" + +curl_flags=( + --silent + --show-error + --location +) + +# Install Go programs +export GOPATH="$TMPDIR/go" + +run_trace false mkdir --parents "$GOPATH" + +# goveralls collects code coverage metrics from tests +# and sends to Coveralls +run_trace false go install github.com/mattn/goveralls@v0.0.9 + +# Install binaries only +run_trace false sudo install --mode=0755 --target-directory="$BINDIR" "$GOPATH/bin/*" + +# Install packages via apt where available +run_trace false sudo apt-get install --no-install-recommends --yes \ + shellcheck + +# Extract binaries as non-root user, then sudo install +run_trace false mkdir --parents "$TMPBIN" + +# gotestsum makes test output more readable +GOTESTSUM_VERSION="1.7.0" +run_trace false curl "${curl_flags[@]}" "https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz" \| \ + tar --extract --gzip --directory="$TMPBIN" --file=- gotestsum + +# golangci-lint to lint Go code with multiple tools +GOLANGCI_LINT_VERSION="1.42.1" +run_trace false curl "${curl_flags[@]}" "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCI_LINT_VERSION}/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \| \ + tar --extract --gzip --directory="$TMPBIN" --file=- --strip-components=1 "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64/golangci-lint" + +# goreleaser to compile, cross-compile, and release binaries +GORELEASER_VERSION="0.178.0" +run_trace false curl "${curl_flags[@]}" "https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}/goreleaser_Linux_x86_64.tar.gz" \| \ + tar --extract --gzip --directory="$TMPBIN" --file=- "goreleaser" + +run_trace false sudo install --mode=0755 --target-directory="$BINDIR" "$TMPBIN/*" + +run_trace false command -v \ + golangci-lint \ + goreleaser \ + gotestsum + +run_trace false sudo rm --verbose --recursive --force "$TMPDIR" diff --git a/scripts/lib.sh b/scripts/lib.sh new file mode 100644 index 0000000..d70427f --- /dev/null +++ b/scripts/lib.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Emit a message to stderr and exit. +# +# This prints the arguments to stderr before exiting. +# +# Example: +# error "Missing flag abc" +# program-failure-info | error +function error() { + set +x + echo + echo "$@" "$(cat)" >&2 + echo + exit 1 +} + +# Check if dependencies are available. +# +# If any dependencies are missing, an error message will be printed to +# stderr and the program will exit, running traps on EXIT beforehand. +# +# Example: +# check_dependencies git bash node +check_dependencies() { + local missing=false + for command in "$@"; do + if ! command -v "$command" &> /dev/null; then + echo "$0: script requires '$command', but it is not in your PATH" >&2 + missing=true + fi + done + + if [ "$missing" = true ]; then + exit 1 + fi +} + +# Indent output by (indent) levels +# +# Example: +# echo "example" | indent 2 +# cat file.txt | indent +function indent() { + local indentSize=2 + local indent=1 + if [ -n "${1:-}" ]; then + indent="$1" + fi + pr --omit-header --indent=$((indent * indentSize)) +} + +# Run a command, with tracing. +# +# This prints a command to stderr for tracing, in a format similar to +# the bash xtrace option (i.e. set -x, set -o xtrace), then runs it using +# eval if the first argument (dry run) is false. +# +# If dry run is true, the command and arguments will be printed, but +# not executed. +# +# Example: +# run_trace $DRY_RUN rm -rf / +# run_trace false echo "abc" \| indent +function run_trace() { + local args=("$@") + local dry_run="${args[0]}" + args=("${args[@]:1}") + + # If we're running in GitHub Actions, use the special syntax + # to start/end a group for each traced command + if [[ -n "${GITHUB_ACTIONS:-}" && "$dry_run" = false ]]; then + echo "::group::Run ${args[*]}" >&2 + else + echo "+ ${args[*]}" >&2 + fi + + local exit_status=0 + if [ "$dry_run" = false ]; then + eval "${args[@]}" + + # Save the exit status code from eval, otherwise it will be + # obscured by future commands. + exit_status="$?" + fi + + if [[ -n "${GITHUB_ACTIONS:-}" && "$dry_run" = false ]]; then + echo "::endgroup::" >&2 + fi + + return "$exit_status" +} diff --git a/scripts/test_go.sh b/scripts/test_go.sh new file mode 100755 index 0000000..3ac7d43 --- /dev/null +++ b/scripts/test_go.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# +# Run unit and integration tests for Go code + +set -euo pipefail +PROJECT_ROOT="$(git rev-parse --show-toplevel)" +cd "$PROJECT_ROOT" +source "./scripts/lib.sh" + +echo "--- Running go test" +export FORCE_COLOR=true + +test_args=( + -v + -failfast + "${TEST_ARGS:-}" +) + +REPORTDIR="/tmp/testreports" +mkdir -p "$REPORTDIR" +TESTREPORT_JSON="$REPORTDIR/test_go.json" +TESTREPORT_XML="$REPORTDIR/test_go.xml" +COVERAGE="$REPORTDIR/test_go.coverage" + +test_args+=( + "-covermode=set" + "-coverprofile=$COVERAGE" +) + +# Allow failures to ensure that we can upload coverage +set +e + +run_trace false gotestsum \ + --debug \ + --jsonfile="$TESTREPORT_JSON" \ + --junitfile="$TESTREPORT_XML" \ + --hide-summary=skipped \ + --packages="./..." \ + -- "${test_args[@]}" +test_status=$? + +# The following steps should never fail, and if they do, +# we want to know about it, so fail the build +set -e + +threshold="5s" +echo "--- ಠ_ಠ The following tests took longer than $threshold to complete:" +run_trace false gotestsum tool slowest \ + --jsonfile="$TESTREPORT_JSON" \ + --threshold="$threshold" + +# From time to time, Coveralls seems to have an issue on their end, so +# make a best-effort attempt to upload coverage but ignore failures +set +e +echo "--- Uploading test coverage report to Coveralls..." +run_trace false goveralls -service=github -coverprofile="$COVERAGE" +set -e + +exit $test_status