-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
466 lines (411 loc) · 22.4 KB
/
Copy pathMakefile
File metadata and controls
466 lines (411 loc) · 22.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
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
# opendbx top-level Makefile
# ============================
#
# spec-0.8 D-7 / T-11 — doc block satisfies binary criterion: ≥ 3 categories
# + ≥ 1 cross-repo dependency + ≥ 1 GNU make / bash requirement.
#
# Categories:
#
# 用户日常: build / test / gate / gate-fast / fmt / lint / bench
# CI 投影: import-check / dep-check / golden / coverage-gate / makefile-check
# release: tag-spec (cross-repo dual-tag) / release (stub → spec-5.1)
# 维护: hooks-install / hooks-status / gen-docs / cc-help-diff / clean
#
# Cross-repo dependencies:
# tag-spec / gen-docs / cc-help-diff / makefile-check 都需要 sibling
# $(OPENDBRB_DIR) (default ../opendbrb; override via env).
#
# Platform:
# Requires GNU make + bash 3.2+ + git. macOS / Linux only; Windows not
# supported (spec-0.7 § 2.3). All shell recipes avoid bash 4+ features
# (no globstar `**`) — macOS bash 3.2 broke spec-0.7 T-14 dogfood on `**`.
# T-10a fix per spec-0.8 R2 MED-1: .PHONY split across multiple single-line
# declarations (makefile-check tool rejects backslash continuation).
.PHONY: all build test test-cover gate gate-fast lint fmt bench clean help
.PHONY: hooks-install hooks-status import-check dep-check
.PHONY: golden golden-update gen-docs cc-help-diff
.PHONY: coverage-gate makefile-check tag-spec release registry-drift-check
.PHONY: vuln-check ci-script-check sync-branch-protection suppression-check errcode-check paint-pattern-check lint-all
BIN_DIR := bin
BIN_NAME := opendbx
GO := go
# spec-0.8 D-3 / T-6: cross-repo sibling path for tag-spec / sync-registry.
# Default ../opendbrb; override with `make tag-spec OPENDBRB_DIR=~/work/opendbrb ...`
# when clone path differs.
OPENDBRB_DIR ?= ../opendbrb
# spec-0.7 D-3 / T-5: linker -X 注入 4 字段构建元数据 (Version / Commit /
# BuildDate / Dirty). Supported platforms: linux, darwin (POSIX shell + git +
# date -u). Windows not a target for ldflag metadata injection.
#
# Key decisions (claude MED-3 + codex MED 整合):
# - VERSION: `git describe --tags --always` -- 去掉 --dirty 后缀, 让 Version
# 保持 Parse-able tag 字符串; dirty 状态由 DIRTY 独立变量携带 (MED-3).
# - COMMIT: `git rev-parse --short=12 HEAD` -- 显式 12-char short hash.
# - BUILD_DATE: ISO8601 UTC (matches spec-0.5 logger sidecar timestamp 风格).
# - DIRTY: `git status --porcelain` -- 捕获 tracked + untracked changes
# (diff-index 漏 untracked, codex MED).
VERSION := $(shell git describe --tags --always 2>/dev/null || echo 'dev')
COMMIT := $(shell git rev-parse --short=12 HEAD 2>/dev/null || echo 'unknown')
BUILD_DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
DIRTY := $(shell git status --porcelain 2>/dev/null | awk 'END { if (NR>0) print "dirty"; else print "" }')
VERSION_PKG := github.com/sqlrush/opendbx/internal/platform/version
LDFLAGS := -s -w \
-X $(VERSION_PKG).Version=$(VERSION) \
-X $(VERSION_PKG).Commit=$(COMMIT) \
-X $(VERSION_PKG).BuildDate=$(BUILD_DATE) \
-X $(VERSION_PKG).Dirty=$(DIRTY)
GO_BUILD_FLAGS := -trimpath -ldflags="$(LDFLAGS)"
all: build ## Default: build the opendbx binary
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " \033[36m%-12s\033[0m %s\n", $$1, $$2}'
build: ## Build opendbx binary into bin/
@mkdir -p $(BIN_DIR)
$(GO) build $(GO_BUILD_FLAGS) -o $(BIN_DIR)/$(BIN_NAME) ./cmd/opendbx
test: ## Run all tests
$(GO) test -race ./...
test-cover: ## Run tests with coverage report
$(GO) test -race -coverprofile=coverage.out ./...
$(GO) tool cover -func=coverage.out | tail -1
lint: ## Run golangci-lint
golangci-lint run --timeout 5m
fmt: ## Format code
gofmt -w .
$(GO) mod tidy
# spec-0.8 D-2 / T-5: bench WARN-mode in gate.
#
# BENCH_TIMEOUT defaults to 2m (Q10 ★A); override via env: `BENCH_TIMEOUT=5m make bench`.
# Output captured to BENCH_OUTPUT for spec-0.11 baseline comparison.
#
# WARN signal format (claude MED-5 + Q2 ★A): any anomaly (timeout, parse
# error, no benchmarks) emits `BENCH_WARN: <reason>` to stderr and exits 0.
# spec-0.11 will grep `BENCH_WARN:` to flip to FAIL semantics with baselines.
# T-13c codex MED-2/LOW-2: BENCH_OUTPUT default aligned with spec D-2 (`bench.out`).
# `*.out` is in .gitignore so this won't pollute the repo. Env override
# allowed: `BENCH_OUTPUT=/tmp/foo.out make bench`.
BENCH_TIMEOUT ?= 2m
BENCH_OUTPUT ?= bench.out
# T-13c codex MED-1: previous `| tee $(BENCH_OUTPUT)` made `rc=$$?` capture
# tee's exit (≈ always 0), masking genuine `go test` failures. Redirect
# first, then `cat` for visibility; rc now reflects `go test` truthfully.
bench: ## Run benchmarks -> bench.out (WARN; spec-0.11 -> FAIL)
@echo "=== bench (BENCH_TIMEOUT=$(BENCH_TIMEOUT)) ==="
@set +e; \
$(GO) test -bench=. -benchmem -run=^$$$$ -count=1 -timeout=$(BENCH_TIMEOUT) ./... > $(BENCH_OUTPUT) 2>&1; \
rc=$$?; \
cat $(BENCH_OUTPUT); \
if [ $$rc -ne 0 ]; then \
echo "BENCH_WARN: bench exit code $$rc (timeout / parse error / runtime panic); see $(BENCH_OUTPUT)" >&2; \
exit 0; \
fi; \
if ! grep -q '^Benchmark' $(BENCH_OUTPUT); then \
echo "BENCH_WARN: no benchmarks present in this codebase (Stage 0 expected; spec-1.4+ adds perf baselines)" >&2; \
fi
# gate-fast: skip the expensive coverage + bench steps for quick dev iteration.
# It does NOT replace push-time `make gate`; spec-0.9 CI should use the full gate.
.PHONY: gate-fast
gate-fast: import-check dep-check golden ## Fast dev gate (skip coverage + bench; not for push)
@echo "=== gate-fast (no coverage / no bench) ==="
gofmt -l . | tee /tmp/opendbx-fmt.txt && [ ! -s /tmp/opendbx-fmt.txt ] || (echo "gofmt failed" && exit 1)
$(GO) vet ./...
CGO_ENABLED=0 $(GO) build ./...
$(GO) test -race ./...
@echo "=== gate-fast PASSED (push 前请跑 make gate 全套) ==="
# spec-0.8 D-3 / T-6: tag-spec wrapper.
#
# Forwards to ../opendbrb/scripts/release/tag-spec.sh (spec-0.7 D-2 SSOT).
# Both repos get this target (Q3 ★A); opendbrb side ships in T-8 / D-5.
#
# Env vars (read by underlying script): DRY_RUN / OPENDBX_TAG_REPAIR
# Make vars → script flags: STAGE_ACCEPTED / FORCE_DIRTY / REPAIR_PEER
#
# Examples:
# make tag-spec SPEC=spec-0.8-makefile-build
# DRY_RUN=1 make tag-spec SPEC=spec-0.8-makefile-build
# make tag-spec SPEC=spec-0.16-stage0-acceptance STAGE_ACCEPTED=1
# make tag-spec SPEC=spec-0.8-... REPAIR_PEER=1 OPENDBX_TAG_REPAIR=1
.PHONY: tag-spec
tag-spec: ## FROZEN-tag a spec via opendbrb tag-spec.sh (SPEC=... req)
@[ -n "$(SPEC)" ] || (echo "ERR: SPEC= required (e.g. make tag-spec SPEC=spec-0.8-makefile-build)" >&2; exit 1)
@[ -d "$(OPENDBRB_DIR)" ] || (echo "ERR: $(OPENDBRB_DIR) not found; clone opendbrb sibling or override OPENDBRB_DIR=..." >&2; exit 1)
@[ -x "$(OPENDBRB_DIR)/scripts/release/tag-spec.sh" ] || (echo "ERR: $(OPENDBRB_DIR)/scripts/release/tag-spec.sh missing or not executable" >&2; exit 1)
@DRY_RUN=$(DRY_RUN) OPENDBX_TAG_REPAIR=$(OPENDBX_TAG_REPAIR) \
$(OPENDBRB_DIR)/scripts/release/tag-spec.sh $(SPEC) \
$(if $(filter 1,$(STAGE_ACCEPTED)),--stage-accepted,) \
$(if $(filter 1,$(FORCE_DIRTY)),--force-dirty,) \
$(if $(filter 1,$(REPAIR_PEER)),--repair-missing-peer,)
# spec-0.8 D-4 / T-7: release pipeline placeholder.
#
# Real release flow (GoReleaser + multi-arch + GitHub Release body) lands
# in spec-5.1. Until then, this target exits 1 with a clear error so:
# - CI never accidentally "succeeds" a release that didn't actually run
# - Local users see explicit pointer to current alternative (make build)
# - spec-0.9 ci-github-actions MUST NOT call this target (CI 调用必须红)
#
# Q4 ★A (R2): stub-fail vs no-op vs scaffold; stub-fail picked because
# silent no-op breaks the worst (CI thinks release pipeline ran).
.PHONY: release
release: ## STUB - release lands in spec-5.1; CI must not call
@echo "ERROR: 'make release' is a stub. The real release pipeline" >&2
@echo " (GoReleaser + multi-arch binary + GitHub Release body)" >&2
@echo " lands in spec-5.1-release-pipeline." >&2
@echo "" >&2
@echo " For local single-binary build:" >&2
@echo " make build # → bin/opendbx" >&2
@echo "" >&2
@echo " For tagging a spec FROZEN (spec-0.7 dual-repo automation):" >&2
@echo " make tag-spec SPEC=spec-X.Y-<slug>" >&2
@echo "" >&2
@echo " spec-0.9 ci-github-actions: do NOT invoke 'make release'." >&2
@exit 1
# Layer-2 gate: 所有这些命令必须 PASS 才允许 push
# 详见设计仓 docs/cicd-and-methodology.md § 2
# Layer-2 gate runs cheap checks first (fmt/vet/tidy/lint/import/dep/golden/
# build), then the expensive coverage-gate step (which itself runs
# `go test -race -coverprofile=...` and enforces CLAUDE.md 规则 8 thresholds).
# spec-0.8 D-1 / T-4.
#
# Prereqs run before recipe (Make semantics), so import-check / dep-check /
# golden run FIRST. The recipe then runs fmt/vet/tidy/lint/build inline,
# and finally invokes coverage-gate (which subsumes the prior `go test -race`).
gate: import-check dep-check golden ## Local layer-2 gate (must pass before push)
@echo "=== Layer-2 Gate ==="
gofmt -l . | tee /tmp/opendbx-fmt.txt && [ ! -s /tmp/opendbx-fmt.txt ] || (echo "gofmt failed" && exit 1)
$(GO) vet ./...
$(GO) mod tidy && git diff --exit-code go.mod go.sum 2>/dev/null || (echo "go.mod/go.sum dirty after tidy" && exit 1)
@if command -v golangci-lint >/dev/null 2>&1; then golangci-lint run --timeout 5m; else echo "golangci-lint not installed (skip in bootstrap)"; fi
CGO_ENABLED=0 $(GO) build ./...
$(MAKE) makefile-check
$(MAKE) registry-drift-check
$(MAKE) ci-script-check
$(MAKE) suppression-check
$(MAKE) errcode-check
$(MAKE) paint-pattern-check
$(MAKE) coverage-gate
$(MAKE) bench
@echo "=== Layer-2 Gate PASSED ==="
# T-13c codex HIGH-1 / claude HIGH-1: sibling-aware delegation to opendbrb's
# registry-drift-check (data-row comparison between SSOT and hook-local copy).
# Silently skipped if sibling absent — gate remains useful for opendbx-only
# clones. spec-0.8 D-5 explicitly requires this in opendbx gate.
registry-drift-check: ## Detect drift vs opendbrb/specs/spec-registry.txt SSOT
@echo "=== gate: registry-drift-check ==="
@if [ -f "$(OPENDBRB_DIR)/Makefile" ]; then \
$(MAKE) -C "$(OPENDBRB_DIR)" registry-drift-check OPENDBX_DIR="$(CURDIR)"; \
else \
echo "skip (sibling $(OPENDBRB_DIR) not present)"; \
fi
# spec-0.8 D-1 / T-4: enforce CLAUDE.md 规则 8 per-package coverage thresholds.
#
# Tiers (R2 用户拍板 CRIT-A + T-13 tool-tier errata):
# core (≥85%): errcode / logger / version
# tool (≥90%): coverage-gate / makefile-check
# other (≥75%): everything not core/exempt
# exempt: entrypoints / import-rules-check / dep-allowlist-check /
# import-rules-check/rules / cmd/opendbx / config / rpc
# total (≥80%): aggregated over non-exempt packages
#
# coverage-gate runs `go test -race -coverprofile=...` internally — gate
# uses this as the unit-test step too, so the regular `go test -race` line
# was removed from the recipe above.
#
# Emergency override: `COVERAGE_GATE_SKIP=1 make coverage-gate` (Q11 ★A;
# usage MUST be noted in CHANGELOG).
COVERAGE_PROFILE := /tmp/opendbx-coverage.out
coverage-gate: ## Coverage gate: per-package thresholds (spec-0.8 D-1)
@echo "=== gate: coverage-gate ==="
$(GO) test -race -coverprofile=$(COVERAGE_PROFILE) ./...
$(GO) run ./tools/coverage-gate -profile=$(COVERAGE_PROFILE)
# spec-0.8 D-6 / T-10b: Makefile lint. Scans this Makefile + sibling
# opendbrb/Makefile (if present) for the 5 conventions defined in
# tools/makefile-check (help comment / .PHONY / kebab-lower / no-dup /
# doc-block + .PHONY no-continuation). Sibling skip is silent — gate
# remains useful for opendbx-only clones.
makefile-check: ## Lint top-level Makefile(s) + sibling if present
@echo "=== gate: makefile-check ==="
@files="Makefile"; \
if [ -f "$(OPENDBRB_DIR)/Makefile" ]; then files="$$files $(OPENDBRB_DIR)/Makefile"; fi; \
$(GO) run ./tools/makefile-check $$files
# spec-0.9 D-2.5 / T-3.5 / T-7.5: govulncheck + OSV allowlist 包装.
#
# govulncheck 本身无 inline 豁免机制; 直 fail on first finding 不可接受 (Stage 0
# Go 1.23 lock vs Go 1.25.8 fix for GO-2026-4602 stdlib vuln). 包装脚本读
# tools/vuln-allowlist/allowlist.json (OSV ID + module + expiry + spec_ref) 过滤
# 已知豁免. 过期 / 未豁免 / module mismatch 强制 fail.
#
# T-7.5 codex CRIT-1 修: 旧版用裸 pipe `govulncheck ... | wrapper`. 在 POSIX shell
# 无 pipefail 时, pipeline 退出码只看 wrapper, govulncheck 分析失败 (rc != 0/3,
# 如 build error / module load error) 被 wrapper 吞掉返回 0 → security required
# job 假绿. 修法: temp file + 显式 rc 检查; govulncheck rc=0 (clean) 或 rc=3
# (called vulns) 都正常投 wrapper; 其他 rc 直接 fail.
GOVULN_VERSION ?= v1.1.4
vuln-check: ## Run govulncheck filtered by OSV allowlist (spec-0.9 D-2.5)
@bash -c ' \
set -e; \
gobin=$$($(GO) env GOBIN); \
if [ -z "$$gobin" ]; then gobin=$$($(GO) env GOPATH)/bin; fi; \
mkdir -p "$$gobin"; \
$(GO) install golang.org/x/vuln/cmd/govulncheck@$(GOVULN_VERSION); \
govulncheck_bin="$$gobin/govulncheck"; \
version_output=$$("$$govulncheck_bin" -version 2>&1 || true); \
if ! printf "%s\n" "$$version_output" | grep -q "Scanner: govulncheck@$(GOVULN_VERSION)"; then \
echo "vuln-check: expected govulncheck $(GOVULN_VERSION), got:" >&2; \
printf "%s\n" "$$version_output" >&2; \
exit 1; \
fi; \
tmp=$$(mktemp -t opendbx-govuln.XXXXXX); \
trap "rm -f $$tmp" EXIT; \
set +e; \
"$$govulncheck_bin" -json -test ./... > $$tmp; \
gv_rc=$$?; \
set -e; \
if [ $$gv_rc -ne 0 ] && [ $$gv_rc -ne 3 ]; then \
echo "vuln-check: govulncheck exit $$gv_rc (analysis error; not just findings)" >&2; \
exit $$gv_rc; \
fi; \
$(GO) run ./tools/vuln-allowlist < $$tmp \
'
# spec-0.9 D-5 / T-7: ci.yml ↔ branch-protection JSON 1:1 drift check.
ci-script-check: ## Detect ci.yml vs branch-protection JSON drift (D-5)
@$(GO) run ./tools/ci-protection-check
@bash scripts/ci/test-sync-branch-protection.sh
# spec-0.9 D-5 / T-7: PATCH /required_status_checks 窄端点同步.
sync-branch-protection: ## Sync branch protection contexts (dry-run; APPLY=1)
@bash scripts/ci/sync-branch-protection.sh $(if $(filter 1,$(APPLY)),--apply,--dry-run)
# spec-0.10 D-2.5 / T-3.5: suppression-lint enforces every `//nolint`,
# `// #nosec`, `// errcode-lint:exempt`, `// govulncheck-exempt` comment
# carries a `spec-X.Y[-tN]` reference (lint-policy.md § 3).
suppression-check: ## Verify all suppression comments carry spec_ref
@$(GO) run ./tools/suppression-lint .
# spec-0.10 D-2 / T-4: errcode-lint permanent enforcement of spec-0.6 D-4
# contract — exported error-returning functions must use errcode, not
# bare errors.New / fmt.Errorf (lint-policy.md § 2).
errcode-check: ## Verify exported public API errors use errcode (D-2)
@$(GO) run ./tools/errcode-lint ./...
# spec-1.20.2 D-3: paint-pattern-lint enforces that cross-receiver
# cell-to-cell copies go through render/paint.Blit/BlitAt, never the
# bare dst.SetCell(x, y, src.Cell(...)) (or one-hop assign) pattern
# that caused the 5/28 user-terminal CJK / markdown mojibake. Run via
# `go run` (not pre-built binary): tooling source must be present, no
# command -v silent skip.
paint-pattern-check: ## Verify no bare cell-to-cell SetCell (D-3)
@$(GO) run ./tools/paint-pattern-lint ./...
# spec-0.11.5 D-5: UI Review 5-layer gate targets.
.PHONY: ui-invariant ui-visual-golden ui-ai-review ui-block-golden ui-block-ai-review ui-tooluse-golden ui-toolresult-golden ui-compact-golden ui-markdown-golden ui-code-golden ui-diff-golden ui-program-golden ui-input-golden ui-keybindings-golden ui-llmchat-golden ui-reasoningrender-golden
ui-invariant: ## Layer 1 static invariants (uiinvariant package tests)
$(GO) test -race -count=1 ./internal/testing/uiinvariant/...
ui-visual-golden: ## Layer 3 freeze + pixelmatch (VISUALGOLDEN_REQUIRED=1 in CI)
VISUALGOLDEN_REQUIRED=$${VISUALGOLDEN_REQUIRED:-} $(GO) test -race -count=1 ./internal/testing/visualgolden/...
ui-ai-review: ## Layer 4 AI review (needs LOCAL_VL_ENDPOINT)
@if [ -z "$$LOCAL_VL_ENDPOINT" ]; then \
echo "ui-ai-review: LOCAL_VL_ENDPOINT unset, skipping" >&2; exit 0; \
fi
$(GO) test -race -count=1 ./internal/testing/aivisual/...
ui-block-golden: ## Block Message visual golden harness
BLOCK_VISUAL_REQUIRED=$${BLOCK_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestMessageVisualGolden ./tests/integration/uitest/block/...
ui-tooluse-golden: ## Block ToolUse visual golden harness (spec-1.9 D-9)
TOOLUSE_VISUAL_REQUIRED=$${TOOLUSE_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestToolUseVisualGolden ./tests/integration/uitest/block/...
ui-toolresult-golden: ## Block ToolResult visual golden harness (spec-1.9b D-8)
TOOLRESULT_VISUAL_REQUIRED=$${TOOLRESULT_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestToolResultVisualGolden ./tests/integration/uitest/block/...
ui-compact-golden: ## Block CompactSummary visual golden harness (spec-1.10 D-8)
COMPACT_VISUAL_REQUIRED=$${COMPACT_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestCompactVisualGolden ./tests/integration/uitest/block/...
ui-markdown-golden: ## Block Markdown visual golden harness (spec-1.11 D-8)
MARKDOWN_VISUAL_REQUIRED=$${MARKDOWN_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestMarkdownVisualGolden ./tests/integration/uitest/block/...
ui-code-golden: ## Block Code (spec-1.12) visual golden harness
CODE_VISUAL_REQUIRED=$${CODE_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestCodeVisualGolden ./tests/integration/uitest/block/...
ui-diff-golden: ## Block Diff (spec-1.13) visual golden harness
DIFF_VISUAL_REQUIRED=$${DIFF_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestDiffVisualGolden ./tests/integration/uitest/block/...
ui-program-golden: ## Program (spec-1.15) visual golden harness
PROGRAM_VISUAL_REQUIRED=$${PROGRAM_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestProgramVisualGolden ./tests/integration/uitest/program/...
ui-input-golden: ## Input mode (spec-1.16) visual golden harness
INPUT_VISUAL_REQUIRED=$${INPUT_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestInputModeVisualGolden ./tests/integration/uitest/input/...
ui-keybindings-golden: ## Keybindings cursor/history (spec-1.17) visual golden harness
KEYBINDINGS_VISUAL_REQUIRED=$${KEYBINDINGS_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestKeybindingsVisualGolden ./tests/integration/uitest/keybindings/...
ui-llmchat-golden: ## LLM chat (spec-1.20) visual golden harness
LLMCHAT_VISUAL_REQUIRED=$${LLMCHAT_VISUAL_REQUIRED:-} $(GO) test -race -count=1 -run TestLLMChatVisualGolden ./tests/integration/uitest/llmchat/...
# spec-1.20.2 D-6: 13th independent block-style visual env gate. Runs the
# production-like smoke (no mojibake / no mixed reasoning / no stderr
# tear) + parked fixture guard. Golden capture lands post-stage-1 SOP.
ui-reasoningrender-golden: ## Reasoning/render (1.20.2) gate
REASONINGRENDER_VISUAL_REQUIRED=$${REASONINGRENDER_VISUAL_REQUIRED:-} $(GO) test -race -count=1 ./tests/integration/uitest/reasoningrender/...
ui-block-ai-review: ## Block Message AI visual review wrapper
$(MAKE) ui-ai-review
# spec-0.2 governance gates (D-5 / D-6 / D-3) — see docs/cicd-and-methodology.md
import-check: ## Run import-rules-check (spec-0.2 D-5)
$(GO) run ./tools/import-rules-check -v .
dep-check: ## Run dep-allowlist-check (spec-0.2 D-6)
$(GO) run ./tools/dep-allowlist-check -v .
golden: ## Run CLI text golden tests (spec-0.2 D-3)
$(GO) test -race -run 'TestGolden|TestSubcommandStubs' ./cmd/opendbx/...
golden-update: ## Regenerate CLI golden files
TEST_UPDATE_GOLDEN=1 $(GO) test -run TestGolden ./cmd/opendbx/...
@echo "goldens updated. Review with 'git diff cmd/opendbx/testdata/golden/'"
gen-docs: ## Regenerate opendbrb docs/error-codes.md from registry
$(GO) run cmd/tools/gen-error-codes/main.go --out=../opendbrb/docs/error-codes.md
# spec-0.3 D-6: drift check vs CC v2.1.138 baseline. Doesn't fail; surfaces
# a unified diff that humans review (ad hoc when CC upgrades) per user D8 +
# D13 decisions.
CC_HELP_BASELINE := ../opendbrb/docs/cc-help-baseline-v2.1.138.txt
cc-help-diff: build ## Diff opendbx --help against the CC help baseline
@echo "=== opendbx --help vs $(CC_HELP_BASELINE) ==="
@if [ ! -f "$(CC_HELP_BASELINE)" ]; then \
echo "ERR: baseline not found: $(CC_HELP_BASELINE)"; \
echo " (per user D3 + D8: lock to local CC version; do not chase latest)"; \
exit 1; \
fi
@./$(BIN_DIR)/$(BIN_NAME) --help > /tmp/opendbx-help.txt 2>&1
@echo "(exit code below is from diff: 0=identical, 1=differences exist, 2=error)"
@diff -u $(CC_HELP_BASELINE) /tmp/opendbx-help.txt || true
@echo ""
@echo "Differences are expected (DB-flavored adaptations + opendbx-specific commands)."
@echo "Curated rationale: ../opendbrb/docs/cc-vs-opendbx-help-diff.md"
clean: ## Remove build artifacts
rm -rf $(BIN_DIR) coverage.out *.prof
# ===== git hooks (spec-0.1 D-8) =====
GIT_HOOKS_DIR := $(shell git rev-parse --git-dir 2>/dev/null)/hooks
SRC_HOOKS := $(wildcard git-hooks/*)
hooks-install: ## Install repo git hooks + build lint binaries (spec-0.10 D-5)
@if [ -z "$(GIT_HOOKS_DIR)" ]; then \
echo "ERR: not in a git repo"; exit 1; \
fi
@for h in $(SRC_HOOKS); do \
name=$$(basename "$$h"); \
dest="$(GIT_HOOKS_DIR)/$$name"; \
ln -sf "$(CURDIR)/$$h" "$$dest"; \
echo "linked $$h -> $$dest"; \
done
@echo ""
@echo "building lint binaries (spec-0.10 D-5 R2 claude H1: precompile防 cold go run)..."
@mkdir -p $(BIN_DIR)
@$(GO) build -o $(BIN_DIR)/errcode-lint ./tools/errcode-lint
@$(GO) build -o $(BIN_DIR)/import-rules-check ./tools/import-rules-check
@$(GO) build -o $(BIN_DIR)/suppression-lint ./tools/suppression-lint
@echo " bin/errcode-lint / bin/import-rules-check / bin/suppression-lint"
@echo ""
@echo "git hooks installed."
@echo "next: build commit-lint binary so the hook can find it:"
@echo " cd ../opendbrb/scripts/opendbrb-commit-lint && go install ."
lint-all: ## Run all lint checks (gate-fast subset; spec-0.10 D-5)
@echo "=== lint-all (spec-0.10 D-5; fast subset of gate) ==="
@$(MAKE) import-check
@$(MAKE) dep-check
@$(MAKE) errcode-check
@$(MAKE) suppression-check
@$(MAKE) ci-script-check
@$(MAKE) makefile-check
@echo "=== lint-all OK ==="
hooks-status: ## Show installed git hooks
@if [ -z "$(GIT_HOOKS_DIR)" ]; then \
echo "ERR: not in a git repo"; exit 1; \
fi
@for h in $(SRC_HOOKS); do \
name=$$(basename "$$h"); \
dest="$(GIT_HOOKS_DIR)/$$name"; \
if [ -L "$$dest" ]; then \
target=$$(readlink "$$dest"); \
echo "OK $$name -> $$target"; \
elif [ -e "$$dest" ]; then \
echo "FILE $$name (not symlink — manually managed?)"; \
else \
echo "MISS $$name (run 'make hooks-install')"; \
fi; \
done