diff --git a/Makefile b/Makefile index 76cf47b..e2b90bb 100644 --- a/Makefile +++ b/Makefile @@ -1,182 +1,219 @@ -.PHONY: all build prod prod-with-native native-release native-build native-debug native-test native-smoke native-clean dev clean clean-docs test test-unit test-integration test-e2e test-coverage lint install configlint worker-configlint ci-local docs docs-setup docs-check-port docs-stop docs-build docs-build-prod benchmark benchmark-local benchmark-native artifacts clean-benchmarks clean-all clean-aggressive status size load-test chaos-test profile-load profile-load-norate profile-ws-queue profile-tools detect-regressions detect-regressions-native tech-excellence docker-build dev-smoke prod-smoke native-smoke self-cleanup test-full test-auth deploy-up deploy-down deploy-status deploy-clean dev-up dev-down dev-status dev-logs prod-up prod-down prod-status prod-logs security-scan gosec govulncheck check-unsafe security-audit test-security check-sqlbuild verify-schema test-schema-validation lint-custom verify-audit verify-audit-chain verify-all install-property-test-deps install-mutation-test-deps install-security-scan-deps install-scorecard install-verify-deps verify-quick verify-full -OK = ✓ +# ============================================================================= +# FetchML Build System - Streamlined +# ============================================================================= + +.PHONY: all build prod prod-with-native dev cross-platform build-cli \ + native-build native-release native-debug native-test native-smoke native-clean \ + clean clean-release prepare-release verify-build \ + test test-unit test-integration test-e2e test-coverage \ + test-infra-up test-infra-down \ + lint configlint worker-configlint ci-local ci-checks \ + verify-quick verify-all verify-schema test-schema-validation \ + lint-custom verify-audit verify-audit-chain install-tools \ + security-scan gosec govulncheck check-unsafe security-audit \ + openapi-generate openapi-generate-server openapi-validate \ + openapi-check-ci openapi-check-implementation \ + openapi-generate-clients \ + docs docs-setup docs-build docs-build-prod docs-generate docs-serve \ + benchmark benchmark-local benchmark-native benchmark-compare \ + load-test chaos-test profile-load profile-load-norate profile-ws-queue profile-tools detect-regressions detect-regressions-native \ + clean-benchmarks status size complete-suite \ + dev-up dev-down dev-logs dev-restart \ + staging-up staging-down staging-logs staging-restart staging-status \ + homelab-secure-up homelab-secure-down \ + prod-up prod-down prod-logs prod-restart prod-status \ + status-all clean-deploy rollback-staging rollback-prod deploy-health-check \ + install docker-build dev-smoke prod-smoke test-auth help + +# ----------------------------------------------------------------------------- +# Variables +# ----------------------------------------------------------------------------- + +OK := ✓ DOCS_PORT ?= 1313 DOCS_BIND ?= 127.0.0.1 DOCS_BASEURL ?= / -DOCS_PROD_BASEURL ?= $(DOCS_BASEURL) -# Default target +BUILD_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo dev) +BUILD_TIME := $(shell date -u +%Y%m%d.%H%M%S) +LDFLAGS := -X main.BuildHash=$(BUILD_HASH) -X main.BuildTime=$(BUILD_TIME) +LDFLAGS_PROD := -s -w +NPROC := $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) +DC := docker compose +TEST_COMPOSE := deployments/docker-compose.test.yml + +define test_summary + @passed=$$(grep -c "^--- PASS:" $(1) 2>/dev/null || echo 0); \ + failed=$$(grep -c "^--- FAIL:" $(1) 2>/dev/null || echo 0); \ + skipped=$$(grep -c "^--- SKIP:" $(1) 2>/dev/null || echo 0); \ + echo " Passed: $$passed Failed: $$failed Skipped: $$skipped"; \ + if [ "$$failed" -gt 0 ]; then exit 1; fi +endef + +define ensure_tool + @if ! command -v $(1) >/dev/null 2>&1; then \ + echo "Installing $(1)..."; \ + go install $(2); \ + fi +endef + +# ----------------------------------------------------------------------------- +# Build +# ----------------------------------------------------------------------------- + all: build -# Build all components (Go binaries + optimized CLI) -build: native-build - go build -ldflags="-X main.BuildHash=$(shell git rev-parse --short HEAD) -X main.BuildTime=$(shell date -u +%Y%m%d.%H%M%S)" -o bin/api-server ./cmd/api-server/main.go - go build -ldflags="-X main.BuildHash=$(shell git rev-parse --short HEAD) -X main.BuildTime=$(shell date -u +%Y%m%d.%H%M%S)" -o bin/worker ./cmd/worker/worker_server.go - go build -ldflags="-X main.BuildHash=$(shell git rev-parse --short HEAD) -X main.BuildTime=$(shell date -u +%Y%m%d.%H%M%S)" -o bin/data_manager ./cmd/data_manager - go build -ldflags="-X main.BuildHash=$(shell git rev-parse --short HEAD) -X main.BuildTime=$(shell date -u +%Y%m%d.%H%M%S)" -o bin/user_manager ./cmd/user_manager - go build -ldflags="-X main.BuildHash=$(shell git rev-parse --short HEAD) -X main.BuildTime=$(shell date -u +%Y%m%d.%H%M%S)" -o bin/tui ./cmd/tui +build: native-build openapi-generate-server + @mkdir -p bin/server bin/cli bin/native + go build -ldflags="$(LDFLAGS)" -o bin/server/api-server ./cmd/api-server/main.go + go build -ldflags="$(LDFLAGS)" -o bin/server/worker ./cmd/worker/worker_server.go + go build -ldflags="$(LDFLAGS)" -o bin/server/data_manager ./cmd/data_manager + go build -ldflags="$(LDFLAGS)" -o bin/server/user_manager ./cmd/user_manager + go build -ldflags="$(LDFLAGS)" -o bin/server/tui ./cmd/tui + @cp native/build/lib*.so native/build/lib*.dylib bin/native/ 2>/dev/null || true $(MAKE) -C ./cli all - @echo "${OK} All components built" + @cp cli/zig-out/bin/ml bin/cli/ + @echo "$(OK) All components built" -# Verify build reproducibility (build twice, compare hashes) -verify-build: - @echo "Building first time..." - @make build - @shasum -a 256 bin/* > /tmp/build_hash_1.txt 2>/dev/null || true - @echo "Building second time..." - @make build - @shasum -a 256 bin/* > /tmp/build_hash_2.txt 2>/dev/null || true - @echo "Comparing hashes..." - @if diff /tmp/build_hash_1.txt /tmp/build_hash_2.txt > /dev/null; then \ - echo "${OK} Build is reproducible - hashes match"; \ - else \ - echo "Build differs (expected for non-reproducible builds with timestamps)"; \ - diff /tmp/build_hash_1.txt /tmp/build_hash_2.txt || true; \ - fi +prod: + @mkdir -p bin/server bin/cli + go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/api-server cmd/api-server/main.go + go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/worker cmd/worker/worker_server.go + go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/data_manager ./cmd/data_manager + go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/user_manager ./cmd/user_manager + go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/tui ./cmd/tui + $(MAKE) -C cli prod + @cp cli/zig-out/bin/ml bin/cli/ + @echo "$(OK) Production binaries built" + +dev: + @mkdir -p bin/server bin/cli + go build -buildvcs=false -o bin/server/api-server cmd/api-server/main.go + go build -buildvcs=false -o bin/server/worker cmd/worker/worker_server.go + go build -buildvcs=false -o bin/server/data_manager ./cmd/data_manager + go build -buildvcs=false -o bin/server/user_manager ./cmd/user_manager + go build -buildvcs=false -o bin/server/tui ./cmd/tui + $(MAKE) -C cli dev + @cp cli/zig-out/bin/ml bin/cli/ + @echo "$(OK) Development binaries built" + +native-build: + @mkdir -p native/build + @cd native/build && cmake .. -DCMAKE_BUILD_TYPE=Release && make -j$(NPROC) + @echo "$(OK) Native libraries built" -# Build native C++ libraries for production (optimized, stripped) native-release: @mkdir -p native/build @cd native/build && cmake .. -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_FLAGS="-O3 -DNDEBUG -fomit-frame-pointer" \ - -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -fomit-frame-pointer" && \ - make -j$(shell nproc 2>/dev/null || sysctl -n hw.ncpu) - @echo "${OK} Native libraries built (release)" + -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -fomit-frame-pointer" && make -j$(NPROC) + @echo "$(OK) Native libraries built (release)" -# Build Go binaries with native library support -prod-with-native: native-release - @mkdir -p bin - @cp native/build/lib*.so native/build/lib*.dylib bin/ 2>/dev/null || true - @go build -ldflags="-s -w" -o bin/api-server ./cmd/api-server/main.go - @go build -ldflags="-s -w" -o bin/worker ./cmd/worker/worker_server.go - @echo "${OK} Production binaries built (with native libs)" - @echo "Copy native libraries from bin/ alongside your binaries" - -# Build native C++ libraries for performance optimization -native-build: - @mkdir -p native/build - @cd native/build && cmake .. -DCMAKE_BUILD_TYPE=Release && make -j$(shell nproc 2>/dev/null || sysctl -n hw.ncpu) - @echo "${OK} Native libraries built" - -# Build native libraries with AddressSanitizer (for testing) native-debug: @mkdir -p native/build - @cd native/build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON && make -j$(shell nproc 2>/dev/null || sysctl -n hw.ncpu) - @echo "${OK} Native libraries built (debug mode)" + @cd native/build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON && make -j$(NPROC) + @echo "$(OK) Native libraries built (debug + ASan)" -# Clean native build artifacts -native-clean: - @rm -rf native/build - @echo "${OK} Native build cleaned" - -# Run native library tests native-test: native-build @cd native/build && ctest --output-on-failure - @echo "${OK} Native tests passed" + @echo "$(OK) Native tests passed" -# Run native libraries smoke test (builds + C++ tests + Go integration) native-smoke: @bash ./scripts/dev/smoke-test.sh --native - @echo "${OK} Native smoke test passed" + @echo "$(OK) Native smoke test passed" -# Build production-optimized binaries -prod: - go build -ldflags="-s -w" -o bin/api-server cmd/api-server/main.go - go build -ldflags="-s -w" -o bin/worker cmd/worker/worker_server.go - go build -ldflags="-s -w" -o bin/data_manager ./cmd/data_manager - go build -ldflags="-s -w" -o bin/user_manager ./cmd/user_manager - go build -ldflags="-s -w" -o bin/tui ./cmd/tui - $(MAKE) -C cli prod - @echo "${OK} Production binaries built" +native-clean: + @rm -rf native/build + @echo "$(OK) Native build cleaned" + +prod-with-native: native-release + @mkdir -p bin/server bin/native + @cp native/build/lib*.so native/build/lib*.dylib bin/native/ 2>/dev/null || true + go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/api-server ./cmd/api-server/main.go + go build -ldflags="$(LDFLAGS_PROD)" -o bin/server/worker ./cmd/worker/worker_server.go + @echo "$(OK) Production binaries built (with native libs)" cross-platform: - @rm -rf dist - @mkdir -p dist - @set -e; \ - LDFLAGS='-s -w -buildid='; \ - for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64; do \ - goos=$${target%/*}; \ - goarch=$${target#*/}; \ - ext=''; \ - if [ "$$goos" = "windows" ]; then ext='.exe'; fi; \ - echo "Building $$goos/$$goarch..."; \ - CGO_ENABLED=0 GOOS=$$goos GOARCH=$$goarch go build -trimpath -buildvcs=false -ldflags="$$LDFLAGS" -o dist/fetch_ml_api-server_$${goos}_$${goarch}$${ext} cmd/api-server/main.go; \ - CGO_ENABLED=0 GOOS=$$goos GOARCH=$$goarch go build -trimpath -buildvcs=false -ldflags="$$LDFLAGS" -o dist/fetch_ml_worker_$${goos}_$${goarch}$${ext} cmd/worker/worker_server.go; \ - CGO_ENABLED=0 GOOS=$$goos GOARCH=$$goarch go build -trimpath -buildvcs=false -ldflags="$$LDFLAGS" -o dist/fetch_ml_data_manager_$${goos}_$${goarch}$${ext} ./cmd/data_manager; \ - CGO_ENABLED=0 GOOS=$$goos GOARCH=$$goarch go build -trimpath -buildvcs=false -ldflags="$$LDFLAGS" -o dist/fetch_ml_user_manager_$${goos}_$${goarch}$${ext} ./cmd/user_manager; \ - CGO_ENABLED=0 GOOS=$$goos GOARCH=$$goarch go build -trimpath -buildvcs=false -ldflags="$$LDFLAGS" -o dist/fetch_ml_tui_$${goos}_$${goarch}$${ext} ./cmd/tui; \ - done - @echo "${OK} Cross-platform binaries built in dist/" + @rm -rf dist && mkdir -p dist + @echo "Building Linux x86_64 static binaries..." + @for bin in \ + "fetchml_api-server:cmd/api-server/main.go" \ + "fetchml_worker:cmd/worker/worker_server.go" \ + "fetchml_data_manager:./cmd/data_manager" \ + "fetchml_user_manager:./cmd/user_manager" \ + "fetchml_tui:./cmd/tui"; do \ + name=$${bin%%:*}; src=$${bin##*:}; \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false \ + -ldflags="-s -w -buildid=" -o dist/$${name}_linux_amd64 $$src; \ + done + @echo "$(OK) Cross-platform binaries in dist/" -# Development build (faster compilation) -dev: - go build -buildvcs=false -o bin/api-server cmd/api-server/main.go - go build -buildvcs=false -o bin/worker cmd/worker/worker_server.go - go build -buildvcs=false -o bin/data_manager ./cmd/data_manager - go build -buildvcs=false -o bin/user_manager ./cmd/user_manager - go build -buildvcs=false -o bin/tui ./cmd/tui - $(MAKE) -C cli dev - @echo "${OK} Development binaries built" +build-cli: + $(MAKE) -C cli all + @echo "$(OK) CLI built" -# Clean build artifacts (Go + Zig + test outputs) -clean: - rm -rf bin/ coverage/ tests/bin/ - rm -rf cli/zig-out/ cli/.zig-cache/ .zig-cache/ - rm -rf dist/ - go clean - @echo "${OK} Cleaned" +verify-build: + @$(MAKE) build && shasum -a 256 bin/server/* bin/cli/* > /tmp/build_hash_1.txt 2>/dev/null || true + @$(MAKE) build && shasum -a 256 bin/server/* bin/cli/* > /tmp/build_hash_2.txt 2>/dev/null || true + @diff /tmp/build_hash_1.txt /tmp/build_hash_2.txt > /dev/null \ + && echo "$(OK) Build is reproducible" \ + || (echo "Build differs (expected with timestamps):"; diff /tmp/build_hash_1.txt /tmp/build_hash_2.txt || true) -# Thorough cleanup for release -clean-release: clean - @echo "Release cleanup..." - rm -rf bin/ cli/zig-out/ cli/.zig-cache/ - rm -rf dist/ coverage/ tests/bin/ native/build/ - go clean -cache -testcache - find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true - find . -name "*.pyc" -delete 2>/dev/null || true - @./scripts/release/cleanup.sh testdata 2>/dev/null || true - @./scripts/release/cleanup.sh logs 2>/dev/null || true - @./scripts/release/cleanup.sh state 2>/dev/null || true - @echo "${OK} Release cleanup complete" +# ----------------------------------------------------------------------------- +# Tests +# ----------------------------------------------------------------------------- -# Run release verification checks -release-check: - @./scripts/release/verify.sh - -# Full release preparation -prepare-release: clean-release release-check - @echo "${OK} Release preparation complete" - -clean-docs: - rm -rf docs/_site/ - @echo "${OK} Cleaned docs" - -# Run all tests (unit, integration, e2e) with docker-compose for external services -test: - @echo "Starting test infrastructure..." - @docker-compose -f tests/e2e/docker-compose.logs-debug.yml down 2>/dev/null || true - @docker-compose -f tests/e2e/docker-compose.logs-debug.yml up -d redis 2>/dev/null || true - @sleep 3 - @echo "Running tests..." - @go test ./tests/unit/... ./tests/integration/... ./tests/e2e/... 2>&1 | grep -v "redis: connection pool" || true - @docker-compose -f tests/e2e/docker-compose.logs-debug.yml down 2>/dev/null || true +test: test-infra-up + @[ -f bin/cli/ml ] || $(MAKE) build-cli + @go test -v ./tests/unit/... ./tests/integration/... ./tests/e2e/... 2>&1 | grep -v "redis: connection pool" | tee /tmp/test-all.txt || true + @echo "\n=== Test Summary ===" + $(call test_summary,/tmp/test-all.txt) + @$(MAKE) test-infra-down @cd cli && zig build test -# Lint Go and Zig code +test-unit: + @go test -v -short ./tests/unit/... 2>&1 | tee /tmp/test-unit.txt || true + @echo "\n=== Unit Test Summary ===" + $(call test_summary,/tmp/test-unit.txt) + @cd cli && zig build test + +test-integration: test-infra-up + @go test -v ./tests/integration/... 2>&1 | tee /tmp/test-integration.txt || true + @echo "\n=== Integration Test Summary ===" + $(call test_summary,/tmp/test-integration.txt) + @$(MAKE) test-infra-down + +test-e2e: test-infra-up + @[ -f bin/cli/ml ] || $(MAKE) build-cli + @go test -v ./tests/e2e/... 2>&1 | tee /tmp/test-e2e.txt || true + @echo "\n=== E2E Test Summary ===" + $(call test_summary,/tmp/test-e2e.txt) + @$(MAKE) test-infra-down + +test-coverage: + @mkdir -p coverage + go test -coverprofile=coverage/coverage.out ./... + go tool cover -html=coverage/coverage.out -o coverage/coverage.html + @echo "$(OK) Coverage report: coverage/coverage.html" + +test-infra-up: + @echo "Starting test infrastructure..." + @$(DC) -f $(TEST_COMPOSE) up -d + @echo "$(OK) Test infrastructure started" + +test-infra-down: + @$(DC) -f $(TEST_COMPOSE) down 2>/dev/null || true + @echo "$(OK) Test infrastructure stopped" + +# ----------------------------------------------------------------------------- +# Lint & Clean +# ----------------------------------------------------------------------------- + lint: gofmt -w ./cmd ./internal ./tests || true go vet ./... cd cli && zig fmt . - @echo "${OK} Lint completed" + @echo "$(OK) Lint complete" -# Install to system (requires sudo) -install: prod - sudo cp bin/api-server /usr/local/bin/fetchml-api - sudo cp bin/worker /usr/local/bin/fetchml-worker - sudo cp bin/tui /usr/local/bin/fetchml-tui - sudo cp cli/zig-out/bin/ml /usr/local/bin/ml - @echo "${OK} Installed" - -# Validate YAML configs against JSON schema configlint: go run ./cmd/configlint --schema configs/schema/api_server_config.yaml \ configs/api/dev.yaml \ @@ -192,601 +229,486 @@ worker-configlint: configs/workers/docker-prod.yaml \ configs/workers/homelab-secure.yaml -# Check CLI builds correctly (SQLite handled automatically by build.zig) -build-cli: - @$(MAKE) -C cli all - @echo "${OK} CLI built successfully" - -dev-smoke: - bash ./scripts/dev/smoke-test.sh dev - @echo "dev smoke: OK" - -prod-smoke: - bash ./scripts/dev/smoke-test.sh prod - @echo "prod smoke: OK" - -# Run maintainability CI checks ci-checks: @bash ./scripts/ci/checks.sh -# Run a local approximation of the CI pipeline -ci-local: ci-checks test lint configlint worker-configlint +ci-local: lint configlint worker-configlint ci-checks test @mkdir -p coverage @echo "Running queue package tests with race detector..." go test -v -race -coverprofile=coverage/queue-coverage.out ./internal/queue/... - @echo "Running coverage..." - make test-coverage + $(MAKE) test-coverage -# Docker image build (no direct docker-compose run; use deploy-* targets instead) -docker-build: - docker build -f build/docker/simple.Dockerfile -t fetchml:latest . - @echo "${OK} Docker image built" +clean: + rm -rf bin/ coverage/ tests/bin/ dist/ + rm -rf cli/zig-out/ cli/.zig-cache/ .zig-cache/ + go clean + @echo "$(OK) Cleaned" -# Enhanced test targets -test-unit: - go test -v -short ./tests/unit/... - cd cli && zig build test +clean-release: clean + rm -rf native/build/ + go clean -cache -testcache + find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true + find . -name "*.pyc" -delete 2>/dev/null || true + @./scripts/release/cleanup.sh testdata 2>/dev/null || true + @./scripts/release/cleanup.sh logs 2>/dev/null || true + @./scripts/release/cleanup.sh state 2>/dev/null || true + @echo "$(OK) Release cleanup complete" -test-integration: - go test -v ./tests/integration/... ./tests - cd cli && zig build test +prepare-release: clean-release + @./scripts/release/verify.sh + @echo "$(OK) Release preparation complete" -test-e2e: - go test -v ./tests/e2e/... +# ----------------------------------------------------------------------------- +# Verification +# ----------------------------------------------------------------------------- -test-coverage: - @mkdir -p coverage - go test -coverprofile=coverage/coverage.out ./... - go tool cover -html=coverage/coverage.out -o coverage/coverage.html - @echo "${OK} Coverage report: coverage/coverage.html" +verify-schema: + @go test ./internal/manifest/... -run TestSchemaUnchanged -v + @echo "$(OK) Schema validation passed" -# Documentation setup -docs-setup: - @echo "Setting up Hugo documentation..." - @if ! command -v hugo >/dev/null 2>&1; then \ - echo "Hugo not found. Please install it manually:"; \ - echo " macOS: brew install hugo"; \ - echo " Linux: See https://gohugo.io/installation/"; \ - exit 1; \ +test-schema-validation: + @go test ./internal/manifest/... -run TestSchemaValidatesExampleManifest -v + @go test ./internal/manifest/... -run TestSchemaRejectsInvalidManifest -v + @echo "$(OK) Schema tests passed" + +lint-custom: + @mkdir -p bin/tools + go build -ldflags="-s -w" -o bin/tools/fetchml-vet ./tools/fetchml-vet/cmd/fetchml-vet/ + go vet -vettool=bin/tools/fetchml-vet ./internal/... ./cmd/... 2>/dev/null || true + @echo "$(OK) Custom lint complete" + +verify-audit: + @go test ./tests/unit/audit/... -run TestChainVerifier -v + @echo "$(OK) Audit chain verification passed" + +verify-audit-chain: + @if [ -z "$(AUDIT_LOG_PATH)" ]; then \ + echo "Usage: make verify-audit-chain AUDIT_LOG_PATH=/path/to/audit.log"; exit 1; \ fi - @echo "Documentation setup complete!" + @mkdir -p bin/tools + go build -ldflags="-s -w" -o bin/tools/audit-verifier ./cmd/audit-verifier/ + ./bin/tools/audit-verifier -log-path=$(AUDIT_LOG_PATH) + @echo "$(OK) Audit chain integrity verified" +verify-quick: verify-schema + +verify-all: verify-schema test-schema-validation lint-custom verify-audit + @echo "$(OK) All verification checks passed" + +# ----------------------------------------------------------------------------- +# Security +# ----------------------------------------------------------------------------- + +security-scan: gosec govulncheck check-unsafe + @echo "$(OK) Security scan complete" + +gosec: + $(call ensure_tool,gosec,github.com/securego/gosec/v2/cmd/gosec@latest) + @mkdir -p reports + gosec -fmt=json -out=reports/gosec-results.json ./... 2>/dev/null || true + gosec -fmt=sarif -out=reports/gosec-results.sarif ./... 2>/dev/null || true + gosec ./... 2>/dev/null || echo "Note: gosec found issues (see reports/gosec-results.json)" + @echo "$(OK) gosec scan complete" + +govulncheck: + $(call ensure_tool,govulncheck,golang.org/x/vuln/cmd/govulncheck@latest) + govulncheck ./... + @echo "$(OK) govulncheck complete" + +check-unsafe: + @if grep -r "unsafe\." --include="*.go" ./internal ./cmd 2>/dev/null | grep -v "_test.go"; then \ + echo "WARNING: unsafe package usage found — review required"; exit 1; \ + else \ + echo "$(OK) No unsafe package usage"; \ + fi + +security-audit: security-scan + @go test -v ./tests/security/... 2>/dev/null || echo "Note: No security tests yet" + @echo "$(OK) Full security audit complete" + +# ----------------------------------------------------------------------------- +# OpenAPI +# ----------------------------------------------------------------------------- + +_ensure_oapi: + $(call ensure_tool,oapi-codegen,github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest) + +openapi-generate: _ensure_oapi + oapi-codegen -package api -generate types api/openapi.yaml > internal/api/types.go + @echo "$(OK) Generated internal/api/types.go" + +openapi-generate-server: _ensure_oapi + oapi-codegen -package api -generate types,server api/openapi.yaml > internal/api/server_gen.go + @echo "$(OK) Generated internal/api/server_gen.go" + +openapi-validate: + @command -v openapi-generator >/dev/null 2>&1 \ + && openapi-generator validate -i api/openapi.yaml 2>/dev/null \ + || echo "Note: Install openapi-generator for validation" + +openapi-check-ci: openapi-generate + @git diff --exit-code internal/api/types.go 2>/dev/null \ + || (echo "ERROR: Run 'make openapi-generate' to sync" && exit 1) + @echo "$(OK) OpenAPI types up to date" + +openapi-check-implementation: openapi-generate-server + @git diff --exit-code internal/api/server_gen.go 2>/dev/null \ + || (echo "ERROR: Run 'make openapi-generate-server' to sync" && exit 1) + @echo "$(OK) Spec and implementation in sync" + +openapi-generate-clients: openapi-generate-ts openapi-generate-python + @echo "$(OK) All client SDKs generated" + +openapi-generate-ts: + @mkdir -p sdk/typescript + @command -v npx >/dev/null 2>&1 || (echo "Note: npx not available" && exit 0) + npx @openapitools/openapi-generator-cli generate \ + -i api/openapi.yaml -g typescript-fetch -o sdk/typescript \ + --additional-properties=supportsES6=true,npmName=fetchml-client 2>/dev/null \ + || echo "Note: npm install -g @openapitools/openapi-generator-cli" + +openapi-generate-python: + @mkdir -p sdk/python + @command -v npx >/dev/null 2>&1 || (echo "Note: npx not available" && exit 0) + npx @openapitools/openapi-generator-cli generate \ + -i api/openapi.yaml -g python -o sdk/python \ + --additional-properties=packageName=fetchml_client 2>/dev/null \ + || echo "Note: npm install -g @openapitools/openapi-generator-cli" + +# ----------------------------------------------------------------------------- # Documentation -docs: docs-setup docs-check-port - @echo "Starting Hugo development server..." +# ----------------------------------------------------------------------------- + +docs-setup: + @command -v hugo >/dev/null 2>&1 \ + || (printf "Hugo not found.\n macOS: brew install hugo\n Linux: https://gohugo.io/installation/\n" && exit 1) + @echo "$(OK) Hugo available" + +docs: docs-setup cd docs && hugo server --bind "$(DOCS_BIND)" --port "$(DOCS_PORT)" --baseURL "$(DOCS_BASEURL)" -# Build documentation docs-build: - @echo "Building static documentation..." cd docs && hugo docs-build-prod: - @echo "Building production docs..." cd docs && hugo --minify --baseURL "$(DOCS_PROD_BASEURL)" -# Performance benchmarking tools +docs-generate: + @mkdir -p docs/api + @command -v npx >/dev/null 2>&1 \ + && npx @redocly/cli build-docs api/openapi.yaml \ + --output docs/api/index.html --title "FetchML API" 2>/dev/null \ + || echo "Note: npm install -g @redocly/cli" + +docs-serve: + @command -v npx >/dev/null 2>&1 \ + && npx @redocly/cli preview-docs api/openapi.yaml \ + || echo "Note: npx not available" + +# ----------------------------------------------------------------------------- +# Performance +# ----------------------------------------------------------------------------- + benchmark: - @echo "Running performance benchmarks..." go test -bench=. -benchmem ./tests/benchmarks/... -# Run benchmarks locally with artifact management benchmark-local: - @echo "Running benchmarks locally with full workflow..." - ./scripts/benchmarks/run-benchmarks-local.sh + @./scripts/benchmarks/run-benchmarks-local.sh -# Run benchmarks with native libraries (requires native_libs build tag) benchmark-native: - @echo "Running benchmarks with native libraries..." go test -bench=. -benchmem -tags native_libs ./tests/benchmarks/... -# Compare Go vs Native implementation performance benchmark-compare: - @echo "=== Go Implementation ===" - @go test -bench=. -benchmem ./tests/benchmarks/... 2>&1 | grep -E '(Benchmark|ns/op|allocs/op)' || true - @echo "" - @echo "=== Native Implementation ===" - @go test -bench=. -benchmem -tags native_libs ./tests/benchmarks/... 2>&1 | grep -E '(Benchmark|ns/op|allocs/op)' || echo "Native not available (build with: make native-build)" + @echo "=== Go ===" + @go test -bench=. -benchmem ./tests/benchmarks/... 2>&1 \ + | grep -E '(Benchmark|ns/op|allocs/op)' || true + @echo "\n=== Native ===" + @go test -bench=. -benchmem -tags native_libs ./tests/benchmarks/... 2>&1 \ + | grep -E '(Benchmark|ns/op|allocs/op)' \ + || echo "Native not available (run: make native-build)" -# Manage benchmark artifacts -artifacts: - @echo "Managing benchmark artifacts..." - ./scripts/dev/manage-artifacts.sh help - -# Clean benchmark artifacts (keep last 10) -clean-benchmarks: - @echo "Cleaning benchmark artifacts..." - ./scripts/maintenance/cleanup-benchmarks.sh benchmarks - -# Comprehensive cleanup (keep last 5 runs) -clean-all: - @echo "Running comprehensive cleanup..." - ./scripts/maintenance/cleanup-benchmarks.sh all - -# Aggressive cleanup (removes more data) -clean-aggressive: - @echo "Running aggressive cleanup..." - ./scripts/maintenance/cleanup-benchmarks.sh aggressive - -# Show disk usage status -status: - @echo "Checking disk usage..." - ./scripts/maintenance/cleanup-benchmarks.sh status - -size: - @echo "Binary sizes:" - @ls -lh bin/* cli/zig-out/bin/ml 2>/dev/null || true - -# Load testing load-test: - @echo "Running load tests..." go test -v ./tests/load/... -# CPU profiling for HTTP LoadTestSuite (MediumLoad only for speed) -profile-load: - @echo "CPU profiling MediumLoad HTTP load test..." - @mkdir -p tests/bin - go test ./tests/load -run TestLoadProfile_Medium -count=1 -cpuprofile tests/bin/cpu_load.out - @echo "${OK} CPU profile written to cpu_load.out (inspect with: go tool pprof tests/bin/cpu_load.out)" - -profile-load-norate: - @echo "CPU profiling MediumLoad HTTP load test (no rate limiting)..." - @mkdir -p tests/bin - go test ./tests/load -run TestLoadProfile_Medium -count=1 -cpuprofile tests/bin/cpu_load.out -v -args -profile-norate - @echo "${OK} CPU profile written to cpu_load.out (inspect with: go tool pprof tests/bin/cpu_load.out)" - -# CPU profiling for WebSocket → Redis queue → worker path -profile-ws-queue: - @echo "CPU profiling WebSocket queue integration test..." - @mkdir -p tests/bin - go test ./tests/integration -run WebSocketQueue -count=2 -cpuprofile tests/bin/cpu_ws.out - @echo "${OK} CPU profile written to cpu_ws.out (inspect with: go tool pprof tests/bin/cpu_ws.out)" - -# Chaos engineering tests chaos-test: - @echo "Running chaos engineering tests..." go test -v ./tests/chaos/... -# Performance profiling tools -profile-tools: - @echo "Building profiling tools..." - go build -o bin/performance-regression-detector ./cmd/performance-regression-detector - go build -o bin/profiler ./cmd/profiler - -# Performance regression detection threshold (percentage) -REGRESSION_THRESHOLD ?= 10.0 - -# Performance regression detection -detect-regressions: - @echo "Detecting performance regressions..." +profile-load: @mkdir -p tests/bin - @if [ ! -f "tests/bin/baseline.bench.txt" ]; then \ - echo "Creating baseline performance metrics..."; \ + go test ./tests/load -run TestLoadProfile_Medium -count=1 -cpuprofile tests/bin/cpu_load.out + @echo "$(OK) Profile → tests/bin/cpu_load.out" + +profile-load-norate: + @mkdir -p tests/bin + go test ./tests/load -run TestLoadProfile_Medium -count=1 \ + -cpuprofile tests/bin/cpu_load.out -v -args -profile-norate + +profile-ws-queue: + @mkdir -p tests/bin + go test ./tests/integration -run WebSocketQueue -count=2 -cpuprofile tests/bin/cpu_ws.out + @echo "$(OK) Profile → tests/bin/cpu_ws.out" + +profile-tools: + @mkdir -p bin/tools + go build -ldflags="-s -w" -o bin/tools/performance-regression-detector \ + ./cmd/performance-regression-detector + go build -ldflags="-s -w" -o bin/tools/profiler ./cmd/profiler + @echo "$(OK) Profiling tools in bin/tools/" + +detect-regressions: profile-tools + @mkdir -p tests/bin + @if [ ! -f tests/bin/baseline.bench.txt ]; then \ + echo "Creating baseline..."; \ go test -bench=. -benchmem ./tests/benchmarks/... | tee tests/bin/baseline.bench.txt; \ fi - @echo "Analyzing current performance against baseline..." go test -bench=. -benchmem ./tests/benchmarks/... | tee tests/bin/current.bench.txt - @$(MAKE) profile-tools - @./bin/performance-regression-detector -baseline tests/bin/baseline.bench.txt -current tests/bin/current.bench.txt -threshold $(REGRESSION_THRESHOLD) + ./bin/tools/performance-regression-detector \ + -baseline tests/bin/baseline.bench.txt \ + -current tests/bin/current.bench.txt \ + -threshold 10.0 -# Performance regression detection with native libraries -detect-regressions-native: native-build - @echo "Detecting performance regressions with native libraries..." +detect-regressions-native: native-build profile-tools @mkdir -p tests/bin - @if [ ! -f "tests/bin/baseline-native.bench.txt" ]; then \ - echo "Creating native baseline performance metrics..."; \ - go test -bench=. -benchmem -tags native_libs ./tests/benchmarks/... | tee tests/bin/baseline-native.bench.txt; \ + @if [ ! -f tests/bin/baseline-native.bench.txt ]; then \ + echo "Creating native baseline..."; \ + go test -bench=. -benchmem -tags native_libs ./tests/benchmarks/... \ + | tee tests/bin/baseline-native.bench.txt; \ fi - @echo "Analyzing current native performance against baseline..." - @go test -bench=. -benchmem -tags native_libs ./tests/benchmarks/... | tee tests/bin/current-native.bench.txt - @$(MAKE) profile-tools - @./bin/performance-regression-detector -baseline tests/bin/baseline-native.bench.txt -current tests/bin/current-native.bench.txt -threshold $(REGRESSION_THRESHOLD) + go test -bench=. -benchmem -tags native_libs ./tests/benchmarks/... \ + | tee tests/bin/current-native.bench.txt + ./bin/tools/performance-regression-detector \ + -baseline tests/bin/baseline-native.bench.txt \ + -current tests/bin/current-native.bench.txt \ + -threshold 10.0 + +clean-benchmarks: + @./scripts/maintenance/cleanup-benchmarks.sh benchmarks + +status: + @./scripts/maintenance/cleanup-benchmarks.sh status + +size: + @ls -lh bin/server/* bin/cli/* bin/tools/* 2>/dev/null || true -# Technical excellence suite (runs all performance tests) complete-suite: benchmark load-test chaos-test profile-tools - @echo "Technical excellence test suite completed" - @echo "Results summary:" - @echo " - Benchmarks: See test output above" - @echo " - Load tests: See test output above" - @echo " - Chaos tests: See test output above" - @echo " - Profiling tools: Built in bin/" - @echo " - Regression detection: Run 'make detect-regressions'" + @echo "$(OK) Performance suite complete" -# Help -help: - @echo "FetchML Build System" - @echo "" - @echo "Build Targets:" - @echo " make build - Build all components (default)" - @echo " make prod - Build production-optimized binaries" - @echo " make prod-with-native - Build production binaries with native C++ libs" - @echo " make dev - Build development binaries (faster)" - @echo " make clean - Remove build artifacts" - @echo " make clean-release - Thorough cleanup for release" - @echo " make prepare-release - Full release preparation (cleanup + verify)" - @echo "" - @echo "Native Library Targets:" - @echo " make native-build - Build native C++ libraries" - @echo " make native-release - Build native libs (release optimized)" - @echo " make native-debug - Build native libs (debug with ASan)" - @echo " make native-test - Run native library tests" - @echo " make native-smoke - Run native smoke test (C++ + Go integration)" - @echo " make native-clean - Clean native build artifacts" - @echo "" - @echo "Docker Targets:" - @echo " make docker-build - Build Docker image" - @echo "" - @echo "Test Targets:" - @echo " make test - Run all tests" - @echo " make test-unit - Run unit tests only" - @echo " make test-integration - Run integration tests" - @echo " make test-e2e - Run end-to-end tests (Podman test is opt-in via FETCH_ML_E2E_PODMAN=1)" - @echo " make test-coverage - Generate coverage report" - @echo " make lint - Run formatters and linters" - @echo " make ci-checks - Run maintainability CI checks" - @echo " make ci-local - Run local CI dry-run (tests, lint, config validation, coverage)" - @echo " make configlint - Validate YAML configs against schema" - @echo " make worker-configlint - Validate worker configs against schema" - @echo "" - @echo "Setup Targets:" - @echo " make install - Install binaries to /usr/local/bin (requires sudo)" - @echo "" - @echo "Performance Testing:" - @echo " make benchmark - Run performance benchmarks" - @echo " make benchmark-local - Run benchmarks locally with artifact management" - @echo " make benchmark-native - Run benchmarks with native libraries" - @echo " make artifacts - Manage benchmark artifacts (list, clean, compare, export)" - @echo " make clean-benchmarks - Clean benchmark artifacts (keep last 10)" - @echo " make clean-all - Comprehensive cleanup (keep last 5 runs)" - @echo " make clean-aggressive - Aggressive cleanup (removes more data)" - @echo " make status - Show disk usage status" - @echo " make load-test - Run load testing suite" - @echo " make profile-load - CPU profile MediumLoad HTTP test suite" - @echo " make profile-ws-queue - CPU profile WebSocket→queue→worker path" - @echo " make chaos-test - Run chaos engineering tests" - @echo " make profile-tools - Build performance profiling tools" - @echo " make detect-regressions - Detect performance regressions" - @echo " make detect-regressions-native - Detect performance regressions with native libs" - @echo " make complete-suite - Run complete technical suite" - @echo "" - @echo "Documentation:" - @echo " make docs-setup - Validate Hugo is installed" - @echo " make docs - Start Hugo dev server (checks DOCS_PORT is free)" - @echo " make docs-check-port - Check if DOCS_PORT is already in use" - @echo " make docs-stop - Stop process listening on DOCS_PORT (use with care)" - @echo " make docs-build - Build static documentation (local)" - @echo " make docs-build-prod - Build static documentation (prod flags; set DOCS_PROD_BASEURL)" - @echo "" - @echo "Scheduler & Services:" - @echo " make dev-up - Start development environment (API, Worker, Redis, monitoring)" - @echo " make dev-down - Stop development environment" - @echo " make dev-status - Check development environment status" - @echo " make dev-logs - View development environment logs" - @echo " make prod-up - Start production environment" - @echo " make prod-down - Stop production environment" - @echo " make scheduler-config - Edit scheduler configuration example" - @echo "" - @echo "Utility:" - @echo " make size - Show binary sizes" - @echo " make self-cleanup - Clean up Docker resources" - @echo " make test-full - Run complete test suite" - @echo " make test-auth - Test multi-user authentication" - @echo " make help - Show this help" - -# Self-cleaning for Docker resources -self-cleanup: - @echo "Running self-cleanup..." - @./scripts/maintenance/cleanup.sh - -# Run full test suite -test-full: - @echo "Running full test suite..." - @$(MAKE) ci-local - -# Quick authentication test -test-auth: - @echo "Testing multi-user authentication..." - @echo "Testing admin user..." && cp ~/.ml/config-admin.toml ~/.ml/config.toml && ./cli/zig-out/bin/ml status - @echo "Testing researcher user..." && cp ~/.ml/config-researcher.toml ~/.ml/config.toml && ./cli/zig-out/bin/ml status - @echo "Testing analyst user..." && cp ~/.ml/config-analyst.toml ~/.ml/config.toml && ./cli/zig-out/bin/ml status - -# Deployment management (using organized docker-compose files) -deploy-up: - @echo "Starting development environment..." - @./deployments/deploy.sh dev up - -deploy-down: - @echo "Stopping development environment..." - @./deployments/deploy.sh dev down - -deploy-status: - @echo "Checking deployment status..." - @./deployments/deploy.sh dev status - -deploy-clean: - @echo "Cleaning all deployments..." - @cd deployments && make clean +# ----------------------------------------------------------------------------- +# Deployment +# ----------------------------------------------------------------------------- dev-up: - @./deployments/deploy.sh dev up + $(DC) -f deployments/docker-compose.dev.yml up -d + @echo "$(OK) Dev environment up (Caddy: 8080/8443 Redis: 6379 Prometheus: 9090 Grafana: 3000)" dev-down: - @./deployments/deploy.sh dev down - -dev-status: - @./deployments/deploy.sh dev status + $(DC) -f deployments/docker-compose.dev.yml down dev-logs: - @./deployments/deploy.sh dev logs + $(DC) -f deployments/docker-compose.dev.yml logs -f + +dev-restart: + $(DC) -f deployments/docker-compose.dev.yml restart + +staging-up: + @if [ ! -f deployments/.env.staging ]; then \ + printf 'DATA_DIR=./data/staging\nLOG_LEVEL=info\nCOMPLIANCE_MODE=standard\n' \ + > deployments/.env.staging; \ + echo "Created deployments/.env.staging with defaults"; \ + fi + $(DC) -f deployments/docker-compose.staging.yml up -d + +staging-down: + $(DC) -f deployments/docker-compose.staging.yml down + +staging-logs: + $(DC) -f deployments/docker-compose.staging.yml logs -f + +staging-restart: + $(DC) -f deployments/docker-compose.staging.yml restart + +staging-status: + $(DC) -f deployments/docker-compose.staging.yml ps + +homelab-secure-up: + $(DC) -f deployments/docker-compose.homelab-secure.yml up -d + +homelab-secure-down: + $(DC) -f deployments/docker-compose.homelab-secure.yml down prod-up: - @./deployments/deploy.sh prod up + @echo "⚠ WARNING: This is production. Ensure you have proper backups." + @read -p "Continue? [y/N] " c && [ "$$c" = "y" ] || exit 1 + $(DC) -f deployments/docker-compose.prod.yml up -d prod-down: - @./deployments/deploy.sh prod down - -prod-status: - @./deployments/deploy.sh prod status + $(DC) -f deployments/docker-compose.prod.yml down prod-logs: - @./deployments/deploy.sh prod logs + $(DC) -f deployments/docker-compose.prod.yml logs -f -# ============================================================================= -# SECURITY TARGETS -# ============================================================================= +prod-restart: + @read -p "Restart production? [y/N] " c && [ "$$c" = "y" ] || exit 1 + $(DC) -f deployments/docker-compose.prod.yml restart -.PHONY: security-scan gosec govulncheck check-unsafe security-audit +prod-status: + $(DC) -f deployments/docker-compose.prod.yml ps -# Run all security scans -security-scan: gosec govulncheck check-unsafe - @echo "${OK} Security scan complete" +status-all: + @for env in dev staging homelab-secure prod; do \ + f="deployments/docker-compose.$$env.yml"; \ + echo "=== $$env ==="; \ + [ -f "$$f" ] && $(DC) -f "$$f" ps 2>/dev/null || echo "Not running"; \ + echo; \ + done -# Run gosec security linter -gosec: - @mkdir -p reports - @echo "Running gosec security scan..." - @if command -v gosec >/dev/null 2>&1; then \ - gosec -fmt=json -out=reports/gosec-results.json ./... 2>/dev/null || true; \ - gosec -fmt=sarif -out=reports/gosec-results.sarif ./... 2>/dev/null || true; \ - gosec ./... 2>/dev/null || echo "Note: gosec found issues (see reports/gosec-results.json)"; \ - else \ - echo "Installing gosec..."; \ - go install github.com/securego/gosec/v2/cmd/gosec@latest; \ - gosec -fmt=json -out=reports/gosec-results.json ./... 2>/dev/null || true; \ - fi - @echo "${OK} gosec scan complete (see reports/gosec-results.*)" +clean-deploy: + @echo "Removes all containers and volumes for every environment. Continue? [y/N]" + @read -r c && [ "$$c" = "y" ] || exit 1 + @for env in dev staging homelab-secure prod; do \ + f="deployments/docker-compose.$$env.yml"; \ + [ -f "$$f" ] && $(DC) -f "$$f" down -v 2>/dev/null || true; \ + done + docker system prune -f + @echo "$(OK) All deployments cleaned" -# Run govulncheck for known vulnerabilities -govulncheck: - @echo "Running govulncheck for known vulnerabilities..." - @if command -v govulncheck >/dev/null 2>&1; then \ - govulncheck ./...; \ - else \ - echo "Installing govulncheck..."; \ - go install golang.org/x/vuln/cmd/govulncheck@latest; \ - govulncheck ./...; \ - fi - @echo "${OK} govulncheck complete" +rollback-staging: + @echo "⚠ Rolls back image only — queue state and audit log are NOT rolled back." + @read -p "Continue? [y/N] " c && [ "$$c" = "y" ] || exit 1 + $(DC) -f deployments/docker-compose.staging.yml down + $(DC) -f deployments/docker-compose.staging.yml up -d + @echo "$$(date -Iseconds) | rollback | staging | actor=$$(whoami)" \ + >> deployments/.staging-audit.log -# Check for unsafe package usage -check-unsafe: - @echo "Checking for unsafe package usage..." - @if grep -r "unsafe\." --include="*.go" ./internal ./cmd ./pkg 2>/dev/null; then \ - echo "WARNING: Found unsafe package usage (review required)"; \ - exit 1; \ - else \ - echo "${OK} No unsafe package usage found"; \ - fi +rollback-prod: + @echo "⚠ CRITICAL: Image rollback only." + @echo " Queue state, audit chain, and storage artifacts are NOT rolled back." + @read -p "CONFIRM PRODUCTION ROLLBACK? [yes/N] " c && [ "$$c" = "yes" ] || exit 1 + $(DC) -f deployments/docker-compose.prod.yml down + $(DC) -f deployments/docker-compose.prod.yml up -d + @echo "$$(date -Iseconds) | rollback | prod | actor=$$(whoami)" \ + >> deployments/.prod-audit.log + @echo "$(OK) Rollback complete — verify: make prod-status" -# Full security audit (tests + scans) -security-audit: security-scan test-security - @echo "${OK} Full security audit complete" +deploy-health-check: + @echo "=== Health Checks ===" + @for item in "Development=9101" "Staging=9102" "Production=9101"; do \ + label=$${item%%=*}; port=$${item##*=}; \ + printf "%-16s (localhost:$$port): " "$$label"; \ + curl -fsS http://localhost:$$port/health 2>/dev/null && echo "✓" || echo "✗ not responding"; \ + done -# Run security-specific tests -test-security: - @echo "Running security tests..." - @go test -v ./tests/security/... 2>/dev/null || echo "Note: No security tests yet (will be added in Phase 5)" - @echo "${OK} Security tests complete" +# ----------------------------------------------------------------------------- +# Install & Misc +# ----------------------------------------------------------------------------- -# ============================================================================= -# API / OPENAPI TARGETS -# ============================================================================= +install: prod + sudo cp bin/server/api-server /usr/local/bin/fetchml-api + sudo cp bin/server/worker /usr/local/bin/fetchml-worker + sudo cp bin/server/tui /usr/local/bin/fetchml-tui + sudo cp bin/cli/ml /usr/local/bin/ml + @echo "$(OK) Installed" -.PHONY: openapi-generate openapi-validate +docker-build: + docker build -f build/docker/simple.Dockerfile -t fetchml:latest . + @echo "$(OK) Docker image built" -# Generate Go types from OpenAPI spec (default, safe) -openapi-generate: - @echo "Generating Go types from OpenAPI spec..." - @if command -v oapi-codegen >/dev/null 2>&1; then \ - oapi-codegen -package api -generate types api/openapi.yaml > internal/api/types.go; \ - echo "${OK} Generated internal/api/types.go"; \ - else \ - echo "Installing oapi-codegen v2..."; \ - go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest; \ - oapi-codegen -package api -generate types api/openapi.yaml > internal/api/types.go; \ - echo "${OK} Generated internal/api/types.go"; \ - fi +dev-smoke: + @bash ./scripts/dev/smoke-test.sh dev -# Generate full server interfaces (for Phase 5 migration) -openapi-generate-server: - @echo "Generating Go server code from OpenAPI spec..." - @if command -v oapi-codegen >/dev/null 2>&1; then \ - oapi-codegen -package api -generate types,server,spec api/openapi.yaml > internal/api/server_gen.go; \ - echo "${OK} Generated internal/api/server_gen.go"; \ - else \ - echo "Installing oapi-codegen v2..."; \ - go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest; \ - oapi-codegen -package api -generate types,server,spec api/openapi.yaml > internal/api/server_gen.go; \ - echo "${OK} Generated internal/api/server_gen.go"; \ - fi +prod-smoke: + @bash ./scripts/dev/smoke-test.sh prod -# Validate OpenAPI spec against schema -openapi-validate: - @echo "Validating OpenAPI spec..." - @if command -v openapi-generator >/dev/null 2>&1; then \ - openapi-generator validate -i api/openapi.yaml 2>/dev/null || echo "Note: Validation warnings (non-fatal)"; \ - else \ - echo "Note: Install openapi-generator for validation (https://openapi-generator.tech)"; \ - fi - @echo "${OK} OpenAPI validation complete" +test-auth: + @for role in admin researcher analyst; do \ + echo "Testing $$role user..."; \ + cp ~/.ml/config-$$role.toml ~/.ml/config.toml && ./bin/cli/ml status; \ + done -# CI check: fail if openapi.yaml changed but types.go didn't -openapi-check-ci: openapi-generate - @git diff --exit-code internal/api/types.go 2>/dev/null || \ - (echo "ERROR: OpenAPI spec changed but generated types not updated. Run 'make openapi-generate'" && exit 1) - @echo "${OK} OpenAPI types are up to date" +install-tools: + $(call ensure_tool,gosec,github.com/securego/gosec/v2/cmd/gosec@latest) + $(call ensure_tool,govulncheck,golang.org/x/vuln/cmd/govulncheck@latest) + $(call ensure_tool,go-mutesting,github.com/zimmski/go-mutesting/cmd/go-mutesting@latest) + $(call ensure_tool,scorecard,github.com/ossf/scorecard/v4/cmd/scorecard@latest) + $(call ensure_tool,oapi-codegen,github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest) + @echo "$(OK) All tools installed" -# CI check: verify spec matches implementation (generated code is in sync) -openapi-check-implementation: - @echo "Verifying spec matches implementation..." - @# Generate fresh types - $(MAKE) openapi-generate-server - @# Check for drift - @git diff --exit-code internal/api/server_gen.go 2>/dev/null || \ - (echo "ERROR: Implementation drift detected. Regenerate with 'make openapi-generate-server'" && exit 1) - @echo "${OK} Spec and implementation in sync" - -# Generate static HTML API documentation -docs-generate: - @echo "Generating API documentation..." - @mkdir -p docs/api - @if command -v npx >/dev/null 2>&1; then \ - npx @redocly/cli build-docs api/openapi.yaml \ - --output docs/api/index.html \ - --title "FetchML API" 2>/dev/null || \ - echo "Note: Redocly CLI not available. Install with: npm install -g @redocly/cli"; \ - else \ - echo "Note: npx not available. Install Node.js to generate docs"; \ - fi - @echo "${OK} Docs generation complete" - -# Serve API documentation locally -docs-serve: - @if command -v npx >/dev/null 2>&1; then \ - npx @redocly/cli preview-docs api/openapi.yaml; \ - else \ - echo "Note: npx not available. Install Node.js to serve docs"; \ - fi - -# Generate TypeScript client SDK -openapi-generate-ts: - @echo "Generating TypeScript client..." - @mkdir -p sdk/typescript - @if command -v npx >/dev/null 2>&1; then \ - npx @openapitools/openapi-generator-cli generate \ - -i api/openapi.yaml \ - -g typescript-fetch \ - -o sdk/typescript \ - --additional-properties=supportsES6=true,npmName=fetchml-client 2>/dev/null || \ - echo "Note: openapi-generator-cli not available. Install with: npm install -g @openapitools/openapi-generator-cli"; \ - else \ - echo "Note: npx not available. Install Node.js to generate TypeScript client"; \ - fi - @echo "${OK} TypeScript client generation complete" - -# Generate Python client SDK -openapi-generate-python: - @echo "Generating Python client..." - @mkdir -p sdk/python - @if command -v npx >/dev/null 2>&1; then \ - npx @openapitools/openapi-generator-cli generate \ - -i api/openapi.yaml \ - -g python \ - -o sdk/python \ - --additional-properties=packageName=fetchml_client 2>/dev/null || \ - echo "Note: openapi-generator-cli not available. Install with: npm install -g @openapitools/openapi-generator-cli"; \ - else \ - echo "Note: npx not available. Install Node.js to generate Python client"; \ - fi - @echo "${OK} Python client generation complete" - -# Generate all client SDKs -openapi-generate-clients: openapi-generate-ts openapi-generate-python - @echo "${OK} All client SDKs generated" - -# ============================================================================ -# Verification & Maintenance Targets (V.1 - V.10) -# ============================================================================ - -# V.1: Verify manifest schema hasn't drifted from committed version -verify-schema: - @echo "Verifying manifest schema..." - @go test ./internal/manifest/... -run TestSchemaUnchanged -v - @echo "${OK} Schema validation passed" - -# V.1: Test manifest schema validation with example manifests -test-schema-validation: - @echo "Testing manifest schema validation..." - @go test ./internal/manifest/... -run TestSchemaValidatesExampleManifest -v - @go test ./internal/manifest/... -run TestSchemaRejectsInvalidManifest -v - @echo "${OK} Schema tests passed" - -# V.4: Build and run custom linting tool (fetchml-vet) -lint-custom: - @echo "Building custom linting tool..." - @go build -o bin/fetchml-vet ./tools/fetchml-vet/cmd/fetchml-vet/ - @echo "Running custom lint rules..." - @go vet -vettool=bin/fetchml-vet ./internal/... ./cmd/... 2>/dev/null || true - @echo "${OK} Custom linting complete" - -# V.7: Verify audit chain integrity -verify-audit: - @echo "Verifying audit chain integrity..." - @go test ./tests/unit/audit/... -run TestChainVerifier -v - @echo "${OK} Audit chain verification passed" - -# V.7: Run audit verifier tool (requires log path) -verify-audit-chain: - @if [ -z "$(AUDIT_LOG_PATH)" ]; then \ - echo "Usage: make verify-audit-chain AUDIT_LOG_PATH=/path/to/audit.log"; \ - exit 1; \ - fi - @echo "Building audit verifier..." - @go build -o bin/audit-verifier ./cmd/audit-verifier/ - @echo "Verifying audit chain at $(AUDIT_LOG_PATH)..." - @./bin/audit-verifier -log-path=$(AUDIT_LOG_PATH) - @echo "${OK} Audit chain integrity verified" - -# Run all verification checks (for CI) -verify-all: verify-schema test-schema-validation lint-custom verify-audit - @echo "${OK} All verification checks passed" - -# V.2: Install property-based testing dependencies -install-property-test-deps: - @echo "Installing property-based testing dependencies..." - @go get github.com/leanovate/gopter 2>/dev/null || true - @echo "${OK} Property testing dependencies installed" - -# V.3: Install mutation testing tool -install-mutation-test-deps: - @echo "Installing mutation testing tool..." - @go install github.com/zimmski/go-mutesting/cmd/go-mutesting@latest 2>/dev/null || true - @echo "${OK} Mutation testing tool installed" - -# V.6: Install security scanning tools -install-security-scan-deps: - @echo "Installing security scanning tools..." - @go install github.com/securego/gosec/v2/cmd/gosec@latest 2>/dev/null || true - @go install github.com/sonatype-nexus-community/nancy@latest 2>/dev/null || true - @echo "${OK} Security scanning tools installed" - -# V.10: Install OpenSSF Scorecard -install-scorecard: - @echo "Installing OpenSSF Scorecard..." - @go install github.com/ossf/scorecard/v4/cmd/scorecard@latest 2>/dev/null || true - @echo "${OK} Scorecard installed" - -# Install all verification tools -install-verify-deps: install-property-test-deps install-mutation-test-deps install-security-scan-deps install-scorecard - @echo "${OK} All verification tools installed" - -# Quick verification for development (fast checks only) -verify-quick: verify-schema - @echo "${OK} Quick verification passed" - -# Full verification suite (slow, comprehensive) -verify-full: verify-all - @echo "Running full verification suite..." - @$(MAKE) test-unit - @$(MAKE) test-integration - @echo "${OK} Full verification passed" +help: + @printf "FetchML Build System\n\n" + @printf "Build\n" + @printf " build Build all components (default)\n" + @printf " prod Production-optimized binaries\n" + @printf " prod-with-native Production binaries + native C++ libs\n" + @printf " dev Fast development build\n" + @printf " cross-platform Linux x86_64 static binaries in dist/\n" + @printf " build-cli Build Zig CLI only\n" + @printf " clean Remove all build artifacts\n" + @printf " clean-release Thorough pre-release cleanup\n" + @printf " prepare-release clean-release + release verification\n" + @printf " verify-build Check build reproducibility\n" + @printf "\n" + @printf "Native Libraries\n" + @printf " native-build C++ libs (release)\n" + @printf " native-release C++ libs (fully optimized)\n" + @printf " native-debug C++ libs (debug + ASan)\n" + @printf " native-test Run C++ unit tests\n" + @printf " native-smoke C++ + Go integration smoke test\n" + @printf " native-clean Remove native build artifacts\n" + @printf "\n" + @printf "Tests\n" + @printf " test All tests with infrastructure (CI default)\n" + @printf " test-unit Fast unit tests, no infrastructure\n" + @printf " test-integration Integration tests (infra auto-managed)\n" + @printf " test-e2e E2E tests (CLI auto-built, infra auto-managed)\n" + @printf " test-coverage Generate HTML coverage report\n" + @printf " test-infra-up Start Redis + SSH test containers\n" + @printf " test-infra-down Stop test containers\n" + @printf "\n" + @printf "Lint & CI\n" + @printf " lint gofmt + go vet + zig fmt\n" + @printf " configlint Validate API YAML configs\n" + @printf " worker-configlint Validate worker configs\n" + @printf " ci-checks Maintainability checks\n" + @printf " ci-local Full local CI (lint → config → test → coverage)\n" + @printf "\n" + @printf "Verification\n" + @printf " verify-quick Schema check only\n" + @printf " verify-all Schema + custom lint + audit chain\n" + @printf " verify-audit-chain Verify live audit log AUDIT_LOG_PATH=...\n" + @printf " install-tools Install all dev/CI tools in one shot\n" + @printf "\n" + @printf "Security\n" + @printf " security-scan gosec + govulncheck + unsafe check\n" + @printf " security-audit security-scan + security tests\n" + @printf "\n" + @printf "OpenAPI\n" + @printf " openapi-generate Regenerate internal/api/types.go\n" + @printf " openapi-generate-server Regenerate internal/api/server_gen.go\n" + @printf " openapi-generate-clients TypeScript + Python SDKs\n" + @printf " openapi-check-ci Fail if types.go out of sync\n" + @printf " openapi-check-implementation Fail if server_gen.go out of sync\n" + @printf "\n" + @printf "Performance\n" + @printf " benchmark Go benchmarks\n" + @printf " benchmark-native Benchmarks with native libs\n" + @printf " benchmark-compare Side-by-side Go vs native\n" + @printf " load-test / chaos-test Load and chaos suites\n" + @printf " detect-regressions Compare against saved baseline\n" + @printf " complete-suite benchmark + load + chaos + profile tools\n" + @printf "\n" + @printf "Deployment\n" + @printf " dev-up / dev-down Development environment\n" + @printf " staging-up / staging-down Staging environment\n" + @printf " prod-up / prod-down Production (confirmation required)\n" + @printf " status-all Status across all environments\n" + @printf " deploy-health-check HTTP health checks for all environments\n" + @printf " rollback-staging Roll back staging image\n" + @printf " rollback-prod Roll back production image (double-confirmed)\n" + @printf " clean-deploy Tear down all environments + prune Docker\n" + @printf "\n" + @printf "Install\n" + @printf " install Install binaries to /usr/local/bin\n" + @printf " docker-build Build Docker image\n" + @printf " install-tools Install all dev/CI tools\n" + @printf " size Show binary sizes\n" + @printf " help This message\n"