diff --git a/Makefile b/Makefile index 8d57d66..4ecc422 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # Makefile - Lolly Build Commands -# 版本信息 APP_NAME := lolly -VERSION := 0.2.2 +FALLBACK_VERSION := 0.2.2 +VERSION := $(shell git describe --tags --always --dirty 2>/dev/null | sed 's/^v//' || echo "$(FALLBACK_VERSION)") GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") @@ -11,10 +11,8 @@ GO_VERSION := $(shell go env GOVERSION) BUILD_PLATFORM := $(shell go env GOOS)/$(shell go env GOARCH) BUILD_DIR := bin -# 静态构建(禁用 CGO) CGO_DISABLE := CGO_ENABLED=0 -# 生产构建标志 LDFLAGS := -ldflags "-s -w \ -X 'rua.plus/lolly/internal/version.Version=$(VERSION)' \ -X 'rua.plus/lolly/internal/version.GitCommit=$(GIT_COMMIT)' \ @@ -23,40 +21,35 @@ LDFLAGS := -ldflags "-s -w \ -X 'rua.plus/lolly/internal/version.GoVersion=$(GO_VERSION)' \ -X 'rua.plus/lolly/internal/version.BuildPlatform=$(BUILD_PLATFORM)'" -# 运行时性能优化标志 PERF_GCFLAGS := -gcflags="-l=4" PERF_ASMFLAGS := -asmflags="-l=4" -# Go 文件 MAIN_PATH := main.go -# Windows 可执行文件扩展名 ifeq ($(OS),Windows_NT) EXECUTABLE := $(BUILD_DIR)/$(APP_NAME).exe else EXECUTABLE := $(BUILD_DIR)/$(APP_NAME) endif -# 默认目标 .DEFAULT_GOAL := build # ============================================ # 构建命令 # ============================================ -# 生产构建(最大运行时性能,静态链接) -build: - @echo "Building $(APP_NAME) with max runtime performance (static)..." +$(BUILD_DIR): @mkdir -p $(BUILD_DIR) + +build: | $(BUILD_DIR) + @echo "Building $(APP_NAME) (static, optimized)..." $(CGO_DISABLE) go build $(LDFLAGS) $(PERF_GCFLAGS) $(PERF_ASMFLAGS) -trimpath \ -o $(EXECUTABLE) $(MAIN_PATH) - @echo "Performance build complete: $(EXECUTABLE)" + @echo "Build complete: $(EXECUTABLE)" -# PGO 构建(需先收集 profile,静态链接) PGO_PROFILE ?= default.pgo -build-pgo: +build-pgo: | $(BUILD_DIR) @echo "Building $(APP_NAME) with PGO optimization (static)..." - @mkdir -p $(BUILD_DIR) if [ -f $(PGO_PROFILE) ]; then \ $(CGO_DISABLE) go build $(LDFLAGS) $(PERF_GCFLAGS) $(PERF_ASMFLAGS) -trimpath \ -pgo=$(PGO_PROFILE) -o $(EXECUTABLE) $(MAIN_PATH); \ @@ -67,7 +60,6 @@ build-pgo: exit 1; \ fi -# 收集 PGO profile(运行代表性 workload) pgo-collect: @echo "=== PGO Profile Collection Guide ===" @echo "" @@ -95,27 +87,26 @@ pgo-collect: @echo "" @echo "Tip: Profile during real workload for best PGO results" -# 跨平台构建(静态链接) -build-linux: - @echo "Building for Linux (static)..." - @mkdir -p $(BUILD_DIR) - $(CGO_DISABLE) GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -trimpath -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(MAIN_PATH) +build-linux: | $(BUILD_DIR) + @echo "Building for Linux amd64 (static)..." + $(CGO_DISABLE) GOOS=linux GOARCH=amd64 go build $(LDFLAGS) $(PERF_GCFLAGS) $(PERF_ASMFLAGS) -trimpath \ + -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 $(MAIN_PATH) @echo "Built: $(BUILD_DIR)/$(APP_NAME)-linux-amd64" -build-darwin: +build-darwin: | $(BUILD_DIR) @echo "Building for macOS (static)..." - @mkdir -p $(BUILD_DIR) - $(CGO_DISABLE) GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -trimpath -o $(BUILD_DIR)/$(APP_NAME)-darwin-amd64 $(MAIN_PATH) - $(CGO_DISABLE) GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -trimpath -o $(BUILD_DIR)/$(APP_NAME)-darwin-arm64 $(MAIN_PATH) + $(CGO_DISABLE) GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) $(PERF_GCFLAGS) $(PERF_ASMFLAGS) -trimpath \ + -o $(BUILD_DIR)/$(APP_NAME)-darwin-amd64 $(MAIN_PATH) + $(CGO_DISABLE) GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) $(PERF_GCFLAGS) $(PERF_ASMFLAGS) -trimpath \ + -o $(BUILD_DIR)/$(APP_NAME)-darwin-arm64 $(MAIN_PATH) @echo "Built: $(BUILD_DIR)/$(APP_NAME)-darwin-{amd64,arm64}" -build-windows: - @echo "Building for Windows (static)..." - @mkdir -p $(BUILD_DIR) - $(CGO_DISABLE) GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -trimpath -o $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe $(MAIN_PATH) +build-windows: | $(BUILD_DIR) + @echo "Building for Windows amd64 (static)..." + $(CGO_DISABLE) GOOS=windows GOARCH=amd64 go build $(LDFLAGS) $(PERF_GCFLAGS) $(PERF_ASMFLAGS) -trimpath \ + -o $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe $(MAIN_PATH) @echo "Built: $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe" -# 构建所有平台 build-all: build-linux build-darwin build-windows @echo "All platform builds complete." @@ -123,17 +114,14 @@ build-all: build-linux build-darwin build-windows # 开发命令 # ============================================ -# 运行开发服务器 run: @echo "Running $(APP_NAME) in development mode..." go run $(MAIN_PATH) -c configs/lolly.yaml -# 测试配置 test-config: @echo "Testing configuration..." go run $(MAIN_PATH) -t -c configs/lolly.yaml -# 显示版本 version: @echo "$(APP_NAME) version $(VERSION)" @echo "Git: $(GIT_COMMIT) ($(GIT_BRANCH))" @@ -144,34 +132,28 @@ version: # 测试命令 # ============================================ -# 运行所有测试 test: @echo "Running tests..." go test -v ./internal/... -# 运行 L2 集成测试(无需 Docker) test-integration: @echo "Running L2 integration tests..." go test -v -tags=integration ./internal/integration/... -# 运行 L3 E2E 测试(需要 Docker) test-e2e: @echo "Running L3 E2E tests (parallel: $(or $(E2E_PARALLEL),4))..." go test -tags=e2e -parallel $(or $(E2E_PARALLEL),4) -count 1 ./internal/e2e/... -# 运行 L3 E2E 测试(带覆盖率) test-e2e-cover: @echo "Running L3 E2E tests with coverage..." go test -tags=e2e -coverprofile=e2e-coverage.out -coverpkg=./... ./internal/e2e/... go tool cover -html=e2e-coverage.out -o e2e-coverage.html @echo "E2E coverage report: e2e-coverage.html" -# 运行 L3 E2E 测试(短模式,仅运行工具测试) test-e2e-short: @echo "Running L3 E2E tests (short mode - testutil only)..." go test -tags=e2e -short -v ./internal/e2e/testutil/... -timeout 60s -# 运行所有测试(单元 + 集成 + E2E)— 并行执行 test-all: @echo "Running all tests in parallel..." @FAIL=0; \ @@ -184,14 +166,12 @@ test-all: if [ $$FAIL -eq 0 ]; then echo "All tests passed."; fi; \ exit $$FAIL -# 运行测试(带覆盖率) test-cover: @echo "Running tests with coverage..." - go test -v -coverprofile=coverage.out ./... + go test -v -coverprofile=coverage.out ./internal/... go tool cover -html=coverage.out -o coverage.html @echo "Coverage report: coverage.html" -# 运行 act 本地 CI 测试 act: @echo "Running CI locally with act..." @if command -v act >/dev/null 2>&1; then \ @@ -201,7 +181,6 @@ act: exit 1; \ fi -# 运行 act 单个 job act-unit: @echo "Running unit tests job with act..." @if command -v act >/dev/null 2>&1; then \ @@ -211,17 +190,18 @@ act-unit: exit 1; \ fi -# 运行基准测试(输出到文件,仅扫描 internal 目录) +# ============================================ +# 基准测试 +# ============================================ + bench: @echo "Running benchmarks..." go test -bench=. -benchmem -count=5 -run=^$$ ./internal/... | tee bench-results.txt -# 运行基准测试(统计模式,10次采样) bench-stat: @echo "Running benchmarks with statistical sampling..." - go test -bench=. -benchmem -count=10 ./... | tee benchmark-current.txt + go test -bench=. -benchmem -count=10 -run=^$$ ./internal/... | tee benchmark-current.txt -# 对比基准测试结果(需要 benchstat) bench-compare: @echo "Comparing benchmarks..." @if command -v benchstat >/dev/null 2>&1; then \ @@ -236,7 +216,6 @@ bench-compare: exit 1; \ fi -# 保存当前基准结果为基准线 bench-save: @echo "Saving benchmark baseline..." @if [ -f benchmark-current.txt ]; then \ @@ -248,25 +227,23 @@ bench-save: cp benchmark-current.txt benchmark-baseline.txt; \ fi -# 检查性能回归(需要 Python) bench-check: @echo "Checking for performance regressions..." - @if [ -f benchmark-comparison.txt ]; then \ - python scripts/check_regression.py benchmark-comparison.txt; \ - elif command -v benchstat >/dev/null 2>&1 && [ -f benchmark-baseline.txt ] && [ -f benchmark-current.txt ]; then \ - benchstat benchmark-baseline.txt benchmark-current.txt > benchmark-comparison.txt; \ - python scripts/check_regression.py benchmark-comparison.txt; \ - else \ - echo "需要 benchstat 和基准线/当前结果文件"; \ - echo "运行: make bench-save && make bench-stat && make bench-check"; \ + @if [ ! -f .benchmark-thresholds.yaml ]; then \ + echo "阈值配置文件不存在: .benchmark-thresholds.yaml"; \ + echo "创建示例: echo 'threshold: 10%%' > .benchmark-thresholds.yaml"; \ exit 1; \ fi + @if [ ! -f benchmark-baseline.txt ] || [ ! -f benchmark-current.txt ]; then \ + echo "缺少 baseline/current 数据,运行: make bench-save && make bench-stat"; \ + exit 1; \ + fi + benchstat benchmark-baseline.txt benchmark-current.txt | python3 scripts/check_regression.py - --config .benchmark-thresholds.yaml # ============================================ # 代码质量 # ============================================ -# 格式化代码(使用 goimports 替代 go fmt) fmt: @echo "Formatting code with gofumpt..." @if command -v gofumpt >/dev/null 2>&1; then \ @@ -276,7 +253,6 @@ fmt: exit 1; \ fi -# 静态检查 lint: @echo "Running linter..." @if command -v golangci-lint >/dev/null 2>&1; then \ @@ -286,7 +262,6 @@ lint: go vet ./...; \ fi -# 现代化检查(检测可使用新 Go 特性的代码) modernize: @echo "Running modernize analyzer..." @if command -v modernize >/dev/null 2>&1; then \ @@ -296,7 +271,6 @@ modernize: exit 1; \ fi -# 现代化检查并自动修复 modernize-fix: @echo "Running modernize analyzer with auto-fix..." @if command -v modernize >/dev/null 2>&1; then \ @@ -306,7 +280,6 @@ modernize-fix: exit 1; \ fi -# 代码检查 check: fmt lint test-all @echo "All checks passed." @@ -314,52 +287,65 @@ check: fmt lint test-all # 依赖管理 # ============================================ -# 下载依赖 deps: @echo "Downloading dependencies..." go mod download go mod tidy -# 更新依赖 update-deps: @echo "Updating dependencies..." - go get -u ./... + go get -u go mod tidy # ============================================ # 安装命令 # ============================================ -# 安装到系统 install: @echo "Installing $(APP_NAME)..." - go install $(LDFLAGS) $(MAIN_PATH) + $(CGO_DISABLE) go install $(LDFLAGS) $(PERF_GCFLAGS) $(PERF_ASMFLAGS) -trimpath $(MAIN_PATH) @echo "Installed to: $(shell go env GOPATH)/bin/$(APP_NAME)" # ============================================ # 清理命令 # ============================================ -# 清理构建产物 clean: @echo "Cleaning build artifacts..." rm -rf $(BUILD_DIR) rm -f coverage.out coverage.html + rm -f e2e-coverage.out e2e-coverage.html + rm -f bench-results.txt benchmark-current.txt benchmark-baseline.txt benchmark-comparison.txt go clean -cache -testcache - # go clean -modcache @echo "Clean complete." +clean-mod: + @echo "Cleaning module cache..." + go clean -modcache + @echo "Module cache cleaned." + # ============================================ # Docker 命令 # ============================================ -# 构建 Docker 镜像 -docker: +DOCKER_ARCH := $(shell go env GOARCH 2>/dev/null || echo "amd64") + +docker: | $(BUILD_DIR) @echo "Building Docker image..." - docker build --build-arg VERSION=$(VERSION) --build-arg GIT_COMMIT=$(shell git rev-parse HEAD 2>/dev/null || echo "unknown") --build-arg GIT_BRANCH=$(GIT_BRANCH) --build-arg BUILD_TIME='$(BUILD_TIME)' --build-arg GO_VERSION='$(GO_VERSION)' --build-arg BUILD_PLATFORM=linux/$(shell go env GOARCH 2>/dev/null || echo "amd64") --build-arg GOPROXY='$(shell go env GOPROXY)' --build-arg GOSUMDB='$(shell go env GOSUMDB)' -t $(APP_NAME):$(VERSION) -t $(APP_NAME):latest . + docker build \ + --build-arg VERSION=$(VERSION) \ + --build-arg GIT_COMMIT=$(shell git rev-parse HEAD 2>/dev/null || echo "unknown") \ + --build-arg GIT_BRANCH=$(GIT_BRANCH) \ + --build-arg BUILD_TIME='$(BUILD_TIME)' \ + --build-arg GO_VERSION='$(GO_VERSION)' \ + --build-arg BUILD_PLATFORM=linux/$(DOCKER_ARCH) \ + --build-arg GOPROXY='$(shell go env GOPROXY)' \ + --build-arg GOSUMDB='$(shell go env GOSUMDB)' \ + -t $(APP_NAME):$(VERSION) \ + -t $(APP_NAME):latest \ + . @echo "Docker image built: $(APP_NAME):$(VERSION), $(APP_NAME):latest" -# 推送 Docker 镜像(需要先 docker login) docker-push: @echo "Pushing Docker image..." @echo "Usage: make docker-push REGISTRY=" @@ -374,7 +360,6 @@ docker-push: docker push $(REGISTRY)/$(APP_NAME):latest @echo "Pushed to: $(REGISTRY)/$(APP_NAME):$(VERSION)" -# 清理 Docker 镜像 docker-clean: @echo "Cleaning Docker images..." docker rmi $(APP_NAME):$(VERSION) $(APP_NAME):latest 2>/dev/null || true @@ -389,8 +374,6 @@ help: @echo "" @echo "Build (static linked):" @echo " make build - Build for current platform" - @echo " make build-prod - Production build (size optimized)" - @echo " make build-perf - Production build (max runtime performance)" @echo " make build-pgo - PGO build (needs profile, use PGO_PROFILE=path)" @echo " make pgo-collect - Guide for collecting PGO profile" @echo " make build-all - Build for all platforms" @@ -404,8 +387,8 @@ help: @echo " make version - Show version info" @echo "" @echo "Testing:" - @echo " make test - Run all tests" - @echo " make test-cover - Run tests with coverage" + @echo " make test - Run unit tests" + @echo " make test-cover - Run unit tests with coverage" @echo " make test-integration - Run L2 integration tests" @echo " make test-e2e - Run L3 E2E tests (requires Docker)" @echo " make test-e2e-cover - Run E2E tests with coverage" @@ -413,18 +396,20 @@ help: @echo " make test-all - Run all tests (unit + integration + E2E)" @echo " make act - Run CI locally with act" @echo " make act-unit - Run unit tests job with act" + @echo "" + @echo "Benchmark:" @echo " make bench - Run benchmarks" @echo " make bench-stat - Run benchmarks with statistical sampling (10x)" @echo " make bench-compare - Compare against baseline (needs benchstat)" @echo " make bench-save - Save current results as baseline" - @echo " make bench-check - Check for performance regressions" + @echo " make bench-check - Check for performance regressions (with thresholds)" @echo "" @echo "Quality:" @echo " make fmt - Format code" @echo " make lint - Run linter" @echo " make modernize - Check for modern Go patterns" @echo " make modernize-fix - Auto-fix modern Go patterns" - @echo " make check - Format + lint + test" + @echo " make check - Format + lint + test-all" @echo "" @echo "Dependencies:" @echo " make deps - Download dependencies" @@ -438,21 +423,14 @@ help: @echo "Other:" @echo " make install - Install to GOPATH/bin" @echo " make clean - Clean build artifacts" + @echo " make clean-mod - Clean module cache" @echo "" -# 回归检测(使用阈值配置文件) -bench-regression: - @echo "Detecting performance regressions with thresholds..." - @if [ ! -f .benchmark-thresholds.yaml ]; then \ - echo "阈值配置文件不存在: .benchmark-thresholds.yaml"; \ - exit 1; \ - fi - @if [ -f bench-old.txt ] && [ -f bench-new.txt ]; then \ - benchstat bench-old.txt bench-new.txt | python3 scripts/check_regression.py - --config .benchmark-thresholds.yaml; \ - elif [ -f benchmark-baseline.txt ] && [ -f benchmark-current.txt ]; then \ - benchstat benchmark-baseline.txt benchmark-current.txt | python3 scripts/check_regression.py - --config .benchmark-thresholds.yaml; \ - else \ - echo "需要 bench-old.txt 和 bench-new.txt 或 baseline/current 文件"; \ - echo "运行: make bench-stat && mv benchmark-current.txt bench-old.txt && make bench-stat && mv benchmark-current.txt bench-new.txt"; \ - exit 1; \ - fi +.PHONY: build build-pgo pgo-collect build-linux build-darwin build-windows build-all \ + run test-config version \ + test test-integration test-e2e test-e2e-cover test-e2e-short test-all test-cover \ + act act-unit \ + bench bench-stat bench-compare bench-save bench-check \ + fmt lint modernize modernize-fix check \ + deps update-deps install clean clean-mod \ + docker docker-push docker-clean help