-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathMakefile
More file actions
538 lines (458 loc) · 24.9 KB
/
Copy pathMakefile
File metadata and controls
538 lines (458 loc) · 24.9 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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
.DEFAULT_GOAL := help
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
# Source archive builds supply COMMIT=<full source revision> on the command line.
COMMIT := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
LDFLAGS := -X main.version=$(VERSION) \
-X main.commit=$(COMMIT) \
-X main.buildDate=$(BUILD_DATE)
LDFLAGS_RELEASE := $(LDFLAGS) -s -w
EXE_SUFFIX := $(if $(filter windows,$(shell go env GOOS)),.exe,)
BINARY := kenn-forge$(EXE_SUFFIX)
GHAPP_BINARY := kenn-forge-github-app$(EXE_SUFFIX)
GOPATH_FIRST := $(shell go env GOPATH | sed -E 's/^([A-Za-z]:)?([^;:]*).*/\1\2/')
ROBOREV_SRC ?= $(HOME)/code/roborev
ROBOREV_REF ?= main
HUMA_CHECK_VERSION := 3e1f59e9011e878ec595aa04aebc8a77c5292c4d
# golangci-lint is pinned in mise.toml and used to build ./custom-gcl with
# the kit analyzers. check-mise fails with an install hint when mise is absent.
GOLANGCI := mise exec -- golangci-lint
GOLANGCI_LINT_VERSION ?= v2.13.2
GOLANGCI_LINT_CACHE ?= $(CURDIR)/.golangci-cache
export GOLANGCI_LINT_CACHE
CUSTOM_GCL := ./custom-gcl
KIT_MODULE := go.kenn.io/kit
KIT_VERSION := $(shell go list -m -f '{{.Version}}' $(KIT_MODULE))
KENNLINT := go run $(KIT_MODULE)/cmd/kennlint@$(KIT_VERSION)
AIR_BIN := $(shell if command -v air >/dev/null 2>&1; then command -v air; \
elif [ -n "$$(go env GOBIN)" ] && [ -x "$$(go env GOBIN)/air$(EXE_SUFFIX)" ]; then printf "%s" "$$(go env GOBIN)/air$(EXE_SUFFIX)"; \
elif [ -x "$(GOPATH_FIRST)/bin/air$(EXE_SUFFIX)" ]; then printf "%s" "$(GOPATH_FIRST)/bin/air$(EXE_SUFFIX)"; \
fi)
NILAWAY_BIN := $(shell if command -v nilaway >/dev/null 2>&1; then command -v nilaway; \
elif [ -n "$$(go env GOBIN)" ] && [ -x "$$(go env GOBIN)/nilaway$(EXE_SUFFIX)" ]; then printf "%s" "$$(go env GOBIN)/nilaway$(EXE_SUFFIX)"; \
elif [ -x "$(GOPATH_FIRST)/bin/nilaway$(EXE_SUFFIX)" ]; then printf "%s" "$(GOPATH_FIRST)/bin/nilaway$(EXE_SUFFIX)"; \
fi)
DEV_LOG_DIR ?= tmp/logs
DEV_BACKEND_LOG ?= $(DEV_LOG_DIR)/backend-dev.log
VITE_PLUS_VERSION := 0.1.24
VITE_PLUS_BIN := node ./node_modules/vite-plus/bin/vp
VITE_PLUS_FRONTEND_BIN := node ../node_modules/vite-plus/bin/vp
VITE_PLUS_PACKAGE_BIN := node ../../node_modules/vite-plus/bin/vp
DEV_CLONE_BACKEND_LOG ?= $(DEV_LOG_DIR)/backend-dev-clone.log
DEV_CLONE_DB_DIR ?= tmp/dev-db-clone
DEV_CLONE_PORT ?= 8092
DEV_CLONE_FRONTEND_PORT ?= 5175
.PHONY: ensure-embed-dir ensure-tmp-dir check-air air-install build build-release install \
rust-pty-manager rust-test vite-plus-install frontend-deps check-vite-plus-bin frontend githubapp-frontend frontend-dev frontend-dev-bun frontend-check frontend-check-no-deps frontend-check-core-no-deps frontend-effect-diagnostics api-generate roborev-api-generate \
docs-build docs-check docs-screenshots docs-vercel-build docs-branding-check docs-deploy-staging docs-deploy \
dev dev-ephemeral dev-ephemeral-stop test test-short test-integration test-e2e test-e2e-roborev huma-check test-fleet-container test-fleet-drive-container test-gitlab-container gitlab-fixture-bake vet check-mise lint lint-check lint-config lint-config-check custom-gcl fmt fmt-check nilaway \
profile-workspace-switch otel-lgtm \
frontend-api-client-check font-size-token-check huma-route-check migration-history-check timing-budget-check playwright-version-check script-tests guardrail-check race-times tidy svelte-skills svelte-skills-sync clean install-hooks help \
dev-clone-db frontend-dev-clone-db
# gotestsum prints package names on success and full output on failure,
# while persisting raw `go test -json` events for downstream reporters.
GOTESTSUM := go tool gotestsum --format pkgname-and-test-fails --jsonfile
GO_TEST_P ?= 4
GO_TEST_P_FLAG := $(if $(GO_TEST_P),-p $(GO_TEST_P),)
# Ensure go:embed has at least one file (no-op if frontend is built)
ensure-embed-dir:
@mkdir -p internal/web/dist
@test -n "$$(ls internal/web/dist/ 2>/dev/null)" \
|| echo ok > internal/web/dist/stub.html
@mkdir -p internal/githubapp/ui/dist
@test -n "$$(ls internal/githubapp/ui/dist/ 2>/dev/null)" \
|| echo ok > internal/githubapp/ui/dist/stub.html
# Ensure tmp/ exists so gotestsum can write JSON output there
ensure-tmp-dir:
@mkdir -p tmp
# Build the binary (debug, with embedded frontend)
build: frontend githubapp-frontend
go build -ldflags="$(LDFLAGS)" -o $(BINARY) ./cmd/kenn-forge
go build -ldflags="$(LDFLAGS)" -o $(GHAPP_BINARY) ./cmd/kenn-forge-github-app
# The relay has no frontend dependency.
.PHONY: build-relay
build-relay:
go build -ldflags="$(LDFLAGS)" -o tmp/kenn-forge-relay ./cmd/kenn-forge-relay
# Build with optimizations (release)
build-release: frontend githubapp-frontend
@printf '%s\n' "$(COMMIT)" | grep -Eq '^[0-9a-f]{40}$$' || \
{ echo 'Release builds require COMMIT=<full source revision>.' >&2; exit 1; }
go build -ldflags="$(LDFLAGS_RELEASE)" -trimpath -o $(BINARY) ./cmd/kenn-forge
go build -ldflags="$(LDFLAGS_RELEASE)" -trimpath -o $(GHAPP_BINARY) ./cmd/kenn-forge-github-app
rust-pty-manager:
cargo build -p kenn-forge-pty-manager
rust-test:
cargo test -p kenn-forge-pty-manager
# Install to ~/.local/bin, $GOBIN, or $GOPATH/bin
install: build-release
@if [ -d "$(HOME)/.local/bin" ]; then \
echo "Installing to ~/.local/bin/$(BINARY)"; \
cp $(BINARY) "$(HOME)/.local/bin/$(BINARY)"; \
cp $(GHAPP_BINARY) "$(HOME)/.local/bin/$(GHAPP_BINARY)"; \
else \
INSTALL_DIR="$${GOBIN:-$$(go env GOBIN)}"; \
if [ -z "$$INSTALL_DIR" ]; then \
INSTALL_DIR="$(GOPATH_FIRST)/bin"; \
fi; \
mkdir -p "$$INSTALL_DIR"; \
echo "Installing to $$INSTALL_DIR/$(BINARY)"; \
cp $(BINARY) "$$INSTALL_DIR/$(BINARY)"; \
cp $(GHAPP_BINARY) "$$INSTALL_DIR/$(GHAPP_BINARY)"; \
fi
# Install Bun workspace dependencies for the frontend packages
frontend-deps:
bun install
check-vite-plus-bin:
@test -f node_modules/vite-plus/bin/vp || { echo "vite-plus is not installed; run make frontend-deps" >&2; exit 1; }
# Install the global Vite+ launcher when it is not already on PATH.
vite-plus-install:
@if command -v vp >/dev/null 2>&1; then \
vp --version | head -n 1; \
else \
echo "vp not found; installing vite-plus@$(VITE_PLUS_VERSION) with Bun"; \
bun install -g vite-plus@$(VITE_PLUS_VERSION); \
fi
# Build frontend SPA and copy into embed directory
frontend: frontend-deps
cd frontend && $(VITE_PLUS_FRONTEND_BIN) build --logLevel warn
node scripts/check-asset-base-paths.mjs
rm -rf internal/web/dist
cp -r frontend/dist internal/web/dist
printf 'ok\n' > internal/web/dist/stub.html
# Build the GitHub App setup page and copy into its embed directory
githubapp-frontend: frontend-deps
cd packages/github-app-ui && $(VITE_PLUS_PACKAGE_BIN) build --logLevel warn
rm -rf internal/githubapp/ui/dist
cp -r packages/github-app-ui/dist internal/githubapp/ui/dist
printf 'ok\n' > internal/githubapp/ui/dist/stub.html
# Run Vite dev server with dependencies installed (use alongside `make dev`)
frontend-dev:
./scripts/frontend-dev.sh $(ARGS)
# Clone the configured database into this worktree and run the backend against it.
# Override with DEV_CLONE_DB_DIR=... DEV_CLONE_PORT=... KENN_FORGE_CONFIG=...
dev-clone-db: ensure-embed-dir check-air
@clone_config="$$(KENN_FORGE_DEV_CLONE_DIR="$(abspath $(DEV_CLONE_DB_DIR))" KENN_FORGE_DEV_CLONE_PORT="$(DEV_CLONE_PORT)" ./scripts/dev-clone-db.sh)"; \
echo "cloned dev config: $$clone_config"; \
echo "cloned dev database: $(abspath $(DEV_CLONE_DB_DIR))/forge.db"; \
echo "backend URL: http://127.0.0.1:$(DEV_CLONE_PORT)"; \
KENN_FORGE_CONFIG="$$clone_config" KENN_FORGE_LOG_FILE="$${KENN_FORGE_LOG_FILE:-$(DEV_CLONE_BACKEND_LOG)}" GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" $(MAKE) dev
# Run Vite against the cloned database backend from `make dev-clone-db`.
frontend-dev-clone-db:
@clone_config="$(abspath $(DEV_CLONE_DB_DIR))/config.toml"; \
if [ ! -f "$$clone_config" ]; then \
clone_config="$$(KENN_FORGE_DEV_CLONE_DIR="$(abspath $(DEV_CLONE_DB_DIR))" KENN_FORGE_DEV_CLONE_PORT="$(DEV_CLONE_PORT)" ./scripts/dev-clone-db.sh)"; \
fi; \
echo "frontend proxy config: $$clone_config"; \
KENN_FORGE_CONFIG="$$clone_config" $(MAKE) frontend-dev ARGS="$${ARGS:---port $(DEV_CLONE_FRONTEND_PORT)}"
# Run Vite+ dev server after installing dependencies with Bun; Node launches Vite+ (use alongside `make dev`)
frontend-dev-bun: frontend-deps
cd frontend && $(VITE_PLUS_FRONTEND_BIN) dev
# Run TypeScript/Svelte lint and type checks
frontend-check: frontend-deps
$(MAKE) frontend-check-no-deps
# Same checks without the bun install prerequisite. CI and explicit local
# checks retain the full-project Effect diagnostics.
frontend-check-no-deps: frontend-check-core-no-deps
$(MAKE) frontend-effect-diagnostics
# The pre-commit hook uses this core target because frontend-deps already
# installed dependencies and full-project Effect diagnostics are retained in
# CI instead of blocking every local commit.
frontend-check-core-no-deps: check-vite-plus-bin
node scripts/check-dev-auth-proxy.mjs
$(VITE_PLUS_BIN) fmt --check frontend packages/github-app-ui --no-error-on-unmatched-pattern --threads=1
$(VITE_PLUS_BIN) lint frontend packages/github-app-ui '!frontend/dist/**' '!packages/github-app-ui/dist/**' '!frontend/test-results/**' '!packages/github-app-ui/test-results/**' '!frontend/src/lib/api/generated/**' '!frontend/src/lib/api/roborev/generated/**' --no-error-on-unmatched-pattern --threads=1
cd frontend && node node_modules/@kenn-io/kit-ui/bin/kit-ui-check.mjs src
$(VITE_PLUS_BIN) run svelte-check
frontend-effect-diagnostics: check-vite-plus-bin
cd frontend && NODE_PATH=../node_modules node node_modules/@effect/language-service/cli.js diagnostics --project tsconfig.json --format text --severity error
# Build the public documentation from the published screenshot assets.
docs-build:
node scripts/build-docs.mjs
# Verify the rendered site independently from its static build.
docs-check: frontend-deps docs-build
node scripts/verify-docs-site.mjs
# Generate the complete screenshot set for publication on docs-assets.
docs-screenshots: frontend-deps
node scripts/generate-docs-screenshots.mjs $(OUTPUT_DIR)
# Reproduce Vercel's remote static docs build after its install command has
# populated the repository-local uv tool directory.
docs-vercel-build:
bash scripts/vercel-build-docs.sh
docs-branding-check: check-vite-plus-bin
$(VITE_PLUS_BIN) exec -- node scripts/check-docs-branding.mjs
docs-deploy-staging:
vercel deploy
docs-deploy:
vercel deploy --prod
# Prevent production frontend code from bypassing generated API clients
frontend-api-client-check: check-vite-plus-bin
$(VITE_PLUS_BIN) exec -- node scripts/lint-api-urls.mjs
# Ensure frontend font sizes use design tokens
font-size-token-check: check-vite-plus-bin
$(VITE_PLUS_BIN) exec -- node scripts/check-font-size-tokens.ts
# Prevent application HTTP routes from bypassing Huma registration
huma-route-check:
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" go run ./tools/nohttpmux ./...
# Keep the browser CI image in lockstep with the Playwright, Bun, and Vite+ pins
playwright-version-check: check-vite-plus-bin
$(VITE_PLUS_BIN) exec -- node scripts/check-playwright-version.mjs
# Run lightweight script regression tests
script-tests: check-vite-plus-bin
$(VITE_PLUS_BIN) exec -- node --test scripts/*.test.mjs scripts/*.test.ts
python3 -m unittest discover -s scripts -p 'test_*.py'
# Run lightweight generated-client/Huma guardrails.
# Guard on vite-plus being present (check-vite-plus-bin) rather than running
# frontend-deps: every sub-target either is Go-only or runs through
# `vp exec -- node`, so this needs node_modules + vp, never a standalone `bun`.
# Under setup-vp (CI) bun is not on PATH, so a `bun install` prerequisite here
# fails with exit 127 even though deps are already installed.
migration-history-check:
go run ./tools/migrationhistorycheck
# Reject unreviewed sub-second test polling budgets.
timing-budget-check:
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" go run ./tools/timingbudgetcheck .
guardrail-check: check-vite-plus-bin
$(MAKE) frontend-api-client-check font-size-token-check huma-route-check migration-history-check playwright-version-check script-tests docs-branding-check timing-budget-check
# Regenerate the checked-in OpenAPI document and generated clients
api-generate: frontend-deps
set -e; tmp="$$(mktemp)"; trap 'rm -f "$$tmp"' EXIT; go run ./cmd/kenn-forge-openapi -out "$$tmp" -format yaml; if [ -f frontend/openapi/openapi.yaml ] && cmp -s "$$tmp" frontend/openapi/openapi.yaml; then rm "$$tmp"; else mv "$$tmp" frontend/openapi/openapi.yaml; fi; trap - EXIT
cd frontend && $(VITE_PLUS_FRONTEND_BIN) build --logLevel warn
go run ./cmd/kenn-forge-openapi -api health -out internal/apiclient/health/openapi.yaml
go run ./cmd/kenn-forge-openapi -api devbox -out internal/apiclient/devbox/openapi.yaml
go generate ./internal/apiclient/...
# Regenerate the roborev TypeScript client from the checked-in OpenAPI spec
roborev-api-generate: frontend-deps
cd frontend && node node_modules/orval/dist/bin/orval.mjs --config orval.roborev.config.ts
$(VITE_PLUS_BIN) fmt frontend/src/lib/api/roborev/generated-next --write
rm -rf frontend/src/lib/api/roborev/generated
mv frontend/src/lib/api/roborev/generated-next frontend/src/lib/api/roborev/generated
go generate ./internal/apiclient/roborev
# Ensure air is installed for backend live reload
check-air:
@if [ -z "$(AIR_BIN)" ]; then \
echo "air not found. Install with: make air-install" >&2; \
exit 1; \
fi
# Install air for backend live reload
air-install:
go install github.com/air-verse/air@latest
# Run Go server in dev mode with live reload and API artifact refresh (use alongside `make frontend-dev`)
dev: ensure-embed-dir check-air
@mkdir -p "$(DEV_LOG_DIR)"
@echo "backend debug log: $${KENN_FORGE_LOG_FILE:-$(DEV_BACKEND_LOG)}"
@echo "backend console log level: $${KENN_FORGE_LOG_STDERR_LEVEL:-info}"
@echo "tail with: tail -F $${KENN_FORGE_LOG_FILE:-$(DEV_BACKEND_LOG)}"
@if [ -n "$(KENN_FORGE_CONFIG)" ]; then \
KENN_FORGE_DEV_RESTART=1 \
KENN_FORGE_LOG_LEVEL="$${KENN_FORGE_LOG_LEVEL:-debug}" \
KENN_FORGE_LOG_FILE="$${KENN_FORGE_LOG_FILE:-$(DEV_BACKEND_LOG)}" \
KENN_FORGE_LOG_STDERR_LEVEL="$${KENN_FORGE_LOG_STDERR_LEVEL:-info}" \
"$(AIR_BIN)" -c .air.toml -- serve -config "$(KENN_FORGE_CONFIG)" $(ARGS); \
else \
KENN_FORGE_DEV_RESTART=1 \
KENN_FORGE_LOG_LEVEL="$${KENN_FORGE_LOG_LEVEL:-debug}" \
KENN_FORGE_LOG_FILE="$${KENN_FORGE_LOG_FILE:-$(DEV_BACKEND_LOG)}" \
KENN_FORGE_LOG_STDERR_LEVEL="$${KENN_FORGE_LOG_STDERR_LEVEL:-info}" \
"$(AIR_BIN)" -c .air.toml -- serve $(ARGS); \
fi
# Run backend and frontend dev servers on free ports with isolated config/data and sync disabled.
dev-ephemeral: ensure-embed-dir ensure-tmp-dir
go run ./tools/devephemeral $(ARGS)
# Stop an ephemeral dev stack from its status JSON.
dev-ephemeral-stop:
if [ -n "$$STATUS" ]; then \
go run ./tools/devephemeral -stop -status "$$STATUS" $(ARGS); \
else \
go run ./tools/devephemeral -stop $(ARGS); \
fi
# Run tests
test: ensure-embed-dir ensure-tmp-dir
$(GOTESTSUM)=tmp/test-output.json -- $(GO_TEST_P_FLAG) ./... -shuffle=on
# Run fast tests only
test-short: ensure-embed-dir ensure-tmp-dir
$(GOTESTSUM)=tmp/test-short-output.json -- $(GO_TEST_P_FLAG) ./... -short -shuffle=on
# Run integration tests that execute real git commands (excluded from test-short)
test-integration: ensure-embed-dir ensure-tmp-dir
$(GOTESTSUM)=tmp/test-integration-output.json -- $(GO_TEST_P_FLAG) -tags integration ./... -run '^TestIntegration' -shuffle=on
# Report per-package wall time for the slow race-test packages.
race-times: ensure-embed-dir
./scripts/test-package-times.sh
# Run full-stack E2E tests (Playwright against real Go server, excludes roborev)
test-e2e: frontend
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" go build -o ./cmd/e2e-server/e2e-server$(EXE_SUFFIX) ./cmd/e2e-server
$(VITE_PLUS_BIN) run kenn-forge-frontend#test:e2e --project=chromium
cd packages/github-app-ui && $(VITE_PLUS_PACKAGE_BIN) build --logLevel warn && node node_modules/.bin/playwright test
# Capture a reproducible workspace-switch profile: warm/cold switch
# timings, a Chromium trace, and a Go execution trace from the seeded
# e2e backend. See frontend/tests/profiling/README.md.
profile-workspace-switch: frontend
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" go build -o ./cmd/e2e-server/e2e-server$(EXE_SUFFIX) ./cmd/e2e-server
$(VITE_PLUS_BIN) run kenn-forge-frontend#profile:workspace-switch
# Run the local all-in-one OTLP collector + Grafana/Tempo UI for
# kenn-forge trace export. See frontend/tests/profiling/README.md.
otel-lgtm:
docker run --rm -ti -p 3000:3000 -p 4317:4317 -p 4318:4318 grafana/otel-lgtm
# Run roborev e2e tests with Docker (ROBOREV_SRC, ROBOREV_REF, ROBOREV_PORT configurable)
test-e2e-roborev:
ROBOREV_SRC="$(ROBOREV_SRC)" ROBOREV_REF="$(ROBOREV_REF)" \
./scripts/run-roborev-e2e.sh
# Run opt-in fleet federation container tests.
test-fleet-container: ensure-embed-dir ensure-tmp-dir
@if [ "$${KENN_FORGE_FLEET_CONTAINER_E2E:-}" != "1" ]; then \
echo "Set KENN_FORGE_FLEET_CONTAINER_E2E=1 to run the fleet container e2e fixture." >&2; \
exit 1; \
fi
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" $(GOTESTSUM)=tmp/test-fleet-container-output.json -- ./internal/server/fleetapi -run TestFleetContainerReadE2E -shuffle=on -timeout 10m
# Run opt-in fleet drive container tests.
test-fleet-drive-container: ensure-embed-dir ensure-tmp-dir
@if [ "$${KENN_FORGE_FLEET_DRIVE_CONTAINER_E2E:-}" != "1" ]; then \
echo "Set KENN_FORGE_FLEET_DRIVE_CONTAINER_E2E=1 to run the fleet drive container e2e fixture." >&2; \
exit 1; \
fi
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" $(GOTESTSUM)=tmp/test-fleet-drive-container-output.json -- ./internal/server/fleetapi -run TestFleetContainerDriveE2E -shuffle=on -timeout 10m
# Run opt-in GitLab CE container compatibility tests.
test-gitlab-container: ensure-embed-dir ensure-tmp-dir
@if [ "$${KENN_FORGE_GITLAB_CONTAINER_E2E:-}" != "1" ]; then \
echo "Set KENN_FORGE_GITLAB_CONTAINER_E2E=1 to run the GitLab CE container e2e fixture." >&2; \
exit 1; \
fi
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" $(GOTESTSUM)=tmp/test-gitlab-container-output.json -- ./internal/server -run TestGitLabContainerE2E -shuffle=on -timeout 40m
# Build a reusable GitLab fixture image from the idempotent bootstrap script.
gitlab-fixture-bake:
./scripts/e2e/gitlab/bake-fixture-image.sh
# Vet
vet: ensure-embed-dir
go vet ./...
# Verify mise is available for pinned repository tools.
check-mise:
@if ! command -v mise >/dev/null 2>&1; then \
echo "mise not found. Install with: brew install mise" >&2; \
exit 1; \
fi
# Render .golangci.yml from kit's canonical config plus the overlay.
lint-config:
$(KENNLINT) config
lint-config-check:
$(KENNLINT) config -check
# Build golangci-lint with the plugins in .custom-gcl.yml. Strip repo-local
# Git env vars so a hook-spawned build does not inherit GIT_DIR.
custom-gcl: check-mise
@unset_args=$$(git rev-parse --local-env-vars 2>/dev/null | sed 's/^/-u /' | tr '\n' ' '); \
env $$unset_args GOFLAGS=-buildvcs=false \
$(GOLANGCI) custom --destination . --name custom-gcl --version "$(GOLANGCI_LINT_VERSION)"
# Lint Go code and auto-fix where possible. Runs kit's shared policy through
# custom-gcl (canonical golangci-lint plus kennlint analyzers).
lint: ensure-embed-dir lint-config-check custom-gcl
$(CUSTOM_GCL) run --fix ./...
# Check Go lint without mutating files; used by CI and pre-push.
lint-check: ensure-embed-dir lint-config-check custom-gcl
$(CUSTOM_GCL) run ./...
# Apply the v2 formatters (gofmt, goimports, gofumpt) to every file in place.
fmt: custom-gcl
$(CUSTOM_GCL) fmt ./...
# Check formatters without touching files. In golangci-lint v2 formatters are
# not invoked by `golangci-lint run`, so a rename that breaks import ordering
# would otherwise slip through the lint gate.
fmt-check: custom-gcl
@diff=$$($(CUSTOM_GCL) fmt --diff ./...); \
status=$$?; \
if [ "$$status" -ne 0 ]; then \
if [ -n "$$diff" ]; then printf '%s\n' "$$diff" >&2; fi; \
exit "$$status"; \
fi; \
if [ -n "$$diff" ]; then \
printf '%s\n' "$$diff"; \
echo ""; \
echo "Formatter would rewrite files listed above."; \
echo "Run 'make fmt' to apply."; \
exit 1; \
fi
# Run NilAway against first-party Go packages
nilaway: ensure-embed-dir
@if [ -z "$(NILAWAY_BIN)" ]; then \
echo "nilaway not found. Install with:" >&2; \
echo "mise install" >&2; \
exit 1; \
fi
@module_path="$$(go list -m)" || { \
echo "failed to determine module path" >&2; \
exit 1; \
}; \
"$(NILAWAY_BIN)" -include-pkgs="$$module_path" -test=false ./...
# Tidy dependencies
tidy:
go mod tidy
# Install or update repo-local Svelte AI skills and per-agent symlinks
svelte-skills:
python3 scripts/update-svelte-skills.py $(ARGS)
# Alias for explicit sync wording
svelte-skills-sync: svelte-skills
# Install pre-commit and pre-push hooks via prek
install-hooks:
@if ! command -v prek >/dev/null 2>&1; then \
echo "prek not found. Install with: brew install prek" >&2; \
exit 1; \
fi
prek install -f
# Clean build artifacts
clean:
rm -f kenn-forge kenn-forge.exe
rm -rf internal/web/dist dist/
# Show help
help:
@echo "kenn-forge build targets:"
@echo ""
@echo " build - Build with embedded frontend"
@echo " build-release - Release build (optimized, stripped)"
@echo " install - Build and install to ~/.local/bin or GOPATH"
@echo " air-install - Install air live reload tool"
@echo ""
@echo " dev - Run Go server with air live reload, debug file logs, and info-level console logs"
@echo " dev-ephemeral - Run backend and frontend on free ports with copied DB state and sync disabled (ARGS=-sync opts in)"
@echo " dev-ephemeral-stop - Stop the default ephemeral dev stack, or use STATUS=/path/to/dev-ephemeral.json"
@echo " dev-clone-db - Clone current DB into tmp/dev-db-clone and run backend on DEV_CLONE_PORT (default 8092)"
@echo " frontend-dev-clone-db - Run Vite against cloned DB backend (default port $(DEV_CLONE_FRONTEND_PORT))"
@echo " frontend-deps - Install Bun workspace dependencies for the frontend packages"
@echo " vite-plus-install - Install global Vite+ launcher with Bun when vp is missing"
@echo " frontend - Build frontend SPA with Vite+"
@echo " frontend-dev - Install deps and run Vite dev server, logging to tmp/logs/frontend-dev.log (honors KENN_FORGE_CONFIG)"
@echo " frontend-dev-bun - Install deps with Bun and run Vite+ dev server (honors KENN_FORGE_CONFIG)"
@echo " frontend-check - Run Vite+ format, lint, type, and Svelte checks for frontend packages"
@echo " docs-build - Build the public documentation site from published assets"
@echo " docs-check - Build and verify the rendered documentation site"
@echo " docs-screenshots - Generate the screenshot set for docs-assets"
@echo " docs-vercel-build - Reproduce the direct Vercel documentation build"
@echo " docs-branding-check - Enforce lowercase kenn-forge documentation branding"
@echo " docs-deploy-staging - Start a remote Vercel preview build"
@echo " docs-deploy - Start a remote Vercel production build"
@echo " frontend-api-client-check - Prevent manual /api/v1 frontend calls outside generated clients"
@echo " font-size-token-check - Enforce --font-size design tokens in frontend styles"
@echo " api-generate - Regenerate checked-in OpenAPI and TS schema"
@echo ""
@echo " test - Run all tests"
@echo " test-short - Run fast tests only"
@echo " test-e2e - Run full-stack E2E Playwright tests"
@echo " test-e2e-roborev - Run roborev e2e tests with Docker (ROBOREV_SRC, ROBOREV_REF)"
@echo " test-fleet-container - Run opt-in fleet container e2e tests"
@echo " test-fleet-drive-container - Run opt-in fleet drive container e2e tests"
@echo " test-gitlab-container - Run opt-in GitLab CE container e2e tests"
@echo " gitlab-fixture-bake - Build a reusable GitLab fixture image"
@echo " vet - Run go vet"
@echo " lint - Run kit shared Go lint policy (auto-fix)"
@echo " lint-check - Run kit shared Go lint policy without modifying files"
@echo " lint-config - Render .golangci.yml from kit plus the overlay"
@echo " fmt - Apply gofmt, goimports, and gofumpt"
@echo " fmt-check - Check Go formatters without modifying files"
@echo " timing-budget-check - Reject unreviewed sub-second test polling budgets"
@echo " nilaway - Run NilAway against first-party Go packages"
@echo " huma-route-check - Prevent non-Huma Go route registrations"
@echo " guardrail-check - Run generated-client, font-size token, and Huma route guardrails"
@echo " tidy - Tidy go.mod"
@echo " svelte-skills - Sync repo-local Svelte AI skills and per-agent symlinks"
@echo " svelte-skills-sync - Alias for svelte-skills"
@echo ""
@echo " install-hooks - Install pre-commit and pre-push hooks (prek)"
@echo " clean - Remove build artifacts"
# Enforce the shared API contract without rewriting files during verification.
huma-check:
go run go.kenn.io/kit/cmd/huma-check@$(HUMA_CHECK_VERSION) -fix=false ./...