From edfe998dd3bfa95f29f50a945bb25e78f8955a77 Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Wed, 13 May 2026 11:23:09 +0200 Subject: [PATCH 1/3] chore: add Makefile with gaze CRAP monitoring targets Add Makefile inspired by complyctl with testing, code quality, and CRAP load monitoring targets. Includes ensure-gaze, crapload, crapload-baseline, and crapload-check for local development and CI consistency. Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- Makefile | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f8d5a5 --- /dev/null +++ b/Makefile @@ -0,0 +1,108 @@ +##@ Testing + +test-unit: ## run unit tests with coverage + go test -race -v -coverprofile=coverage.out ./... +.PHONY: test-unit + +format: ## format Go source + go fmt ./... +.PHONY: format + +vet: ## run go vet + go vet ./... +.PHONY: vet + +lint: ## run yamllint on peribolos.yaml + yamllint peribolos.yaml +.PHONY: lint + +sanity: vendor format vet lint ## ensure code is ready for commit + git diff --exit-code +.PHONY: sanity + +##@ Environment + +vendor: ## go mod sync + go mod tidy + go mod verify + go mod vendor +.PHONY: vendor + +clean: ## remove generated files + rm -f coverage.out +.PHONY: clean + +##@ CRAP Load Monitoring + +GAZE_VERSION ?= latest +GAZE_BASELINE := .gaze/baseline.json +GAZE_COVERPROFILE := coverage.out +GAZE_NEW_FUNC_THRESHOLD ?= 30 + +ensure-gaze: ## install gaze if not present + @command -v gaze >/dev/null 2>&1 || \ + (echo "Installing gaze..." && go install github.com/unbound-force/gaze/cmd/gaze@$(GAZE_VERSION)) +.PHONY: ensure-gaze + +crapload: ensure-gaze test-unit ## run CRAP and GazeCRAP analysis (human-readable) + gaze crap --format=text --coverprofile=$(GAZE_COVERPROFILE) ./... +.PHONY: crapload + +crapload-baseline: ensure-gaze test-unit ## generate baseline thresholds in .gaze/baseline.json + @mkdir -p .gaze + @REPO_ROOT=$$(pwd); \ + gaze crap --format=json --coverprofile=$(GAZE_COVERPROFILE) ./... | \ + jq --arg root "$$REPO_ROOT/" '(.scores[],.summary.worst_crap[]?,.summary.worst_gaze_crap[]?) |= (.file |= ltrimstr($$root))' > $(GAZE_BASELINE) + @echo "Baseline written to $(GAZE_BASELINE)" +.PHONY: crapload-baseline + +crapload-check: ensure-gaze test-unit ## check for CRAP regressions against baseline + @if [ ! -f $(GAZE_BASELINE) ]; then \ + echo "ERROR: Baseline file $(GAZE_BASELINE) not found. Run 'make crapload-baseline' first."; \ + exit 1; \ + fi + @REPO_ROOT=$$(pwd); \ + gaze crap --format=json --coverprofile=$(GAZE_COVERPROFILE) ./... | \ + jq --arg root "$$REPO_ROOT/" '(.scores[],.summary.worst_crap[]?,.summary.worst_gaze_crap[]?) |= (.file |= ltrimstr($$root))' > /tmp/crapload-current.json + @echo "Comparing against baseline..." + @jq -r '.scores[] | "\(.file):\(.function) \(.crap) \(.gaze_crap // 0)"' $(GAZE_BASELINE) | sort > /tmp/crapload-baseline.txt + @jq -r '.scores[] | "\(.file):\(.function) \(.crap) \(.gaze_crap // 0)"' /tmp/crapload-current.json | sort > /tmp/crapload-current.txt + @REGRESSIONS=0; \ + while IFS=' ' read -r func crap gaze_crap; do \ + baseline_crap=$$(grep -F "$$func " /tmp/crapload-baseline.txt | head -1 | awk '{print $$2}'); \ + baseline_gaze=$$(grep -F "$$func " /tmp/crapload-baseline.txt | head -1 | awk '{print $$3}'); \ + if [ -z "$$baseline_crap" ]; then \ + if [ "$$(echo "$$crap > $(GAZE_NEW_FUNC_THRESHOLD)" | bc -l)" = "1" ]; then \ + echo "NEW FUNCTION VIOLATION: $$func CRAP=$$crap (threshold=$(GAZE_NEW_FUNC_THRESHOLD))"; \ + REGRESSIONS=$$((REGRESSIONS + 1)); \ + fi; \ + else \ + if [ "$$(echo "$$crap > $$baseline_crap" | bc -l)" = "1" ]; then \ + echo "REGRESSION: $$func CRAP $$baseline_crap -> $$crap"; \ + REGRESSIONS=$$((REGRESSIONS + 1)); \ + fi; \ + if [ "$$(echo "$$gaze_crap > $$baseline_gaze" | bc -l)" = "1" ]; then \ + echo "REGRESSION: $$func GazeCRAP $$baseline_gaze -> $$gaze_crap"; \ + REGRESSIONS=$$((REGRESSIONS + 1)); \ + fi; \ + fi; \ + done < /tmp/crapload-current.txt; \ + if [ $$REGRESSIONS -gt 0 ]; then \ + echo "FAIL: $$REGRESSIONS regression(s) detected"; \ + exit 1; \ + else \ + echo "PASS: No regressions detected"; \ + fi +.PHONY: crapload-check + +##@ Help + +GREEN := \033[0;32m +TEAL := \033[0;36m +CLEAR := \033[0m + +help: ## show this help + @printf "Usage: make $(GREEN)$(CLEAR)\n" + @awk -v "green=${GREEN}" -v "teal=${TEAL}" -v "clear=${CLEAR}" -F ":.*## *" \ + '/^[a-zA-Z0-9_-]+:/{sub(/:.*/,"",$$1);printf " %s%-20s%s %s\n", green, $$1, clear, $$2} /^##@/{printf "%s%s%s\n", teal, substr($$1,5), clear}' $(MAKEFILE_LIST) +.PHONY: help From 4590697cda18523c7b07b6dc1706e645b5c44e9e Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Wed, 13 May 2026 11:23:56 +0200 Subject: [PATCH 2/3] chore: add peribolos local testing targets to Makefile Add ensure-peribolos, peribolos-dryrun, and peribolos-apply targets for local testing against the live org. Uses a token file at ~/.config/peribolos/token (defaulting to gh auth token output). Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- Makefile | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 4f8d5a5..e104219 100644 --- a/Makefile +++ b/Makefile @@ -30,8 +30,70 @@ vendor: ## go mod sync clean: ## remove generated files rm -f coverage.out + rm -f /tmp/peribolos .PHONY: clean +##@ Peribolos (local testing) + +PERIBOLOS_BIN := /tmp/peribolos +PERIBOLOS_TOKEN_PATH ?= $(HOME)/.config/peribolos/token + +ensure-peribolos: ## build peribolos binary if not present + @if [ ! -f $(PERIBOLOS_BIN) ]; then \ + echo "Building peribolos..."; \ + TMPDIR=$$(mktemp -d); \ + git clone --depth 1 https://github.com/kubernetes-sigs/prow.git "$$TMPDIR/prow"; \ + cd "$$TMPDIR/prow/cmd/peribolos" && go mod tidy && go build -o $(PERIBOLOS_BIN) .; \ + rm -rf "$$TMPDIR"; \ + echo "Peribolos built at $(PERIBOLOS_BIN)"; \ + else \ + echo "Peribolos already at $(PERIBOLOS_BIN)"; \ + fi +.PHONY: ensure-peribolos + +peribolos-dryrun: ensure-peribolos ## dry-run peribolos against the live org (no changes) + @if [ ! -f $(PERIBOLOS_TOKEN_PATH) ]; then \ + echo "Token not found at $(PERIBOLOS_TOKEN_PATH)"; \ + echo "Create it with: mkdir -p ~/.config/peribolos && gh auth token > ~/.config/peribolos/token"; \ + exit 1; \ + fi + $(PERIBOLOS_BIN) \ + --config-path peribolos.yaml \ + --fix-org \ + --fix-org-members \ + --fix-teams \ + --fix-team-members \ + --fix-repos \ + --fix-team-repos \ + --min-admins 2 \ + --require-self=false \ + --github-token-path $(PERIBOLOS_TOKEN_PATH) \ + 2>&1 | jq -r '[.severity, .time, .msg] | join(" | ")' +.PHONY: peribolos-dryrun + +peribolos-apply: ensure-peribolos ## apply peribolos config to the live org (DESTRUCTIVE) + @if [ ! -f $(PERIBOLOS_TOKEN_PATH) ]; then \ + echo "Token not found at $(PERIBOLOS_TOKEN_PATH)"; \ + echo "Create it with: mkdir -p ~/.config/peribolos && gh auth token > ~/.config/peribolos/token"; \ + exit 1; \ + fi + @echo "WARNING: This will modify the complytime GitHub org. Press Ctrl+C to abort." + @sleep 3 + $(PERIBOLOS_BIN) \ + --config-path peribolos.yaml \ + --fix-org \ + --fix-org-members \ + --fix-teams \ + --fix-team-members \ + --fix-repos \ + --fix-team-repos \ + --min-admins 2 \ + --require-self=false \ + --confirm \ + --github-token-path $(PERIBOLOS_TOKEN_PATH) \ + 2>&1 | jq -r '[.severity, .time, .msg] | join(" | ")' +.PHONY: peribolos-apply + ##@ CRAP Load Monitoring GAZE_VERSION ?= latest @@ -52,7 +114,7 @@ crapload-baseline: ensure-gaze test-unit ## generate baseline thresholds in .gaz @mkdir -p .gaze @REPO_ROOT=$$(pwd); \ gaze crap --format=json --coverprofile=$(GAZE_COVERPROFILE) ./... | \ - jq --arg root "$$REPO_ROOT/" '(.scores[],.summary.worst_crap[]?,.summary.worst_gaze_crap[]?) |= (.file |= ltrimstr($$root))' > $(GAZE_BASELINE) + jq --arg root "$$REPO_ROOT/" '(.scores // []) as $$s | .scores = [$$s[] | .file |= ltrimstr($$root)] | .summary.worst_crap = [(.summary.worst_crap // [])[] | .file |= ltrimstr($$root)] | .summary.worst_gaze_crap = [(.summary.worst_gaze_crap // [])[] | .file |= ltrimstr($$root)]' > $(GAZE_BASELINE) @echo "Baseline written to $(GAZE_BASELINE)" .PHONY: crapload-baseline @@ -63,10 +125,10 @@ crapload-check: ensure-gaze test-unit ## check for CRAP regressions against base fi @REPO_ROOT=$$(pwd); \ gaze crap --format=json --coverprofile=$(GAZE_COVERPROFILE) ./... | \ - jq --arg root "$$REPO_ROOT/" '(.scores[],.summary.worst_crap[]?,.summary.worst_gaze_crap[]?) |= (.file |= ltrimstr($$root))' > /tmp/crapload-current.json + jq --arg root "$$REPO_ROOT/" '(.scores // []) as $$s | .scores = [$$s[] | .file |= ltrimstr($$root)] | .summary.worst_crap = [(.summary.worst_crap // [])[] | .file |= ltrimstr($$root)] | .summary.worst_gaze_crap = [(.summary.worst_gaze_crap // [])[] | .file |= ltrimstr($$root)]' > /tmp/crapload-current.json @echo "Comparing against baseline..." - @jq -r '.scores[] | "\(.file):\(.function) \(.crap) \(.gaze_crap // 0)"' $(GAZE_BASELINE) | sort > /tmp/crapload-baseline.txt - @jq -r '.scores[] | "\(.file):\(.function) \(.crap) \(.gaze_crap // 0)"' /tmp/crapload-current.json | sort > /tmp/crapload-current.txt + @jq -r '(.scores // [])[] | "\(.file):\(.function) \(.crap) \(.gaze_crap // 0)"' $(GAZE_BASELINE) | sort > /tmp/crapload-baseline.txt + @jq -r '(.scores // [])[] | "\(.file):\(.function) \(.crap) \(.gaze_crap // 0)"' /tmp/crapload-current.json | sort > /tmp/crapload-current.txt @REGRESSIONS=0; \ while IFS=' ' read -r func crap gaze_crap; do \ baseline_crap=$$(grep -F "$$func " /tmp/crapload-baseline.txt | head -1 | awk '{print $$2}'); \ From 32195b9bb554b08075a8a28c7c07db55d97d9fef Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Wed, 13 May 2026 11:26:35 +0200 Subject: [PATCH 3/3] chore: generate initial gaze CRAP baseline Generated via make crapload-baseline. Empty scores since this repository has no Go application functions (only config tests). Required by the CRAP Load Analysis CI check. Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- .gaze/baseline.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .gaze/baseline.json diff --git a/.gaze/baseline.json b/.gaze/baseline.json new file mode 100644 index 0000000..3cddc25 --- /dev/null +++ b/.gaze/baseline.json @@ -0,0 +1,13 @@ +{ + "scores": [], + "summary": { + "total_functions": 0, + "avg_complexity": 0, + "avg_line_coverage": 0, + "avg_crap": 0, + "crapload": 0, + "crap_threshold": 15, + "worst_crap": [], + "worst_gaze_crap": [] + } +}