-
-
Notifications
You must be signed in to change notification settings - Fork 107
Expand file tree
/
Copy pathMakefile
More file actions
235 lines (199 loc) · 10.4 KB
/
Copy pathMakefile
File metadata and controls
235 lines (199 loc) · 10.4 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
.PHONY: all build run demo clean tidy mod-check frontend frontend-check frontend-stub test test-race test-dashboard test-e2e test-integration test-contract test-all lint lint-fix fix fix-check record-api swagger docs-openapi helm-lint install-tools perf-check perf-bench infra image seed-demo-data build-plugins image-plugins example-plugins
all: frontend build
# Get version info
VERSION ?= $(shell git describe --tags --always --dirty)
COMMIT ?= $(shell git rev-parse --short HEAD)
DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
DOCS_API_SERVERS ?= http://localhost:8080
LOG_LEVEL ?= debug
SWAGGER_ENABLED ?= true
# Build tags covering every file the linter and fixers must see. Without these,
# tag-gated files (tests/e2e, tests/integration, tests/contract) are skipped.
BUILD_TAGS ?= swagger,e2e,integration,contract
GOLANGCI_LINT_VERSION := 2.13.1
GOLANGCI_LINT ?= $(shell go env GOPATH)/bin/golangci-lint
# Linker flags to inject version info
LDFLAGS := -X "github.com/enterpilot/gomodel/internal/version.Version=$(VERSION)" \
-X "github.com/enterpilot/gomodel/internal/version.Commit=$(COMMIT)" \
-X "github.com/enterpilot/gomodel/internal/version.Date=$(DATE)"
install-tools:
@installed_version="$$($(GOLANGCI_LINT) version 2>/dev/null || true)"; \
case "$$installed_version" in \
*"version $(GOLANGCI_LINT_VERSION) "*) ;; \
*) echo "Installing golangci-lint v$(GOLANGCI_LINT_VERSION)..."; \
GOBIN="$(dir $(GOLANGCI_LINT))" go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v$(GOLANGCI_LINT_VERSION) ;; \
esac
@command -v pre-commit > /dev/null 2>&1 || (echo "Installing pre-commit..." && pip install pre-commit==4.5.1)
@echo "All tools are ready"
# Compiles the gateway. The dashboard must be built first (see `frontend`);
# the binary embeds internal/admin/dashboard/static/dist.
build: frontend
go build -ldflags '$(LDFLAGS)' -o bin/gomodel ./cmd/gomodel
# Run the application.
#
# Built and exec'd rather than `go run`: `go run` exits 1 when it is
# interrupted, even after the program it supervises shuts down cleanly, so
# Ctrl+C always ended in a bogus "make: *** [run] Error 1". exec replaces the
# recipe shell, which also puts the gateway directly under make's signal
# handling instead of behind a supervisor.
run:
go build -tags=swagger -ldflags '$(LDFLAGS)' -o bin/gomodel ./cmd/gomodel
LOG_LEVEL="$(LOG_LEVEL)" SWAGGER_ENABLED="$(SWAGGER_ENABLED)" exec ./bin/gomodel
# Seed the local SQLite database and start GoModel with a populated dashboard.
# Guardrails (which imply plugins) are on so the seeded guardrail instances and
# the workflows referencing them are live rather than capped off at runtime.
# Audit retention is raised to the seeded window: the 30-day default would
# delete two thirds of the demo audit log on the first startup sweep.
# Exported so the seeder reads the same window as the retention settings: a
# maintainer changing this default must not leave them out of step.
export DEMO_DAYS ?= 90
demo: seed-demo-data
$(MAKE) run GOMODEL_DEMO_MODE=true GUARDRAILS_ENABLED=true \
LOGGING_RETENTION_DAYS=$(DEMO_DAYS) USAGE_RETENTION_DAYS=$(DEMO_DAYS)
# Clean build artifacts
clean:
rm -rf bin/
# Tidy dependencies
tidy:
go mod tidy
# Verify dependencies are tidy and match downloaded module checksums without
# mutating the working tree. CI and release builds use this target.
mod-check:
go mod tidy -diff
go mod verify
# Docker Compose: Redis, PostgreSQL, MongoDB, Adminer (no app image build)
infra:
docker compose up -d
# Docker Compose: full stack (GoModel + Prometheus; builds app image when needed)
image: frontend
docker compose --profile app up -d
# Shared-object plugin support (Go's plugin package) needs a cgo-enabled
# binary; the default `build` and the release binaries are static. These
# targets produce the cgo variants. Plugins must be built with the same Go
# toolchain and flags as the binary that loads them: `gomodel plugin build`
# copies the flags of the gomodel binary that runs it.
build-plugins: frontend
CGO_ENABLED=1 go build -ldflags '$(LDFLAGS)' -o bin/gomodel-plugins ./cmd/gomodel
# Docker image with plugin support (Dockerfile.plugins). Tag: gomodel:<version>-plugins.
image-plugins: frontend
docker build -f Dockerfile.plugins -t gomodel:$(VERSION)-plugins -t gomodel:plugins \
--build-arg VERSION=$(VERSION) --build-arg COMMIT=$(COMMIT) --build-arg DATE=$(DATE) .
# Build every example plugin under docs/example_plugins into ./plugins/<name>.so
# using the toolchain of this checkout (matches `make build-plugins`).
example-plugins:
@mkdir -p plugins
@for dir in docs/example_plugins/*/; do \
name=$$(basename $$dir); \
echo "building $$dir -> plugins/$$name.so"; \
go run ./cmd/gomodel plugin build -o plugins/$$name.so $$dir || exit 1; \
done
# Seed rolling demo telemetry and dashboard configuration into SQLite.
# Usage: SQLITE_PATH=data/gomodel.db make seed-demo-data
seed-demo-data:
bash tools/seed-demo-data.sh
# Run unit tests only
test: frontend-check
go test ./cmd/... ./config/... ./ext/... ./internal/... ./run/... -v
# Run unit tests with race detection and coverage
test-race: frontend-check
go test -v -race -coverprofile=coverage.out ./cmd/... ./config/... ./ext/... ./internal/... ./run/...
# Build the Svelte dashboard into internal/admin/dashboard/static/dist, which
# the Go binary embeds. The output is not committed: CI builds it in a
# secretless job (docs/adr/0010-dashboard-built-in-ci.md). --ignore-scripts
# keeps npm lifecycle scripts from running; the build does not need them.
frontend:
cd web/dashboard && npm ci --no-audit --no-fund --ignore-scripts && npm run build
# The Go suites embed the dashboard, so they need a build in place. This only
# checks; it does not build, because CI supplies static/dist as an artifact
# from the secretless `frontend` job and must not rebuild it here.
frontend-check:
@test -f internal/admin/dashboard/static/dist/index.html || { \
echo "internal/admin/dashboard/static/dist is missing: run 'make frontend' first." >&2; exit 1; }
# Write a placeholder bundle so the Go suites compile and pass without Node.
# CI test jobs use this instead of waiting for the `frontend` job; the Build
# job and releases still embed the real bundle. A no-op when a bundle exists.
frontend-stub:
sh tools/frontend-stub.sh
# Run dashboard JavaScript unit tests
test-dashboard:
cd web/dashboard && npm test
# Run e2e tests (uses an in-process mock LLM server; no Docker required)
test-e2e: frontend-check
go test -v -tags=e2e ./tests/e2e/...
# Run integration tests (requires Docker)
test-integration:
go test -v -tags=integration -timeout=10m ./tests/integration/...
# Run contract tests (validates API response structures against golden files)
test-contract:
go test -v -tags=contract -timeout=5m ./tests/contract/...
# Run all tests including dashboard, e2e, integration, and contract tests
test-all: test test-dashboard test-e2e test-integration test-contract
# Every guard in tests/perf, with no -run filter: the name list this used to
# carry had to be edited by hand for each new guard, and silently left the ones
# nobody remembered to add (TestFormatPerfGuardResult,
# TestSessionIDVisibilityByBodySize) running nowhere. A guard that CI does not
# execute is worse than no guard, because it reads as covered.
perf-check:
go test -count=1 -v ./tests/perf/...
perf-bench:
go test -bench=. -benchmem ./tests/perf/...
# Record API responses for contract tests
# Usage: OPENAI_API_KEY=sk-xxx make record-api
record-api:
@echo "Recording OpenAI chat completion..."
go run ./cmd/recordapi -provider=openai -endpoint=chat \
-output=tests/contract/testdata/openai/chat_completion.json
@echo "Recording OpenAI models..."
go run ./cmd/recordapi -provider=openai -endpoint=models \
-output=tests/contract/testdata/openai/models.json
@echo "Done! Golden files saved to tests/contract/testdata/"
swagger:
go run github.com/swaggo/swag/v2/cmd/swag init --generalInfo main.go \
--dir cmd/gomodel,internal \
--output cmd/gomodel/docs \
--outputTypes go \
--parseDependency
@command -v node >/dev/null 2>&1 || { echo "node is required to build docs; install from https://nodejs.org" >&2; exit 1; }
node tools/swagger-postprocess.mjs cmd/gomodel/docs/docs.go
$(MAKE) docs-openapi
docs-openapi:
@command -v node >/dev/null 2>&1 || { echo "node is required to build docs; install from https://nodejs.org" >&2; exit 1; }
@command -v npx >/dev/null 2>&1 || { echo "npx is required; install npm (includes npx)" >&2; exit 1; }
@tmp_dir=$$(mktemp -d); \
trap 'rm -rf "$$tmp_dir"' EXIT; \
go run github.com/swaggo/swag/v2/cmd/swag init --quiet --generalInfo main.go \
--dir cmd/gomodel,internal \
--output "$$tmp_dir" \
--outputTypes json \
--parseDependency; \
npx -y swagger2openapi@7.0.8 --patch -o docs/openapi.json "$$tmp_dir/swagger.json"; \
DOCS_API_SERVERS="$(DOCS_API_SERVERS)" node tools/openapi-postprocess.mjs docs/openapi.json
# Run linter
lint:
$(GOLANGCI_LINT) run --build-tags=$(BUILD_TAGS) ./cmd/... ./config/... ./ext/... ./internal/... ./run/... ./tests/...
# Lint the Helm chart with every CI values profile and validate the rendered
# manifests against the Kubernetes schemas (requires helm and kubeconform).
helm-lint:
helm lint --strict helm
for values in helm/ci/*-values.yaml; do helm lint --strict helm -f "$$values"; done
rendered=$$(mktemp); trap 'rm -f "$$rendered"' EXIT; \
for values in helm/ci/*-values.yaml; do \
helm template gomodel helm -f "$$values" --namespace gomodel > "$$rendered" || exit 1; \
kubeconform -strict -summary -schema-location default -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' < "$$rendered" || exit 1; \
done
# Run linter with auto-fix. Mirrors `lint`: same tags, same packages, so the
# autofix pass cannot silently skip the tag-gated files under tests/.
lint-fix:
$(GOLANGCI_LINT) run --fix --build-tags=$(BUILD_TAGS) ./cmd/... ./config/... ./ext/... ./internal/... ./run/... ./tests/...
# Report modernizations go fix would apply, without touching the tree.
# Exits non-zero when the tree has drifted; run `make fix` to apply.
fix-check:
go fix -diff -tags=$(BUILD_TAGS) ./...
# Apply go fix modernizations in place.
#
# Not idempotent in one pass: when go fix marks a helper `//go:fix inline` it
# inlines the callers on the *next* run, which can leave the helper orphaned.
# Re-run until `make fix-check` is clean, then delete any helper `make lint`
# now reports as unused.
fix:
go fix -tags=$(BUILD_TAGS) ./...