Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .github/workflows/pre-commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,26 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}

- name: Install go module dependencies
- name: Install Go tools
run: |
# Install shfmt
go install mvdan.cc/sh/v3/cmd/shfmt@latest

# Install goimports
go install golang.org/x/tools/cmd/goimports@latest

# Install gocyclo
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest

# Install golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$(go env GOPATH)/bin"

# Install gocritic
go install github.com/go-critic/go-critic/cmd/gocritic@latest

# Add Go bin directory to PATH
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"

- name: Setup go-task
run: |
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin v${{ env.TASK_VERSION }}
Expand Down
53 changes: 53 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
name: Tests
on:
merge_group:
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened
push:
branches:
- main
workflow_dispatch:

concurrency:
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.ref }}

env:
GO_VERSION: "1.26.1"

permissions:
actions: read
checks: write
contents: read
pull-requests: write

jobs:
tests:
name: Run Go tests and determine code coverage
runs-on: ubuntu-latest
steps:
- name: Checkout git repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true

- name: Generate the coverage output
run: |
bash .hooks/run-go-tests.sh coverage

- name: Upload coverage artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: coverage-report
path: cli/coverage-all.out
retention-days: 14
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ __pycache__/
.task/

# Build artifacts
dreadgoad
ansible/roles/adcs_templates/files/ADCSTemplate.zip
ansible/roles/vulns_adcs_templates/files/ADCSTemplate.zip

Expand Down
9 changes: 9 additions & 0 deletions .hooks/go-no-replacement.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

REPO_ROOT=$(git rev-parse --show-toplevel 2> /dev/null)
GO_MOD="${REPO_ROOT}/cli/go.mod"

if grep -q "^replace " "${GO_MOD}" 2> /dev/null; then
echo "ERROR: Don't commit a replacement in go.mod!"
exit 1
fi
14 changes: 14 additions & 0 deletions .hooks/go-vet.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
set -e

REPO_ROOT=$(git rev-parse --show-toplevel 2> /dev/null)
cd "${REPO_ROOT}/cli"

pkgs=$(go list ./...)

for pkg in $pkgs; do
dir="$(basename "$pkg")/"
if [[ "${dir}" != .*/ ]]; then
go vet "${pkg}"
fi
done
105 changes: 105 additions & 0 deletions .hooks/run-go-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/bin/bash

set -euo pipefail

TESTS_TO_RUN=${1:-}
RETURN_CODE=0
GITHUB_ACTIONS=${GITHUB_ACTIONS:-}
PROJECT_NAME=$(basename "$(git rev-parse --show-toplevel 2> /dev/null)" || echo "dreadgoad")

TIMESTAMP=$(date +"%Y%m%d%H%M%S")
LOGFILE="/tmp/${PROJECT_NAME}-unit-test-results-$TIMESTAMP.log"

# Go code lives in the cli/ subdirectory
REPO_ROOT=$(git rev-parse --show-toplevel 2> /dev/null)
GO_DIR="${REPO_ROOT}/cli"

if [[ -z "${TESTS_TO_RUN}" ]]; then
echo "No tests input" | tee -a "$LOGFILE"
echo "Example - Run all shorter collection of tests: bash .hooks/run-go-tests.sh short" | tee -a "$LOGFILE"
echo "Example - Run all tests: bash .hooks/run-go-tests.sh all" | tee -a "$LOGFILE"
echo "Example - Run coverage for a specific version: bash .hooks/run-go-tests.sh coverage" | tee -a "$LOGFILE"
echo "Example - Run tests for modified files: bash .hooks/run-go-tests.sh modified" | tee -a "$LOGFILE"
exit 1
fi

run_tests() {
local coverage_file=${1:-}
pushd "${GO_DIR}" > /dev/null || exit
echo "Logging output to ${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
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 -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
local modified_files=()
while IFS= read -r file; do
[[ -n "$file" ]] && modified_files+=("$file")
done < <(git diff --name-only --cached | grep '^cli/.*\.go$' | sed 's|^cli/||' || true)

if [[ ${#modified_files[@]} -eq 0 ]]; then
echo "No modified Go files found to test" | tee -a "$LOGFILE"
popd > /dev/null
return 0
fi

local pkg_dirs=()
for file in "${modified_files[@]}"; do
local pkg_dir
pkg_dir=$(dirname "$file")
pkg_dirs+=("$pkg_dir")
done

# Remove duplicate package directories
IFS=$'\n' read -r -a pkg_dirs <<< "$(printf '%s\n' "${pkg_dirs[@]}" | sort -u)"
unset IFS

for dir in "${pkg_dirs[@]}"; do
if [[ -n "$dir" ]]; then
local pkg_list
pkg_list=$(go list "./$dir/..." 2>&1)
if [[ -n "$pkg_list" ]] && [[ ! "$pkg_list" =~ "matched no packages" ]]; then
go test -v -short -race -failfast "./$dir/..." 2>&1 | tee -a "$LOGFILE"
else
echo "Skipping $dir (no packages match current platform/build constraints)" | tee -a "$LOGFILE"
fi
fi
done
else
if [[ "${GITHUB_ACTIONS}" != 'true' ]]; then
go test -v -failfast -race "./.../${TESTS_TO_RUN}" 2>&1 | tee -a "$LOGFILE"
fi
fi

RETURN_CODE=$?
popd > /dev/null
}

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
72 changes: 72 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,75 @@ repos:
always_run: false
files: ^ansible/(roles/|plugins/|playbooks/).*
additional_dependencies: []

- id: go-fmt
name: Run gofmt
entry: bash -c 'cd cli && gofmt -l -w .'
language: system
files: '\.go$'
pass_filenames: false

- id: go-imports
name: Run goimports
entry: bash -c 'cd cli && goimports -l -w .'
language: system
files: '\.go$'
pass_filenames: false

- id: go-cyclo
name: Run gocyclo
entry: bash -c 'cd cli && gocyclo -over 15 .'
language: system
files: '\.go$'
pass_filenames: false

- id: golangci-lint
name: Run golangci-lint
entry: bash -c 'cd cli && golangci-lint run --timeout=5m --enable=unused ./...'
language: system
files: '\.go$'
pass_filenames: false

- id: go-critic
name: Run go-critic
entry: bash -c 'cd cli && gocritic check ./...'
language: system
files: '\.go$'
pass_filenames: false

- id: go-build
name: Run go build
entry: bash -c 'cd cli && go build ./...'
language: system
files: '\.go$'
pass_filenames: false

- id: go-mod-tidy
name: Run go mod tidy
entry: bash -c 'cd cli && go mod tidy'
language: system
files: '(go\.mod|go\.sum)$'
pass_filenames: false

- id: go-no-replacement
name: Avoid committing a go module replacement
entry: .hooks/go-no-replacement.sh
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
Loading
Loading