-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
351 lines (284 loc) · 14.5 KB
/
Makefile
File metadata and controls
351 lines (284 loc) · 14.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
.PHONY: help sqlc proto install-deps install-mocks generate-mocks generate-all test-unit graphql-init graphql-generate graphql-generate-module graphql-generate-all graphql-validate graphql-add graphql-from-proto validate-setup quickstart doctor format tidy pre-commit
.DEFAULT_GOAL := help
help: ## Show available commands
@echo "Available commands:"
@echo ""
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-30s\033[0m %s\n", $$1, $$2}'
install-deps: ## Install developer tools
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
go install github.com/bufbuild/buf/cmd/buf@latest
go install github.com/air-verse/air@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/99designs/gqlgen@latest
go install go.uber.org/mock/mockgen@latest
install-mocks: ## Install gomock for test mocking
go install go.uber.org/mock/mockgen@latest
validate-setup: ## Validate development environment setup
@./scripts/validate-setup.sh
quickstart: ## Run complete setup process (install deps, start docker, run migrations)
@./scripts/quickstart.sh
doctor: ## Run development environment diagnostics
@./scripts/doctor.sh
sqlc: ## Generate type-safe Go code from SQL
sqlc generate
proto: ## Generate gRPC code from protobuf definitions
buf generate
proto-version-create: ## Create a new API version for a module (usage: make proto-version-create MODULE_NAME=auth VERSION=v2)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make proto-version-create MODULE_NAME=<module_name> VERSION=<version>"; exit 1; fi
@if [ -z "$(VERSION)" ]; then echo "Usage: make proto-version-create MODULE_NAME=<module_name> VERSION=<version>"; exit 1; fi
@./scripts/proto-version-create.sh $(MODULE_NAME) $(VERSION)
@echo "✅ New version created. Run 'make proto' to generate code."
proto-breaking-check: ## Check for breaking changes in proto files (usage: make proto-breaking-check [MODULE_NAME=module_name])
@if [ -z "$(MODULE_NAME)" ]; then \
./scripts/proto-breaking-check.sh; \
else \
./scripts/proto-breaking-check.sh $(MODULE_NAME); \
fi
proto-lint: ## Lint all proto files
buf lint
generate-mocks: ## Generate all mocks from interfaces
@echo "Generating mocks..."
@go generate ./modules/...
@echo "Mocks generated successfully"
generate-all: sqlc proto generate-mocks ## Generate all code (sqlc + proto + mocks)
docker-up: ## Run docker-compose
docker-compose up -d
docker-up-minimal: ## Run docker-compose with minimal services (db + redis only)
docker-compose -f docker-compose.minimal.yaml up -d
test: ## Run tests
go test -v -race -cover ./...
test-unit: generate-mocks ## Run unit tests with fresh mocks
go test -v -short ./...
test-integration: ## Run integration tests (requires Docker)
@echo "Running integration tests with testcontainers..."
go test -v -run Integration ./...
test-all: test-unit test-integration ## Run all tests (unit + integration)
test-coverage: ## Run tests with coverage report
go test -v -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
coverage-report: ## Generate detailed coverage report
@echo "=== 📊 Coverage Total del Proyecto ==="
@echo ""
@go test ./... -coverprofile=coverage.out -covermode=atomic 2>&1 | grep "coverage:" | grep -v "0.0%" | grep -v "no test"
@echo ""
@echo "=== 📈 Resumen por Componente ==="
@go tool cover -func=coverage.out | grep -v "\.pb\.go" | grep -v "\.pb\.gw\.go" | grep -v "generated" | tail -20
@echo ""
@echo "=== 🎯 Coverage Total (sin código generado) ==="
@go tool cover -func=coverage.out | grep -v "\.pb\.go" | grep -v "\.pb\.gw\.go" | grep -v "generated" | grep -v "cmd/" | tail -1
@echo ""
@echo "💡 Para ver el reporte HTML completo: make test-coverage"
coverage-html: ## Open coverage report in browser
@go test ./... -coverprofile=coverage.out -covermode=atomic > /dev/null 2>&1
@go tool cover -html=coverage.out
lint: ## Run linter
golangci-lint run
format: ## Format code with gofmt (and goimports if available)
@echo "Formatting code with gofmt..."
@gofmt -w .
@if command -v goimports > /dev/null; then \
echo "Formatting imports with goimports..."; \
goimports -w .; \
else \
echo "💡 Tip: Install goimports for import formatting: go install golang.org/x/tools/cmd/goimports@latest"; \
fi
@echo "✅ Code formatted"
tidy: ## Tidy Go module dependencies
@echo "Tidying Go module dependencies..."
@go mod tidy
@echo "✅ Dependencies tidied"
pre-commit: format lint test-unit ## Run pre-commit checks (format + lint + test-unit)
docker-down: ## Stop docker-compose services
docker-compose down
# Load .env file
ifneq (,$(wildcard ./.env))
include .env
export
endif
# Database migrations are now handled by the modulith itself
# The server discovers and runs migrations for all registered modules
migrate-up: ## Run all module migrations (uses modulith's migration system)
@echo "🚀 Running migrations for all modules..."
go run cmd/server/main.go -migrate
migrate: migrate-up ## Alias for migrate-up
seed: ## Run seed data for all modules
@echo "🌱 Running seed data for all modules..."
go run cmd/server/main.go seed
admin: ## Run admin task (usage: make admin TASK=task_name)
@if [ -z "$(TASK)" ]; then echo "Usage: make admin TASK=task_name"; exit 1; fi
@echo "🔧 Running admin task: $(TASK)"
go run cmd/server/main.go admin $(TASK)
migrate-down: ## Rollback last migration for a specific module (usage: make migrate-down MODULE_NAME=auth)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make migrate-down MODULE_NAME=module_name"; exit 1; fi
@MIGRATIONS_DIR=modules/$(MODULE_NAME)/resources/db/migration; \
if [ ! -d "$$MIGRATIONS_DIR" ]; then \
echo "Error: Module '$(MODULE_NAME)' not found or has no migrations directory"; \
exit 1; \
fi; \
echo "⚠️ Rolling back last migration for module: $(MODULE_NAME)"; \
migrate -path $$MIGRATIONS_DIR -database "$(DB_DSN)" down 1
migrate-create: ## Create a new migration file for a module (usage: make migrate-create MODULE_NAME=auth NAME=add_users)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make migrate-create MODULE_NAME=module_name NAME=migration_name"; exit 1; fi
@if [ -z "$(NAME)" ]; then echo "Usage: make migrate-create MODULE_NAME=module_name NAME=migration_name"; exit 1; fi
@MIGRATIONS_DIR=modules/$(MODULE_NAME)/resources/db/migration; \
if [ ! -d "$$MIGRATIONS_DIR" ]; then \
echo "Error: Module '$(MODULE_NAME)' not found or has no migrations directory"; \
exit 1; \
fi; \
migrate create -ext sql -dir $$MIGRATIONS_DIR -seq $(NAME)
migrate-force: ## Force migration version to clean dirty state (usage: make migrate-force MODULE_NAME=auth VERSION=1)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make migrate-force MODULE_NAME=module_name VERSION=version_number"; exit 1; fi
@if [ -z "$(VERSION)" ]; then echo "Usage: make migrate-force MODULE_NAME=module_name VERSION=version_number"; exit 1; fi
@MIGRATIONS_DIR=modules/$(MODULE_NAME)/resources/db/migration; \
if [ ! -d "$$MIGRATIONS_DIR" ]; then \
echo "Error: Module '$(MODULE_NAME)' not found or has no migrations directory"; \
exit 1; \
fi; \
echo "⚠️ Forcing migration version $(VERSION) for module $(MODULE_NAME) (clears dirty state)..."; \
if echo "$(DB_DSN)" | grep -q "?"; then \
MODULE_DSN="$(DB_DSN)&x-migrations-table=$(MODULE_NAME)_schema_migrations"; \
else \
MODULE_DSN="$(DB_DSN)?x-migrations-table=$(MODULE_NAME)_schema_migrations"; \
fi; \
migrate -path $$MIGRATIONS_DIR -database "$$MODULE_DSN" force $(VERSION)
db-down: ## Rollback ALL migrations for all modules (drops all tables, uses modulith's migration system)
@echo "⚠️ WARNING: This will rollback ALL migrations for ALL modules (drops all tables)!"
@read -p "Are you sure? Type 'yes' to confirm: " confirm && [ "$$confirm" = "yes" ] || exit 1
@echo "🔄 Rolling back all migrations for all modules..."
@go run cmd/server/main.go migrate-down
db-reset: ## Drop all module schemas and re-run all migrations (destructive, asks for confirmation)
@./scripts/db-reset.sh
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_TIME ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
LDFLAGS := -X github.com/cmelgarejo/go-modulith-template/internal/version.Version=$(VERSION) \
-X github.com/cmelgarejo/go-modulith-template/internal/version.Commit=$(COMMIT) \
-X github.com/cmelgarejo/go-modulith-template/internal/version.BuildTime=$(BUILD_TIME)
build: ## Build the monolith binary
@mkdir -p bin
go build -ldflags "$(LDFLAGS)" -o bin/server ./cmd/server/main.go
build-module: ## Build a specific module binary (usage: make build-module MODULE_NAME)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make build-module MODULE_NAME"; exit 1; fi
@if [ ! -d "cmd/$(MODULE_NAME)" ]; then echo "Error: Module '$(MODULE_NAME)' not found in cmd/"; exit 1; fi
@mkdir -p bin
@echo "Building module: $(MODULE_NAME)"
go build -ldflags "$(LDFLAGS)" -o bin/$(MODULE_NAME) ./cmd/$(MODULE_NAME)/main.go
build-worker: ## Build the worker binary
@mkdir -p bin
go build -ldflags "$(LDFLAGS)" -o bin/worker ./cmd/worker/main.go
build-all: build build-worker ## Build all binaries (server + worker + all modules)
@mkdir -p bin
@for dir in cmd/*/; do \
module=$$(basename $$dir); \
if [ "$$module" != "server" ] && [ "$$module" != "worker" ]; then \
echo "Building module: $$module"; \
go build -ldflags "$(LDFLAGS)" -o bin/$$module ./cmd/$$module/main.go; \
fi \
done
clean: ## Clean build artifacts
rm -rf bin/
run: ## Run the monolith server (without hot reload)
go run -ldflags "$(LDFLAGS)" cmd/server/main.go || true
dev: ## Run the monolith with live reload (requires Air)
@./scripts/preflight-check.sh || exit 1
@if command -v air > /dev/null; then \
air -c .air.toml; \
else \
echo "Air is not installed. Please install it with: go install github.com/air-verse/air@latest"; \
fi
dev-worker: ## Run the worker with live reload (requires Air)
@./scripts/preflight-check.sh || exit 1
@if command -v air > /dev/null; then \
air -c .air.worker.toml; \
else \
echo "Air is not installed. Please install it with: go install github.com/air-verse/air@latest"; \
fi
dev-module: ## Run a specific module with live reload (usage: make dev-module MODULE_NAME)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make dev-module MODULE_NAME"; exit 1; fi
@if [ ! -f ".air.$(MODULE_NAME).toml" ]; then echo "Error: Air config '.air.$(MODULE_NAME).toml' not found"; exit 1; fi
@./scripts/preflight-check.sh || exit 1
@if command -v air > /dev/null; then \
echo "Starting module: $(MODULE_NAME) with hot reload..."; \
air -c .air.$(MODULE_NAME).toml; \
else \
echo "Air is not installed. Please install it with: go install github.com/air-verse/air@latest"; \
fi
# Handle positional arguments for new-module
ifeq (new-module,$(firstword $(MAKECMDGOALS)))
MODULE_NAME := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(MODULE_NAME):;@:)
endif
# Handle positional arguments for destroy-module
ifeq (destroy-module,$(firstword $(MAKECMDGOALS)))
MODULE_NAME := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(MODULE_NAME):;@:)
endif
# Handle positional arguments for build-module
ifeq (build-module,$(firstword $(MAKECMDGOALS)))
MODULE_NAME := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(MODULE_NAME):;@:)
endif
# Handle positional arguments for docker-build-module
ifeq (docker-build-module,$(firstword $(MAKECMDGOALS)))
MODULE_NAME := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(MODULE_NAME):;@:)
endif
# Handle positional arguments for dev-module
ifeq (dev-module,$(firstword $(MAKECMDGOALS)))
MODULE_NAME := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(MODULE_NAME):;@:)
endif
# Handle positional arguments for graphql-generate-module
ifeq (graphql-generate-module,$(firstword $(MAKECMDGOALS)))
MODULE_NAME := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(MODULE_NAME):;@:)
endif
##### Docker
docker-build: ## Build docker image for server
docker build \
--build-arg TARGET=server \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t modulith-server:latest .
docker-build-module: ## Build docker image for a specific module (usage: make docker-build-module MODULE_NAME)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make docker-build-module MODULE_NAME"; exit 1; fi
@if [ ! -d "cmd/$(MODULE_NAME)" ]; then echo "Error: Module '$(MODULE_NAME)' not found in cmd/"; exit 1; fi
@echo "Building Docker image for module: $(MODULE_NAME)"
docker build \
--build-arg TARGET=$(MODULE_NAME) \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t modulith-$(MODULE_NAME):latest .
##### Modules
new-module: ## Scaffold a new module (usage: make new-module MODULE_NAME)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make new-module NAME"; exit 1; fi
./scripts/scaffold-module.sh $(MODULE_NAME)
destroy-module: ## Destroy a module completely (usage: make destroy-module MODULE_NAME)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make destroy-module MODULE_NAME"; exit 1; fi
./scripts/destroy-module.sh $(MODULE_NAME)
##### GraphQL (Optional)
graphql-add: ## Add optional GraphQL support using gqlgen (automatically generates code)
./scripts/graphql-add-to-project.sh
graphql-init: ## Initialize GraphQL (alias for graphql-add)
$(MAKE) graphql-add
graphql-generate: graphql-generate-all ## Generate GraphQL code for all modules (alias for graphql-generate-all)
graphql-generate-module: ## Generate GraphQL code for a specific module (usage: make graphql-generate-module auth)
@if [ -z "$(MODULE_NAME)" ]; then echo "Usage: make graphql-generate-module <module_name>"; exit 1; fi
./scripts/graphql-generate-module.sh $(MODULE_NAME)
graphql-generate-all: ## Generate GraphQL code for all modules (auto-discovers modules with schemas)
./scripts/graphql-generate-all.sh
graphql-from-proto: ## Generate GraphQL schemas from OpenAPI/Swagger files for all modules
./scripts/graphql-from-proto-all.sh
graphql-validate: ## Validate GraphQL schema
visualize: ## Visualize module connections (usage: make visualize [FORMAT=html|json|dot] [SERVE=true])
@echo "🔍 Analyzing modulith architecture..."
@FORMAT=$${FORMAT:-html}; \
SERVE=$${SERVE:-false}; \
if [ "$$SERVE" = "true" ]; then \
go run ./cmd/visualize/main.go -format=$$FORMAT -serve; \
else \
go run ./cmd/visualize/main.go -format=$$FORMAT; \
fi