-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathMakefile
More file actions
285 lines (241 loc) · 11.7 KB
/
Makefile
File metadata and controls
285 lines (241 loc) · 11.7 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
.DEFAULT_GOAL := help
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_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 := middleman$(EXE_SUFFIX)
GOPATH_FIRST := $(shell go env GOPATH | sed -E 's/^([A-Za-z]:)?([^;:]*).*/\1\2/')
ROBOREV_SRC ?= $(HOME)/code/roborev
ROBOREV_REF ?= main
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)
DEV_LOG_DIR ?= tmp/logs
DEV_BACKEND_LOG ?= $(DEV_LOG_DIR)/backend-dev.log
.PHONY: ensure-embed-dir ensure-tmp-dir check-air air-install build build-release install \
frontend-deps frontend frontend-dev frontend-dev-bun frontend-check api-generate roborev-api-generate \
dev test test-short test-integration test-e2e test-e2e-roborev test-gitlab-container gitlab-fixture-bake vet lint nilaway testify-helper-check \
frontend-api-client-check huma-route-check script-tests guardrail-check tidy svelte-skills svelte-skills-sync clean install-hooks help
# 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
# 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
# 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
go build -ldflags="$(LDFLAGS)" -o $(BINARY) ./cmd/middleman
# Build with optimizations (release)
build-release: frontend
go build -ldflags="$(LDFLAGS_RELEASE)" -trimpath -o $(BINARY) ./cmd/middleman
# 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)"; \
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)"; \
fi
# Install Bun workspace dependencies for frontend and packages/ui
frontend-deps:
bun install
# Build frontend SPA and copy into embed directory
frontend: frontend-deps
cd frontend && bun run build
rm -rf internal/web/dist
cp -r frontend/dist internal/web/dist
printf 'ok\n' > internal/web/dist/stub.html
# Run Vite dev server with dependencies installed (use alongside `make dev`)
frontend-dev:
./scripts/frontend-dev.sh $(ARGS)
# Run Vite dev server with Bun (use alongside `make dev`)
frontend-dev-bun: frontend-deps
cd frontend && bun run dev
# Run TypeScript/Svelte lint and type checks
frontend-check: frontend-deps
cd packages/ui && bun run typecheck && bun run lint
cd frontend && bun run typecheck && bun run lint
# Prevent production frontend code from bypassing generated API clients
frontend-api-client-check:
bun scripts/lint-api-urls.mjs
# Prevent application HTTP routes from bypassing Huma registration
huma-route-check:
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" go run ./tools/nohttpmux ./...
# Run lightweight script regression tests
script-tests:
bun test scripts/*.test.mjs
# Run lightweight generated-client/Huma guardrails
guardrail-check: frontend-api-client-check huma-route-check script-tests
# Regenerate the checked-in OpenAPI document and generated clients
api-generate:
GOCACHE="$${GOCACHE:-/tmp/middleman-gocache}" go run ./cmd/middleman-openapi -out frontend/openapi/openapi.yaml
GOCACHE="$${GOCACHE:-/tmp/middleman-gocache}" go run ./cmd/middleman-openapi -out internal/apiclient/spec/openapi.json -version 3.0
cd packages/ui && bunx openapi-typescript ../../frontend/openapi/openapi.yaml -o src/api/generated/schema.ts
printf '%s\n' \
'/**' \
' * This file was auto-generated from frontend/openapi/openapi.yaml.' \
' * Do not make direct changes to the file.' \
' */' \
'' \
'import createClient, { type ClientOptions } from "openapi-fetch";' \
'import type { paths } from "./schema";' \
'' \
'export function createAPIClient(baseUrl: string, options: Pick<ClientOptions, "fetch" | "querySerializer"> = {}) {' \
' return createClient<paths>({ baseUrl, ...options });' \
'}' \
> packages/ui/src/api/generated/client.ts
GOCACHE="$${GOCACHE:-/tmp/middleman-gocache}" go generate ./internal/apiclient/generated
# Regenerate roborev TypeScript client types from checked-in OpenAPI spec
roborev-api-generate:
cd packages/ui && bunx openapi-typescript src/api/roborev/openapi.json -o src/api/roborev/generated/schema.ts
@echo "Roborev API types generated"
# 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: $${MIDDLEMAN_LOG_FILE:-$(DEV_BACKEND_LOG)}"
@echo "backend console log level: $${MIDDLEMAN_LOG_STDERR_LEVEL:-info}"
@echo "tail with: tail -F $${MIDDLEMAN_LOG_FILE:-$(DEV_BACKEND_LOG)}"
@if [ -n "$(MIDDLEMAN_CONFIG)" ]; then \
MIDDLEMAN_LOG_LEVEL="$${MIDDLEMAN_LOG_LEVEL:-debug}" \
MIDDLEMAN_LOG_FILE="$${MIDDLEMAN_LOG_FILE:-$(DEV_BACKEND_LOG)}" \
MIDDLEMAN_LOG_STDERR_LEVEL="$${MIDDLEMAN_LOG_STDERR_LEVEL:-info}" \
"$(AIR_BIN)" -c .air.toml -- -config "$(MIDDLEMAN_CONFIG)" $(ARGS); \
else \
MIDDLEMAN_LOG_LEVEL="$${MIDDLEMAN_LOG_LEVEL:-debug}" \
MIDDLEMAN_LOG_FILE="$${MIDDLEMAN_LOG_FILE:-$(DEV_BACKEND_LOG)}" \
MIDDLEMAN_LOG_STDERR_LEVEL="$${MIDDLEMAN_LOG_STDERR_LEVEL:-info}" \
"$(AIR_BIN)" -c .air.toml -- $(ARGS); \
fi
# Run tests
test: ensure-embed-dir ensure-tmp-dir
$(GOTESTSUM)=tmp/test-output.json -- ./... -shuffle=on
# Run fast tests only
test-short: ensure-embed-dir ensure-tmp-dir
$(GOTESTSUM)=tmp/test-short-output.json -- ./... -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 -- -tags integration ./... -shuffle=on
# 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
cd frontend && bun run playwright test --config=playwright-e2e.config.ts --project=chromium
# 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 GitLab CE container compatibility tests.
test-gitlab-container: ensure-embed-dir ensure-tmp-dir
@if [ "$${MIDDLEMAN_GITLAB_CONTAINER_E2E:-}" != "1" ]; then \
echo "Set MIDDLEMAN_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 ./...
# Enforce testify helper usage for assertion-heavy tests
testify-helper-check: ensure-embed-dir
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" go run ./cmd/testify-helper-check ./...
# Lint Go code and auto-fix where possible
lint: ensure-embed-dir
@if ! command -v mise >/dev/null 2>&1; then \
echo "mise not found. Install with: brew install mise" >&2; \
exit 1; \
fi
GOCACHE="$${GOCACHE:-/tmp/middleman-gocache}" mise exec -- golangci-lint run --fix
GOFLAGS="$${GOFLAGS:+$$GOFLAGS }-buildvcs=false" go run ./cmd/testify-helper-check ./...
# Run NilAway against first-party Go packages
nilaway: ensure-embed-dir
@if ! command -v nilaway >/dev/null 2>&1; then \
echo "nilaway not found. Install with:" >&2; \
echo "go install go.uber.org/nilaway/cmd/nilaway@v0.0.0-20260318203545-ad240b12fb4c" >&2; \
exit 1; \
fi
@module_path="$$(go list -m)" || { \
echo "failed to determine module path" >&2; \
exit 1; \
}; \
nilaway -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 middleman middleman.exe
rm -rf internal/web/dist dist/
# Show help
help:
@echo "middleman 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 " frontend-deps - Install Bun workspace dependencies for frontend and packages/ui"
@echo " frontend - Build frontend SPA"
@echo " frontend-dev - Install deps and run Vite dev server, logging to tmp/logs/frontend-dev.log (honors MIDDLEMAN_CONFIG)"
@echo " frontend-dev-bun - Install deps with Bun and run Vite dev server (honors MIDDLEMAN_CONFIG)"
@echo " frontend-check - Run TS/Svelte lint and typecheck for frontend and packages/ui"
@echo " frontend-api-client-check - Prevent manual /api/v1 frontend calls outside generated clients"
@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-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 mise-managed golangci-lint (auto-fix)"
@echo " nilaway - Run NilAway against first-party Go packages"
@echo " testify-helper-check - Enforce Assert.New(t) in assertion-heavy Go tests"
@echo " huma-route-check - Prevent non-Huma Go route registrations"
@echo " guardrail-check - Run generated-client 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"