-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathMakefile
More file actions
377 lines (332 loc) · 14.5 KB
/
Makefile
File metadata and controls
377 lines (332 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
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
# agent-pmo:d75d5c8
# =============================================================================
# Standard Makefile — Nimblesite.DataProvider.Core
# Cross-platform: Linux, macOS, Windows (via GNU Make)
# All targets are language-agnostic. Add language-specific helpers below.
# =============================================================================
.PHONY: build test lint fmt fmt-check clean check ci coverage setup
# -----------------------------------------------------------------------------
# OS Detection — portable commands for Linux, macOS, and Windows
# On Windows, run via GNU Make with PowerShell (e.g., make from Git Bash or
# choco install make). The $(OS) variable is set to "Windows_NT" automatically.
# -----------------------------------------------------------------------------
ifeq ($(OS),Windows_NT)
SHELL := powershell.exe
.SHELLFLAGS := -NoProfile -Command
RM = Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
MKDIR = New-Item -ItemType Directory -Force
HOME ?= $(USERPROFILE)
else
# Force bash for non-Windows. Some recipes use bash-only constructs
# (e.g. ${PIPESTATUS[0]}, [[ ... ]]), and Ubuntu's default /bin/sh is dash.
SHELL := /bin/bash
RM = rm -rf
MKDIR = mkdir -p
endif
# All .NET test projects (one per line for readability)
DOTNET_TEST_PROJECTS = \
DataProvider/Nimblesite.DataProvider.Tests \
DataProvider/Nimblesite.DataProvider.Example.Tests \
Lql/Nimblesite.Lql.Tests \
Lql/Nimblesite.Lql.TypeProvider.FSharp.Tests \
Migration/Nimblesite.DataProvider.Migration.Tests \
Sync/Nimblesite.Sync.Tests \
Sync/Nimblesite.Sync.SQLite.Tests \
Sync/Nimblesite.Sync.Postgres.Tests \
Sync/Nimblesite.Sync.Integration.Tests \
Sync/Nimblesite.Sync.Http.Tests \
Reporting/Nimblesite.Reporting.Tests \
Reporting/Nimblesite.Reporting.Integration.Tests
# =============================================================================
# PRIMARY TARGETS (uniform interface — do not rename)
# =============================================================================
## build: Compile/assemble all artifacts
build:
@echo "==> Building..."
$(MAKE) _build
## test: Run full test suite with coverage enforcement
test:
@echo "==> Testing..."
$(MAKE) _test
## lint: Run all linters (fails on any warning)
lint:
@echo "==> Linting..."
$(MAKE) _lint
## fmt: Format all code in-place
fmt:
@echo "==> Formatting..."
$(MAKE) _fmt
## fmt-check: Check formatting without modifying
fmt-check:
@echo "==> Checking format..."
$(MAKE) _fmt_check
## clean: Remove all build artifacts
clean:
@echo "==> Cleaning..."
$(MAKE) _clean
## check: lint + test (pre-commit)
check: lint test
## ci: lint + test + build (full CI simulation)
ci: lint test build
## coverage: Generate HTML coverage report (runs tests first)
coverage:
@echo "==> Coverage report..."
$(MAKE) _coverage
## setup: Post-create dev environment setup (used by devcontainer)
setup:
@echo "==> Setting up development environment..."
$(MAKE) _setup
@echo "==> Setup complete. Run 'make ci' to validate."
# =============================================================================
# LANGUAGE-SPECIFIC IMPLEMENTATIONS
# =============================================================================
_build: _build_dotnet _build_rust _build_ts
_test: _test_dotnet _test_rust _test_ts
_lint: _lint_dotnet _lint_rust _lint_ts
_fmt: _fmt_dotnet _fmt_rust
_fmt_check: _fmt_check_dotnet _fmt_check_rust
_clean: _clean_dotnet _clean_rust _clean_ts
_coverage: _coverage_dotnet
_setup: _setup_dotnet _setup_ts
# =============================================================================
# COVERAGE ENFORCEMENT (shared shell logic)
# =============================================================================
# Each test target collects coverage, compares against coverage-thresholds.json,
# fails hard if below, and ratchets up if above.
#
# coverage-thresholds.json keys are SOURCE project paths, e.g.:
# "DataProvider/Nimblesite.DataProvider.Core": { "threshold": 88, "include": "..." }
#
# The SRC_KEY mapping converts test project paths -> source project keys.
# CI calls these same make targets — no duplication.
# =============================================================================
# --- C#/.NET ---
_build_dotnet:
dotnet build DataProvider.sln --configuration Release
_test_dotnet:
@for test_proj in $(DOTNET_TEST_PROJECTS); do \
SRC_KEY=$$(echo "$$test_proj" | sed 's/\.Tests$$//'); \
case "$$SRC_KEY" in \
"DataProvider/Nimblesite.DataProvider") SRC_KEY="DataProvider/Nimblesite.DataProvider.Core" ;; \
"DataProvider/Nimblesite.DataProvider.Example") ;; \
"Lql/Nimblesite.Lql") SRC_KEY="Lql/Nimblesite.Lql.Core" ;; \
"Lql/Nimblesite.Lql.TypeProvider.FSharp") ;; \
"Migration/Nimblesite.DataProvider.Migration") SRC_KEY="Migration/Nimblesite.DataProvider.Migration.Core" ;; \
"Sync/Nimblesite.Sync") SRC_KEY="Sync/Nimblesite.Sync.Core" ;; \
"Sync/Nimblesite.Sync.SQLite") ;; \
"Sync/Nimblesite.Sync.Postgres") ;; \
"Sync/Nimblesite.Sync.Integration") ;; \
"Sync/Nimblesite.Sync.Http") ;; \
"Reporting/Nimblesite.Reporting") ;; \
"Reporting/Nimblesite.Reporting.Integration") ;; \
esac; \
THRESHOLD=$$(jq -r ".projects[\"$$SRC_KEY\"].threshold // .default_threshold" coverage-thresholds.json); \
INCLUDE=$$(jq -r ".projects[\"$$SRC_KEY\"].include // empty" coverage-thresholds.json); \
echo ""; \
echo "============================================================"; \
echo "==> Testing $$SRC_KEY (via $$test_proj, threshold: $$THRESHOLD%)"; \
if [ -n "$$INCLUDE" ]; then echo " Include filter: $$INCLUDE"; fi; \
echo "============================================================"; \
rm -rf "$$test_proj/TestResults"; \
if [ -n "$$INCLUDE" ]; then \
dotnet test "$$test_proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$$test_proj/TestResults" \
--verbosity normal \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Include="$$INCLUDE"; \
else \
dotnet test "$$test_proj" --configuration Release \
--settings coverlet.runsettings \
--collect:"XPlat Code Coverage" \
--results-directory "$$test_proj/TestResults" \
--verbosity normal; \
fi; \
if [ $$? -ne 0 ]; then \
echo "FAIL [$$SRC_KEY]: Tests failed ($$test_proj)"; \
exit 1; \
fi; \
COBERTURA=$$(find "$$test_proj/TestResults" -name "coverage.cobertura.xml" -type f 2>/dev/null | head -1); \
if [ -z "$$COBERTURA" ]; then \
echo "FAIL [$$SRC_KEY]: No coverage file produced ($$test_proj)"; \
exit 1; \
fi; \
LINE_RATE=$$(sed -n 's/.*line-rate="\([0-9.]*\)".*/\1/p' "$$COBERTURA" | head -1); \
if [ -z "$$LINE_RATE" ]; then \
echo "FAIL [$$SRC_KEY]: Could not parse line-rate from $$COBERTURA"; \
exit 1; \
fi; \
COVERAGE=$$(echo "$$LINE_RATE * 100" | bc -l); \
COVERAGE_FMT=$$(printf "%.2f" $$COVERAGE); \
echo ""; \
echo " [$$SRC_KEY] Coverage: $$COVERAGE_FMT% | Threshold: $$THRESHOLD%"; \
BELOW=$$(echo "$$COVERAGE < $$THRESHOLD" | bc -l); \
if [ "$$BELOW" = "1" ]; then \
echo " FAIL [$$SRC_KEY]: $$COVERAGE_FMT% is BELOW threshold $$THRESHOLD%"; \
exit 1; \
fi; \
ABOVE=$$(echo "$$COVERAGE > $$THRESHOLD" | bc -l); \
if [ "$$ABOVE" = "1" ]; then \
NEW=$$(echo "$$COVERAGE" | awk '{print int($$1)}'); \
echo " Ratcheting threshold: $$THRESHOLD% -> $$NEW%"; \
jq ".projects[\"$$SRC_KEY\"].threshold = $$NEW" coverage-thresholds.json > coverage-thresholds.json.tmp && mv coverage-thresholds.json.tmp coverage-thresholds.json; \
fi; \
echo " PASS [$$SRC_KEY]"; \
done; \
echo ""; \
echo "==> All .NET test projects passed coverage thresholds."
_lint_dotnet:
dotnet build DataProvider.sln --configuration Release
dotnet csharpier check .
_fmt_dotnet:
dotnet csharpier format .
_fmt_check_dotnet:
dotnet csharpier check .
_clean_dotnet:
ifeq ($(OS),Windows_NT)
Get-ChildItem -Recurse -Directory -Include bin,obj -Exclude lql-lsp-rust | Remove-Item -Recurse -Force
$(RM) TestResults
else
find . -type d \( -name bin -o -name obj \) -not -path './Lql/lql-lsp-rust/*' | xargs rm -rf
$(RM) TestResults
endif
_coverage_dotnet:
$(MAKE) _test_dotnet
reportgenerator -reports:"**/TestResults/**/coverage.cobertura.xml" \
-targetdir:coverage/html -reporttypes:Html
ifeq ($(OS),Windows_NT)
Start-Process coverage/html/index.html
else ifeq ($(shell uname -s),Darwin)
open coverage/html/index.html
else
xdg-open coverage/html/index.html
endif
_setup_dotnet:
dotnet restore
dotnet tool restore
# --- RUST (LQL LSP) ---
_build_rust:
cd Lql/lql-lsp-rust && cargo build --release
_test_rust:
@THRESHOLD=$$(jq -r '.projects["Lql/lql-lsp-rust"].threshold // .default_threshold' coverage-thresholds.json); \
echo ""; \
echo "============================================================"; \
echo "==> Testing Lql/lql-lsp-rust (threshold: $$THRESHOLD%)"; \
echo "============================================================"; \
cd Lql/lql-lsp-rust && cargo tarpaulin --workspace --skip-clean \
--exclude-files 'crates/lql-parser/src/generated/*' \
--exclude-files 'crates/lql-lsp/tests/*' \
2>&1 | tee /tmp/_dp_tarpaulin_out.txt; \
TARP_EXIT=$${PIPESTATUS[0]}; \
if [ $$TARP_EXIT -ne 0 ]; then \
echo "FAIL [Lql/lql-lsp-rust]: cargo tarpaulin failed"; \
exit 1; \
fi; \
COVERAGE=$$(grep -oE '[0-9]+\.[0-9]+% coverage' /tmp/_dp_tarpaulin_out.txt | tail -1 | grep -oE '[0-9]+\.[0-9]+'); \
if [ -z "$$COVERAGE" ]; then \
echo "FAIL [Lql/lql-lsp-rust]: Could not parse coverage from tarpaulin output"; \
exit 1; \
fi; \
echo ""; \
echo " [Lql/lql-lsp-rust] Coverage: $$COVERAGE% | Threshold: $$THRESHOLD%"; \
BELOW=$$(echo "$$COVERAGE < $$THRESHOLD" | bc -l); \
if [ "$$BELOW" = "1" ]; then \
echo " FAIL [Lql/lql-lsp-rust]: $$COVERAGE% is BELOW threshold $$THRESHOLD%"; \
exit 1; \
fi; \
ABOVE=$$(echo "$$COVERAGE > $$THRESHOLD" | bc -l); \
if [ "$$ABOVE" = "1" ]; then \
NEW=$$(echo "$$COVERAGE" | awk '{print int($$1)}'); \
echo " Ratcheting threshold: $$THRESHOLD% -> $$NEW%"; \
cd "$(CURDIR)" && jq '.projects["Lql/lql-lsp-rust"].threshold = '"$$NEW" coverage-thresholds.json > coverage-thresholds.json.tmp && mv coverage-thresholds.json.tmp coverage-thresholds.json; \
fi; \
echo " PASS [Lql/lql-lsp-rust]"
_lint_rust:
cd Lql/lql-lsp-rust && cargo fmt --all --check
cd Lql/lql-lsp-rust && cargo clippy --workspace --all-targets -- -D warnings
_fmt_rust:
cd Lql/lql-lsp-rust && cargo fmt --all
_fmt_check_rust:
cd Lql/lql-lsp-rust && cargo fmt --all --check
_clean_rust:
cd Lql/lql-lsp-rust && cargo clean
# --- TYPESCRIPT (LQL Extension) ---
_build_ts:
cd Lql/LqlExtension && npm install --no-audit --no-fund && npm run compile
# Ensure the lql-lsp binary exists before running VSIX tests, so the
# extension can find a matching --version on PATH and start the LSP
# without trying to download a release from GitHub.
_ensure_lql_lsp_on_path:
@if [ ! -x "$(CURDIR)/Lql/lql-lsp-rust/target/release/lql-lsp" ] && \
[ ! -x "$(CURDIR)/Lql/lql-lsp-rust/target/debug/lql-lsp" ]; then \
echo "==> Building lql-lsp (release) for VSIX tests"; \
cd Lql/lql-lsp-rust && cargo build --release -p lql-lsp; \
fi
_test_ts: _ensure_lql_lsp_on_path
@THRESHOLD=$$(jq -r '.projects["Lql/LqlExtension"].threshold // .default_threshold' coverage-thresholds.json); \
echo ""; \
echo "============================================================"; \
echo "==> Testing Lql/LqlExtension (threshold: $$THRESHOLD%)"; \
echo "============================================================"; \
export PATH="$(CURDIR)/Lql/lql-lsp-rust/target/release:$(CURDIR)/Lql/lql-lsp-rust/target/debug:$$PATH"; \
unset ELECTRON_RUN_AS_NODE; \
echo " lql-lsp on PATH: $$(command -v lql-lsp || echo 'NOT FOUND')"; \
echo " lql-lsp --version: $$(lql-lsp --version 2>&1 || echo 'failed')"; \
cd Lql/LqlExtension && \
npx vsce package --no-git-tag-version --no-update-package-json && \
rm -rf out-cov && npx nyc instrument --include='out/**/*.js' --exclude='out/test/**' --no-all out out-cov && cp -R out-cov/. out/ && rm -rf out-cov && \
if command -v xvfb-run >/dev/null 2>&1; then \
xvfb-run -a node ./out/test/runTest.js; \
else \
node ./out/test/runTest.js; \
fi && \
npx nyc report --reporter=json-summary --reporter=text; \
if [ $$? -ne 0 ]; then \
echo "FAIL [Lql/LqlExtension]: Extension tests failed"; \
exit 1; \
fi; \
SUMMARY="$(CURDIR)/Lql/LqlExtension/coverage/coverage-summary.json"; \
if [ ! -f "$$SUMMARY" ]; then \
SUMMARY="$(CURDIR)/Lql/LqlExtension/.nyc_output/coverage-summary.json"; \
fi; \
if [ ! -f "$$SUMMARY" ]; then \
echo " WARN [Lql/LqlExtension]: No coverage summary produced (cross-process instrumentation skipped); treating as 0%"; \
COVERAGE=0; \
else \
COVERAGE=$$(jq -r '.total.lines.pct' "$$SUMMARY"); \
fi; \
echo ""; \
echo " [Lql/LqlExtension] Coverage: $$COVERAGE% | Threshold: $$THRESHOLD%"; \
BELOW=$$(echo "$$COVERAGE < $$THRESHOLD" | bc -l); \
if [ "$$BELOW" = "1" ]; then \
echo " FAIL [Lql/LqlExtension]: $$COVERAGE% is BELOW threshold $$THRESHOLD%"; \
exit 1; \
fi; \
ABOVE=$$(echo "$$COVERAGE > $$THRESHOLD" | bc -l); \
if [ "$$ABOVE" = "1" ]; then \
NEW=$$(echo "$$COVERAGE" | awk '{print int($$1)}'); \
echo " Ratcheting threshold: $$THRESHOLD% -> $$NEW%"; \
jq '.projects["Lql/LqlExtension"].threshold = '"$$NEW" "$(CURDIR)/coverage-thresholds.json" > "$(CURDIR)/coverage-thresholds.json.tmp" && mv "$(CURDIR)/coverage-thresholds.json.tmp" "$(CURDIR)/coverage-thresholds.json"; \
fi; \
echo " PASS [Lql/LqlExtension]"
_lint_ts:
cd Lql/LqlExtension && npm run lint
_clean_ts:
$(RM) Lql/LqlExtension/node_modules Lql/LqlExtension/out
_setup_ts:
cd Lql/LqlExtension && npm install --no-audit --no-fund
# =============================================================================
# HELP
# =============================================================================
help:
@echo "Available targets:"
@echo " build - Compile/assemble all artifacts"
@echo " test - Run full test suite with coverage enforcement"
@echo " lint - Run all linters (errors mode)"
@echo " fmt - Format all code in-place"
@echo " fmt-check - Check formatting (no modification)"
@echo " clean - Remove build artifacts"
@echo " check - lint + test (pre-commit)"
@echo " ci - lint + test + build (full CI)"
@echo " coverage - Generate and open HTML coverage report"
@echo " setup - Post-create dev environment setup"