From 814cf987c428abda604e6d6258e6ae3da7eba8e9 Mon Sep 17 00:00:00 2001 From: Jayson Grace Date: Fri, 21 Jun 2024 17:39:21 -0600 Subject: [PATCH] Added pre-commit hooks for Go tests and linting. **Added:** - `.hooks/run-go-tests.sh` for running Go tests. - Pre-commit hook for running Go unit tests in `.pre-commit-config.yaml`. - Pre-commit hook for running `go vet` in `.pre-commit-config.yaml`. - Pre-commit hook for checking Go licenses in `.pre-commit-config.yaml`. --- .hooks/go-copyright.sh | 2 +- .hooks/go-licenses.sh | 43 +++++++++++++++++++ .hooks/go-vet.sh | 11 +++++ .hooks/run-go-tests.sh | 91 +++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 32 +++++++++++++++ go.mod | 2 +- 6 files changed, 179 insertions(+), 2 deletions(-) create mode 100755 .hooks/go-licenses.sh create mode 100755 .hooks/go-vet.sh create mode 100755 .hooks/run-go-tests.sh diff --git a/.hooks/go-copyright.sh b/.hooks/go-copyright.sh index 99ebab2b..cded4ed5 100755 --- a/.hooks/go-copyright.sh +++ b/.hooks/go-copyright.sh @@ -3,7 +3,7 @@ set -ex copyright_header='/* -Copyright © 2023-present, Meta Platforms, Inc. and affiliates +Copyright © 2024-present, Meta Platforms, Inc. and affiliates Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/.hooks/go-licenses.sh b/.hooks/go-licenses.sh new file mode 100755 index 00000000..030e62bf --- /dev/null +++ b/.hooks/go-licenses.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Function to check if go mod vendor should run or not +run_vendor() { + echo "Running go mod vendor..." + go mod vendor +} + +# Function to check licenses +check_licenses() { + action=$1 + + go install github.com/google/go-licenses@latest + + # Decide action based on input + if [[ $action == "check_forbidden" ]]; then + echo "Checking for forbidden licenses..." + output=$(go-licenses check ./... 2> /dev/null) + if [[ "${output}" == *"ERROR: forbidden license found"* ]]; then + echo "Forbidden licenses found. Please remove them." + exit 1 + else + echo "No forbidden licenses found." + fi + elif [[ $action == "output_csv" ]]; then + echo "Outputting licenses to csv..." + status=go-licenses csv ./... 2> /dev/null + elif [[ $action == "vendor" ]]; then + echo "Vendoring dependencies..." + run_vendor + fi +} + +# Ensure input is provided +if [[ $# -lt 1 ]]; then + echo "Incorrect number of arguments." + echo "Usage: $0 " + echo "Example: $0 check_forbidden" + exit 1 +fi + +# Run checks +check_licenses "${1}" diff --git a/.hooks/go-vet.sh b/.hooks/go-vet.sh new file mode 100755 index 00000000..1e877653 --- /dev/null +++ b/.hooks/go-vet.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +pkgs=$(go list ./...) + +for pkg in $pkgs; do + dir="$(basename "$pkg")/" + if [[ "${dir}" != .*/ ]]; then + go vet "${pkg}" + fi +done diff --git a/.hooks/run-go-tests.sh b/.hooks/run-go-tests.sh new file mode 100755 index 00000000..325f0ae5 --- /dev/null +++ b/.hooks/run-go-tests.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +set -e + +TESTS_TO_RUN=$1 +PROJECT=TTPFORGE +RETURN_CODE=0 + +TIMESTAMP=$(date +"%Y%m%d%H%M%S") +LOGFILE="/tmp/$PROJECT-unit-test-results-$TIMESTAMP.log" +MODULE_ROOT=$(go list -m -f "{{.Dir}}") + +if [[ -z "${TESTS_TO_RUN}" ]]; then + echo "No tests input" | tee -a "$LOGFILE" + echo "Example - Run all shorter collection of tests: bash run-go-tests.sh short" | tee -a "$LOGFILE" + echo "Example - Run all tests: bash run-go-tests.sh all" | tee -a "$LOGFILE" + echo "Example - Run coverage for a specific version: bash run-go-tests.sh coverage" | tee -a "$LOGFILE" + echo "Example - Run tests for modified files: bash run-go-tests.sh modified" | tee -a "$LOGFILE" + exit 1 +fi + +run_tests() { + local coverage_file=$1 + repo_root=$(git rev-parse --show-toplevel 2> /dev/null) || exit + pushd "${repo_root}" || exit + echo "Logging output to ${LOGFILE}" | tee -a "$LOGFILE" + echo "Run the following command to see the output in real time:" | tee -a "$LOGFILE" + echo "tail -f ${LOGFILE}" | tee -a "$LOGFILE" + echo "Running tests..." | tee -a "$LOGFILE" + + # Check if go.mod and go.sum exist + if [[ -f "go.mod" && -f "go.sum" ]]; then + # Check if `go mod tidy` is necessary + MOD_TMP=$(mktemp) + SUM_TMP=$(mktemp) + cp go.mod "$MOD_TMP" + cp go.sum "$SUM_TMP" + go mod tidy + if ! cmp -s go.mod "$MOD_TMP" || ! cmp -s go.sum "$SUM_TMP"; then + echo "Running 'go mod tidy' to clean up module dependencies..." | tee -a "$LOGFILE" + go mod tidy 2>&1 | tee -a "$LOGFILE" + fi + rm "$MOD_TMP" "$SUM_TMP" + fi + + if [[ "${TESTS_TO_RUN}" == 'coverage' ]]; then + go test -v -race -failfast -tags=integration -coverprofile="${coverage_file}" ./... 2>&1 | tee -a "$LOGFILE" + elif [[ "${TESTS_TO_RUN}" == 'all' ]]; then + go test -v -race -failfast ./... 2>&1 | tee -a "$LOGFILE" + elif [[ "${TESTS_TO_RUN}" == 'short' ]] && [[ "${GITHUB_ACTIONS}" != "true" ]]; then + go test -v -short -failfast -race ./... 2>&1 | tee -a "$LOGFILE" + elif [[ "${TESTS_TO_RUN}" == 'modified' ]]; then + # Run tests for modified files + local modified_files + IFS=$'\n' read -r -a modified_files <<< "$(git diff --name-only --cached | grep '\.go$')" + + local pkg_dirs=() + + for file in "${modified_files[@]}"; do + local pkg_dir + pkg_dir=$(dirname "$file") + pkg_dir=${pkg_dir#"$MODULE_ROOT/"} + pkg_dirs+=("$pkg_dir") + done + + # Remove duplicate package directories + IFS=$'\n' read -r -a pkg_dirs <<< "$(sort -u <<< "${pkg_dirs[*]}")" + unset IFS + + for dir in "${pkg_dirs[@]}"; do + go test -v -race -failfast "./$dir/..." 2>&1 | tee -a "$LOGFILE" + done + else + if [[ "${GITHUB_ACTIONS}" != 'true' ]]; then + go test -v -failfast -race "./.../${TESTS_TO_RUN}" 2>&1 | tee -a "$LOGFILE" + fi + fi + + RETURN_CODE=$? +} + +if [[ "${TESTS_TO_RUN}" == 'coverage' ]]; then + run_tests 'coverage-all.out' +else + run_tests +fi + +if [[ "${RETURN_CODE}" -ne 0 ]]; then + echo "unit tests failed" | tee -a "$LOGFILE" + exit 1 +fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d93ce1f3..9a2958ec 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,6 +28,38 @@ repos: language: script files: go.mod + - id: go-unit-tests + name: Go unit tests + language: script + entry: .hooks/run-go-tests.sh modified + files: '\.go$' + pass_filenames: true + + - id: go-vet + name: Run go vet + language: script + entry: .hooks/go-vet.sh + files: '\.go$' + always_run: true + pass_filenames: true + require_serial: true + log_file: /tmp/go-vet.log + + - id: go-licenses + name: Run go-licenses + language: script + entry: .hooks/go-licenses.sh check_forbidden + + - id: go-vet + name: Run go vet + language: script + entry: .hooks/go-vet.sh + files: '\.go$' + always_run: true + pass_filenames: true + require_serial: true + log_file: /tmp/go-vet.log + - id: go-copyright name: Ensure all go files have the copyright header language: script diff --git a/go.mod b/go.mod index e9ea56f2..428fa4a9 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/google/go-cmp v0.6.0 + github.com/google/uuid v1.6.0 github.com/otiai10/copy v1.14.0 github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.0 @@ -18,7 +19,6 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect