upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-18 17:33:41 +0530
committerYour Name <you@example.com>2026-05-18 17:33:41 +0530
commit8f00db9d57e9e06df7fbf3e7285ae9256d21d716 (patch)
treeaec979f3beb42077713cd92217786c4552182361
parent02d699d22989fd8bda091b00657d0dd1b7406247 (diff)
parente0eeeab6eb714ab22d0bc6697730b71d2381746e (diff)
merge: latest master with per-board locks and port assignments
-rw-r--r--Makefile155
1 files changed, 141 insertions, 14 deletions
diff --git a/Makefile b/Makefile
index c84acdf..044ad6b 100644
--- a/Makefile
+++ b/Makefile
@@ -7,8 +7,8 @@ export
7IDF_PATH ?= $(HOME)/esp/esp-idf 7IDF_PATH ?= $(HOME)/esp/esp-idf
8PROJECT_DIR := $(shell pwd) 8PROJECT_DIR := $(shell pwd)
9BUILD_DIR := $(PROJECT_DIR)/build 9BUILD_DIR := $(PROJECT_DIR)/build
10PORT_A ?= /dev/ttyACM0 10PORT_A ?= /dev/ttyACM1
11PORT_B ?= /dev/ttyACM1 11PORT_B ?= /dev/ttyACM2
12PORT ?= $(PORT_A) 12PORT ?= $(PORT_A)
13BAUD ?= 460800 13BAUD ?= 460800
14TARGET ?= esp32s3 14TARGET ?= esp32s3
@@ -19,6 +19,62 @@ 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
@@ -27,6 +83,7 @@ TOLLGATE_IP ?= 10.192.45.1
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
29.PHONY: cvm-pubkey cvm-test-tool cvm-announce 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
30 87
31help: 88help:
32 @echo "TollGate ESP32 — Makefile" 89 @echo "TollGate ESP32 — Makefile"
@@ -129,13 +186,18 @@ setup:
129 186
130flash: build 187flash: build
131 @echo "=== Flashing to $(PORT) ===" 188 @echo "=== Flashing to $(PORT) ==="
132 . $(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
133 191
134flash-a: PORT=$(PORT_A) 192flash-a: build
135flash-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
136 196
137flash-b: PORT=$(PORT_B) 197flash-b: build
138flash-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
139 201
140build: 202build:
141 @echo "=== Building $(TARGET) ===" 203 @echo "=== Building $(TARGET) ==="
@@ -143,14 +205,13 @@ build:
143 idf.py set-target $(TARGET) 2>/dev/null; \ 205 idf.py set-target $(TARGET) 2>/dev/null; \
144 idf.py build 206 idf.py build
145 207
146monitor: 208monitor-a:
147 . $(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
148 211
149monitor-a: PORT=$(PORT_A) 212monitor-b:
150monitor-a: monitor 213 $(call require_lock_b)
151 214 . $(IDF_PATH)/export.sh && idf.py -p $(PORT_B) monitor
152monitor-b: PORT=$(PORT_B)
153monitor-b: monitor
154 215
155# ────────────────────────────────────────────── 216# ──────────────────────────────────────────────
156# Testing 217# Testing
@@ -164,6 +225,7 @@ test-integration: test-api test-network test-reset-auth test-dns-firewall test-c
164 @echo "=== Integration tests passed ===" 225 @echo "=== Integration tests passed ==="
165 226
166test-e2e: 227test-e2e:
228 $(call _require_board_lock)
167 @echo "=== Running Playwright E2E tests ===" 229 @echo "=== Running Playwright E2E tests ==="
168 cd tests/e2e && npx playwright test 230 cd tests/e2e && npx playwright test
169 231
@@ -174,38 +236,47 @@ test: test-unit test-integration
174 @echo "=== Tests passed ===" 236 @echo "=== Tests passed ==="
175 237
176test-smoke: 238test-smoke:
239 $(call _require_board_lock)
177 @echo "=== Running smoke test (30s) ===" 240 @echo "=== Running smoke test (30s) ==="
178 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/smoke.mjs 241 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/smoke.mjs
179 242
180test-api: 243test-api:
244 $(call _require_board_lock)
181 @echo "=== Running API tests ===" 245 @echo "=== Running API tests ==="
182 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/api.mjs 246 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/api.mjs
183 247
184test-network: 248test-network:
249 $(call _require_board_lock)
185 @echo "=== Running network tests ===" 250 @echo "=== Running network tests ==="
186 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/network.mjs 251 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/network.mjs
187 252
188test-portal: 253test-portal:
254 $(call _require_board_lock)
189 @echo "=== Running Playwright portal tests ===" 255 @echo "=== Running Playwright portal tests ==="
190 cd tests/e2e && npx playwright test captive-portal.spec.mjs 256 cd tests/e2e && npx playwright test captive-portal.spec.mjs
191 257
192test-payment: 258test-payment:
259 $(call _require_board_lock)
193 @echo "=== Running payment tests ===" 260 @echo "=== Running payment tests ==="
194 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/phase2.mjs 261 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/phase2.mjs
195 262
196test-reset-auth: 263test-reset-auth:
264 $(call _require_board_lock)
197 @echo "=== Running reset auth test ===" 265 @echo "=== Running reset auth test ==="
198 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-reset-auth.mjs 266 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-reset-auth.mjs
199 267
200test-session-expiry: 268test-session-expiry:
269 $(call _require_board_lock)
201 @echo "=== Running session expiry test (65s wait, ~80s total) ===" 270 @echo "=== Running session expiry test (65s wait, ~80s total) ==="
202 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-session-expiry.mjs 271 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-session-expiry.mjs
203 272
204test-dns-firewall: 273test-dns-firewall:
274 $(call _require_board_lock)
205 @echo "=== Running DNS + firewall test ===" 275 @echo "=== Running DNS + firewall test ==="
206 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-dns-firewall.mjs 276 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-dns-firewall.mjs
207 277
208test-cvm: 278test-cvm:
279 $(call _require_board_lock)
209 @echo "=== Running CVM integration test ===" 280 @echo "=== Running CVM integration test ==="
210 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-cvm.mjs 281 TOLLGATE_IP=$(TOLLGATE_IP) $(NODE) tests/integration/test-cvm.mjs
211 282
@@ -255,6 +326,7 @@ cvm-announce:
255 curl -s http://$(TOLLGATE_IP):2121/ | head -1 || echo "Board not reachable" 326 curl -s http://$(TOLLGATE_IP):2121/ | head -1 || echo "Board not reachable"
256 327
257cvm-test-tool: 328cvm-test-tool:
329 $(call _require_board_lock)
258 @METHOD=$${METHOD:-get_config}; \ 330 @METHOD=$${METHOD:-get_config}; \
259 PARAMS=$${PARAMS:-{}}; \ 331 PARAMS=$${PARAMS:-{}}; \
260 echo "=== Calling $$METHOD via CVM ==="; \ 332 echo "=== Calling $$METHOD via CVM ==="; \
@@ -275,16 +347,19 @@ clean:
275 . $(IDF_PATH)/export.sh && idf.py fullclean 347 . $(IDF_PATH)/export.sh && idf.py fullclean
276 348
277erase-nvs: 349erase-nvs:
350 $(call _require_board_lock)
278 @echo "=== Erasing NVS on $(PORT) ===" 351 @echo "=== Erasing NVS on $(PORT) ==="
279 . $(IDF_PATH)/export.sh && \ 352 . $(IDF_PATH)/export.sh && \
280 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}'); \
281 python3 -m esptool --port $(PORT) erase_region $$partition_offset 0x6000 354 python3 -m esptool --port $(PORT) erase_region $$partition_offset 0x6000
282 355
283reset: 356reset:
357 $(call _require_board_lock)
284 @echo "=== Resetting device on $(PORT) ===" 358 @echo "=== Resetting device on $(PORT) ==="
285 python3 -m esptool --port $(PORT) run 2>/dev/null || true 359 python3 -m esptool --port $(PORT) run 2>/dev/null || true
286 360
287serial-log: 361serial-log:
362 $(call _require_board_lock)
288 @echo "=== Capturing serial output from $(PORT) ===" 363 @echo "=== Capturing serial output from $(PORT) ==="
289 python3 -c "import serial; s=serial.Serial('$(PORT)',115200,timeout=1); \ 364 python3 -c "import serial; s=serial.Serial('$(PORT)',115200,timeout=1); \
290 [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'')]"
@@ -293,3 +368,55 @@ bootstrap-config:
293 @echo "=== Bootstrapping config.json ===" 368 @echo "=== Bootstrapping config.json ==="
294 @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
295 @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