upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/Makefile
diff options
context:
space:
mode:
Diffstat (limited to 'Makefile')
-rw-r--r--Makefile192
1 files changed, 178 insertions, 14 deletions
diff --git a/Makefile b/Makefile
index 40f0e7b..10b7359 100644
--- a/Makefile
+++ b/Makefile
@@ -19,13 +19,71 @@ PYTHON ?= python3
19 19
20TOLLGATE_IP ?= 10.192.45.1 20TOLLGATE_IP ?= 10.192.45.1
21 21
22BOARD ?= b
23
24HARDWARE_LOCK_DIR := /home/c03rad0r/physical-router-test-automation/locks
25
26RED := \033[31m
27GREEN := \033[32m
28YELLOW := \033[33m
29BOLD := \033[1m
30RESET := \033[0m
31
32define require_lock_a
33 @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-a.lock" ]; then \
34 echo "$(RED)$(BOLD)Board A not locked — run 'make lock-a PHASE=\"description\"' first$(RESET)"; \
35 echo "$(YELLOW)Another LLM session may be using Board A.$(RESET)"; \
36 exit 1; \
37 fi
38endef
39
40define require_lock_b
41 @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-b.lock" ]; then \
42 echo "$(RED)$(BOLD)Board B not locked — run 'make lock-b PHASE=\"description\"' first$(RESET)"; \
43 echo "$(YELLOW)Another LLM session may be using Board B.$(RESET)"; \
44 exit 1; \
45 fi
46endef
47
48define _require_board_lock
49 @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-$(BOARD).lock" ]; then \
50 echo "$(RED)$(BOLD)Board $(BOARD) not locked — run 'make lock-$(BOARD) PHASE=\"description\"' first$(RESET)"; \
51 echo "$(YELLOW)Another LLM session may be using Board $(BOARD).$(RESET)"; \
52 exit 1; \
53 fi
54endef
55
56define _acquire_lock
57 @if [ -f "$(HARDWARE_LOCK_DIR)/$(1).lock" ]; then \
58 echo "$(RED)$(BOLD)Cannot acquire lock — $(1) already locked:$(RESET)"; \
59 echo ""; \
60 cat $(HARDWARE_LOCK_DIR)/$(1).lock | sed 's/^/ /'; \
61 echo ""; \
62 echo "$(YELLOW)Use 'make force-unlock-$(1)' to override.$(RESET)"; \
63 exit 1; \
64 fi; \
65 branch=$$(git branch --show-current 2>/dev/null || echo "unknown"); \
66 worktree=$$(pwd); \
67 echo "locked: true" > $(HARDWARE_LOCK_DIR)/$(1).lock; \
68 echo "board: $(1)" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \
69 echo "branch: $$branch" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \
70 echo "worktree: $$worktree" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \
71 echo "session: $$USER@$$HOSTNAME" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \
72 echo "timestamp: $$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \
73 echo "phase: $(PHASE)" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \
74 echo "$(GREEN)$(BOLD)$(1) lock acquired$(RESET)"; \
75 cat $(HARDWARE_LOCK_DIR)/$(1).lock
76endef
77
22.PHONY: help setup detect-ports detect-chip detect-all 78.PHONY: help setup detect-ports detect-chip detect-all
23.PHONY: flash flash-a flash-b monitor monitor-a monitor-b 79.PHONY: flash flash-a flash-b monitor monitor-a monitor-b
24.PHONY: test test-unit test-integration test-e2e test-all 80.PHONY: test test-unit test-integration test-e2e test-all
25.PHONY: test-smoke test-api test-network test-portal test-payment 81.PHONY: test-smoke test-api test-network test-portal test-payment
26.PHONY: test-reset-auth test-session-expiry test-dns-firewall 82.PHONY: test-reset-auth test-session-expiry test-dns-firewall test-cvm
27.PHONY: tokens wallet-setup wallet-info wallet-balance mint-token send-token 83.PHONY: tokens wallet-setup wallet-info wallet-balance mint-token send-token
28.PHONY: clean erase-nvs reset serial-log bootstrap-config 84.PHONY: clean erase-nvs reset serial-log bootstrap-config
85.PHONY: cvm-pubkey cvm-test-tool cvm-announce
86.PHONY: lock-a lock-b unlock-a unlock-b force-unlock-a force-unlock-b lock-status
29 87
30help: 88help:
31 @echo "TollGate ESP32 — Makefile" 89 @echo "TollGate ESP32 — Makefile"
@@ -50,6 +108,12 @@ help:
50 @echo " test-reset-auth Reset auth + per-client NAT filter test" 108 @echo " test-reset-auth Reset auth + per-client NAT filter test"
51 @echo " test-dns-firewall DNS hijack + NAT filter test" 109 @echo " test-dns-firewall DNS hijack + NAT filter test"
52 @echo " test-session-expiry Session lifecycle with 65s expiry wait" 110 @echo " test-session-expiry Session lifecycle with 65s expiry wait"
111 @echo " test-cvm ContextVM protocol integration test"
112 @echo ""
113 @echo "ContextVM:"
114 @echo " cvm-pubkey Print board's ContextVM npub"
115 @echo " cvm-announce Trigger re-publish of CEP-6 announcements"
116 @echo " cvm-test-tool Send single MCP tools/call (METHOD=get_config)"
53 @echo "" 117 @echo ""
54 @echo "Wallet:" 118 @echo "Wallet:"
55 @echo " wallet-setup Initialize nutshell wallet for test mint" 119 @echo " wallet-setup Initialize nutshell wallet for test mint"
@@ -122,13 +186,18 @@ setup:
122 186
123flash: build 187flash: build
124 @echo "=== Flashing to $(PORT) ===" 188 @echo "=== Flashing to $(PORT) ==="
125 . $(IDF_PATH)/export.sh && idf.py -p $(PORT) -b $(BAUD) flash 189 @echo "$(RED)Error: use 'make flash-a' or 'make flash-b' (per-board lock required)$(RESET)"
190 @exit 1
126 191
127flash-a: PORT=$(PORT_A) 192flash-a: build
128flash-a: flash 193 $(call require_lock_a)
194 @echo "=== Flashing to $(PORT_A) (Board A) ==="
195 . $(IDF_PATH)/export.sh && idf.py -p $(PORT_A) -b $(BAUD) flash
129 196
130flash-b: PORT=$(PORT_B) 197flash-b: build
131flash-b: flash 198 $(call require_lock_b)
199 @echo "=== Flashing to $(PORT_B) (Board B) ==="
200 . $(IDF_PATH)/export.sh && idf.py -p $(PORT_B) -b $(BAUD) flash
132 201
133build: 202build:
134 @echo "=== Building $(TARGET) ===" 203 @echo "=== Building $(TARGET) ==="
@@ -136,14 +205,13 @@ build:
136 idf.py set-target $(TARGET) 2>/dev/null; \ 205 idf.py set-target $(TARGET) 2>/dev/null; \
137 idf.py build 206 idf.py build
138 207
139monitor: 208monitor-a:
140 . $(IDF_PATH)/export.sh && idf.py -p $(PORT) monitor 209 $(call require_lock_a)
210 . $(IDF_PATH)/export.sh && idf.py -p $(PORT_A) monitor
141 211
142monitor-a: PORT=$(PORT_A) 212monitor-b:
143monitor-a: monitor 213 $(call require_lock_b)
144 214 . $(IDF_PATH)/export.sh && idf.py -p $(PORT_B) monitor
145monitor-b: PORT=$(PORT_B)
146monitor-b: monitor
147 215
148# ────────────────────────────────────────────── 216# ──────────────────────────────────────────────
149# Testing 217# Testing
@@ -153,10 +221,11 @@ test-unit:
153 @echo "=== Running host unit tests ===" 221 @echo "=== Running host unit tests ==="
154 $(MAKE) -C tests/unit test 222 $(MAKE) -C tests/unit test
155 223
156test-integration: test-api test-network test-reset-auth test-dns-firewall 224test-integration: test-api test-network test-reset-auth test-dns-firewall test-cvm
157 @echo "=== Integration tests passed ===" 225 @echo "=== Integration tests passed ==="
158 226
159test-e2e: 227test-e2e:
228 $(call _require_board_lock)
160 @echo "=== Running Playwright E2E tests ===" 229 @echo "=== Running Playwright E2E tests ==="
161 cd tests/e2e && npx playwright test 230 cd tests/e2e && npx playwright test
162 231
@@ -167,37 +236,50 @@ test: test-unit test-integration
167 @echo "=== Tests passed ===" 236 @echo "=== Tests passed ==="
168 237
169test-smoke: 238test-smoke:
239 $(call _require_board_lock)
170 @echo "=== Running smoke test (30s) ===" 240 @echo "=== Running smoke test (30s) ==="
171 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/smoke.mjs 241 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/smoke.mjs
172 242
173test-api: 243test-api:
244 $(call _require_board_lock)
174 @echo "=== Running API tests ===" 245 @echo "=== Running API tests ==="
175 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/api.mjs 246 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/api.mjs
176 247
177test-network: 248test-network:
249 $(call _require_board_lock)
178 @echo "=== Running network tests ===" 250 @echo "=== Running network tests ==="
179 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/network.mjs 251 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/network.mjs
180 252
181test-portal: 253test-portal:
254 $(call _require_board_lock)
182 @echo "=== Running Playwright portal tests ===" 255 @echo "=== Running Playwright portal tests ==="
183 cd tests/e2e && npx playwright test captive-portal.spec.mjs 256 cd tests/e2e && npx playwright test captive-portal.spec.mjs
184 257
185test-payment: 258test-payment:
259 $(call _require_board_lock)
186 @echo "=== Running payment tests ===" 260 @echo "=== Running payment tests ==="
187 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/phase2.mjs 261 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/phase2.mjs
188 262
189test-reset-auth: 263test-reset-auth:
264 $(call _require_board_lock)
190 @echo "=== Running reset auth test ===" 265 @echo "=== Running reset auth test ==="
191 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-reset-auth.mjs 266 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-reset-auth.mjs
192 267
193test-session-expiry: 268test-session-expiry:
269 $(call _require_board_lock)
194 @echo "=== Running session expiry test (65s wait, ~80s total) ===" 270 @echo "=== Running session expiry test (65s wait, ~80s total) ==="
195 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-session-expiry.mjs 271 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-session-expiry.mjs
196 272
197test-dns-firewall: 273test-dns-firewall:
274 $(call _require_board_lock)
198 @echo "=== Running DNS + firewall test ===" 275 @echo "=== Running DNS + firewall test ==="
199 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-dns-firewall.mjs 276 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-dns-firewall.mjs
200 277
278test-cvm:
279 $(call _require_board_lock)
280 @echo "=== Running CVM integration test ==="
281 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-cvm.mjs
282
201# ────────────────────────────────────────────── 283# ──────────────────────────────────────────────
202# Wallet 284# Wallet
203# ────────────────────────────────────────────── 285# ──────────────────────────────────────────────
@@ -230,6 +312,33 @@ send-token:
230tokens: send-token 312tokens: send-token
231 313
232# ────────────────────────────────────────────── 314# ──────────────────────────────────────────────
315# ContextVM
316# ──────────────────────────────────────────────
317
318cvm-pubkey:
319 @echo "=== Board ContextVM npub ==="
320 @nak key public a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 | xargs -I{} nak encode npub {}
321 @echo ""
322 @echo "Search for this npub on https://contextvm.org/servers"
323
324cvm-announce:
325 @echo "=== Triggering CEP-6 re-announcement ==="
326 curl -s http://$(TOLLGATE_IP):2121/ | head -1 || echo "Board not reachable"
327
328cvm-test-tool:
329 $(call _require_board_lock)
330 @METHOD=$${METHOD:-get_config}; \
331 PARAMS=$${PARAMS:-{}}; \
332 echo "=== Calling $$METHOD via CVM ==="; \
333 NPUB_HEX=$$(nak key public a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2); \
334 CONTENT="$$(echo "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"$$METHOD\",\"arguments\":$$PARAMS}}" | jq -c .)"; \
335 EVENT_JSON="$$(nak event --kind 25910 --tag p=$$NPUB_HEX --content "$$CONTENT" wss://relay.damus.io 2>/dev/null)"; \
336 echo "Published: $$EVENT_JSON"; \
337 echo "Waiting for response..."; \
338 sleep 3; \
339 nak req -k 25910 -a $$NPUB_HEX -l 5 wss://relay.damus.io
340
341# ──────────────────────────────────────────────
233# Utilities 342# Utilities
234# ────────────────────────────────────────────── 343# ──────────────────────────────────────────────
235 344
@@ -238,16 +347,19 @@ clean:
238 . $(IDF_PATH)/export.sh && idf.py fullclean 347 . $(IDF_PATH)/export.sh && idf.py fullclean
239 348
240erase-nvs: 349erase-nvs:
350 $(call _require_board_lock)
241 @echo "=== Erasing NVS on $(PORT) ===" 351 @echo "=== Erasing NVS on $(PORT) ==="
242 . $(IDF_PATH)/export.sh && \ 352 . $(IDF_PATH)/export.sh && \
243 partition_offset=$$(idf.py partition-table 2>/dev/null | grep nvs | awk '{print $$2}'); \ 353 partition_offset=$$(idf.py partition-table 2>/dev/null | grep nvs | awk '{print $$2}'); \
244 python3 -m esptool --port $(PORT) erase_region $$partition_offset 0x6000 354 python3 -m esptool --port $(PORT) erase_region $$partition_offset 0x6000
245 355
246reset: 356reset:
357 $(call _require_board_lock)
247 @echo "=== Resetting device on $(PORT) ===" 358 @echo "=== Resetting device on $(PORT) ==="
248 python3 -m esptool --port $(PORT) run 2>/dev/null || true 359 python3 -m esptool --port $(PORT) run 2>/dev/null || true
249 360
250serial-log: 361serial-log:
362 $(call _require_board_lock)
251 @echo "=== Capturing serial output from $(PORT) ===" 363 @echo "=== Capturing serial output from $(PORT) ==="
252 python3 -c "import serial; s=serial.Serial('$(PORT)',115200,timeout=1); \ 364 python3 -c "import serial; s=serial.Serial('$(PORT)',115200,timeout=1); \
253 [print(s.readline().decode(errors='replace'),end='') for _ in iter(lambda: s.readline(), b'')]" 365 [print(s.readline().decode(errors='replace'),end='') for _ in iter(lambda: s.readline(), b'')]"
@@ -256,3 +368,55 @@ bootstrap-config:
256 @echo "=== Bootstrapping config.json ===" 368 @echo "=== Bootstrapping config.json ==="
257 @echo '{"wifi_networks":[{"ssid":"$(WIFI_SSID)","password":"$(WIFI_PASSWORD)"}],"ap_ssid":"$(AP_SSID)","ap_password":"$(AP_PASSWORD)","mint_url":"$(MINT_URL)","lnurl_url":"$(LNURL_URL)","price_per_step":$(PRICE_PER_STEP),"step_size_ms":$(STEP_SIZE)}' > main/config.json 369 @echo '{"wifi_networks":[{"ssid":"$(WIFI_SSID)","password":"$(WIFI_PASSWORD)"}],"ap_ssid":"$(AP_SSID)","ap_password":"$(AP_PASSWORD)","mint_url":"$(MINT_URL)","lnurl_url":"$(LNURL_URL)","price_per_step":$(PRICE_PER_STEP),"step_size_ms":$(STEP_SIZE)}' > main/config.json
258 @echo "Config written to main/config.json" 370 @echo "Config written to main/config.json"
371
372# ──────────────────────────────────────────────
373# Per-Board Hardware Locks
374# ──────────────────────────────────────────────
375
376lock-a: ## Acquire Board A lock (set PHASE="description")
377 $(call _acquire_lock,board-a)
378
379lock-b: ## Acquire Board B lock (set PHASE="description")
380 $(call _acquire_lock,board-b)
381
382unlock-a: ## Release Board A lock
383 @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-a.lock" ]; then \
384 echo "$(YELLOW)Board A not locked.$(RESET)"; exit 0; \
385 fi; \
386 rm -f $(HARDWARE_LOCK_DIR)/board-a.lock; \
387 echo "$(GREEN)Board A lock released.$(RESET)"
388
389unlock-b: ## Release Board B lock
390 @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-b.lock" ]; then \
391 echo "$(YELLOW)Board B not locked.$(RESET)"; exit 0; \
392 fi; \
393 rm -f $(HARDWARE_LOCK_DIR)/board-b.lock; \
394 echo "$(GREEN)Board B lock released.$(RESET)"
395
396force-unlock-a: ## Force-release Board A lock
397 @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-a.lock" ]; then \
398 echo "$(YELLOW)Board A not locked.$(RESET)"; exit 0; \
399 fi; \
400 echo "$(RED)$(BOLD)WARNING: Force-releasing Board A!$(RESET)"; \
401 cat $(HARDWARE_LOCK_DIR)/board-a.lock | sed 's/^/ /'; \
402 rm -f $(HARDWARE_LOCK_DIR)/board-a.lock; \
403 echo "$(GREEN)Board A force-released.$(RESET)"
404
405force-unlock-b: ## Force-release Board B lock
406 @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-b.lock" ]; then \
407 echo "$(YELLOW)Board B not locked.$(RESET)"; exit 0; \
408 fi; \
409 echo "$(RED)$(BOLD)WARNING: Force-releasing Board B!$(RESET)"; \
410 cat $(HARDWARE_LOCK_DIR)/board-b.lock | sed 's/^/ /'; \
411 rm -f $(HARDWARE_LOCK_DIR)/board-b.lock; \
412 echo "$(GREEN)Board B force-released.$(RESET)"
413
414lock-status: ## Show all board lock statuses
415 @for board in a b; do \
416 if [ -f "$(HARDWARE_LOCK_DIR)/board-$$board.lock" ]; then \
417 echo "$(YELLOW)Board $$board: LOCKED$(RESET)"; \
418 cat $(HARDWARE_LOCK_DIR)/board-$$board.lock | sed 's/^/ /'; \
419 else \
420 echo "Board $$board: $(GREEN)available$(RESET)"; \
421 fi; \
422 done