GODOT := $(shell command -v godot4 2>/dev/null || command -v godot 2>/dev/null) .PHONY: help setup build client server game stop test lint ci ci-client ci-server clean \ decisions-sync decisions-coverage decisions-active decisions-orphan \ db-backup db-install validate-content content-ron check-fact-ids setup-hooks \ pre-pr pre-pr-lint pre-pr-build pre-pr-test pre-pr-validate pre-pr-fixtures \ pre-pr-server pre-pr-client pre-pr-content \ fixtures-client # --- Configuration --- GODOT_VERSION ?= 4.6 # Default help: @echo "The Settled Reach — Development Commands" @echo "" @echo " make setup Install dev dependencies (Rust, Godot, tooling)" @echo " make build Build client and server" @echo " make game Build and run the full game (server + client)" @echo " make stop Stop any running server instance" @echo " make client Run the Godot client (test mode)" @echo " make server Run the Rust simulation server" @echo " make test Run all tests" @echo " make lint Run all linters" @echo " make ci Run full CI pipeline locally" @echo " make ci-client Run client CI checks" @echo " make ci-server Run server CI checks" @echo " make clean Remove build artifacts and caches" @echo "" @echo " make db-backup Backup shared database to git (main only)" @echo " make db-install Restore shared database from backup" @echo "" @echo " make decisions-sync Sync decisions/*.md into SQLite" @echo " make decisions-coverage Decision-to-ticket coverage by domain" @echo " make decisions-active List active decisions" @echo " make decisions-orphan Decisions without implementing tickets" @echo " make validate-content Validate content YAML against schemas" @echo " make check-fact-ids Check fact_id references against knowledge catalogs" @echo " make content-ron Convert content YAML to RON (build-time)" @echo " make fixtures-client Generate GDScript->Rust cross-encoder fixtures (#475)" @echo "" @echo " make pre-pr Run all pre-PR checks (lint, build, test, validate, fixtures)" @echo " make pre-pr-server Server-scoped pre-PR (lint, build, test, fixtures)" @echo " make pre-pr-client Client-scoped pre-PR (lint, build, test)" @echo " make pre-pr-content Content-scoped pre-PR (schema + cross-ref validation)" @echo "" @echo " make setup-hooks Install pre-commit hooks (included in setup)" @echo "" @echo " GODOT_VERSION=4.6 make setup Override Godot version" # --- Setup --- setup: setup-rust setup-godot setup-tooling setup-hooks decisions-sync @echo "Dev environment ready." setup-rust: @tooling/install-rust setup-godot: @GODOT_VERSION=$(GODOT_VERSION) tooling/install-godot setup-tooling: @echo "Checking tooling dependencies..." @command -v python3 >/dev/null 2>&1 || { echo "Install Python 3"; exit 1; } @command -v curl >/dev/null 2>&1 || { echo "Install curl (required for Godot download)"; exit 1; } @command -v unzip >/dev/null 2>&1 || { echo "Install unzip (required for Godot download)"; exit 1; } setup-hooks: @git config core.hooksPath .config/hooks @echo "Git hooks path set to .config/hooks" # --- Build --- build: build-server build-client build-server: cd server && cargo build build-client: @test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; } $(GODOT) --headless --path client --quit # --- Run --- server: cd server && cargo run client: @test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; } $(GODOT) --path client game: stop build-server @test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; } @echo "Starting server..." @cd server && cargo run & @sleep 2 @echo "Starting client..." @SR_LIVE=1 $(GODOT) --path client @$(MAKE) stop stop: @lsof -ti :9876 | xargs -r kill 2>/dev/null || true @echo "Stopped any running server on port 9876" # --- Test --- test: test-server test-client test-server: cd server && cargo nextest run fixtures: cd server && cargo test --test gen_fixtures -- --ignored fixtures-client: @test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; } @echo "Generating GDScript fixtures for Rust decoder..." $(GODOT) --headless --path client -s res://tests/gen_client_fixtures.gd @echo "Verifying Rust can decode GDScript fixtures..." cd server && cargo test --test serialization gdscript_generated_fixtures @echo "--- Fixtures-client: PASS ---" test-client: @test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; } $(GODOT) --headless --path client -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode -a res://tests/ # --- Lint --- lint: lint-server lint-client lint-server: cd server && cargo clippy -- -D warnings cd server && cargo fmt --check lint-client: @test -n "$(GODOT)" || { echo "Godot not found. Run 'make setup' first."; exit 1; } @echo "Checking GDScript for errors..." @$(GODOT) --headless --path client --quit 2>&1 | grep -i "SCRIPT ERROR" && { echo "GDScript errors found"; exit 1; } || echo "No script errors found" # --- Pre-PR verification --- pre-pr: pre-pr-lint pre-pr-build pre-pr-test pre-pr-validate pre-pr-fixtures @echo "" @echo "=== PRE-PR: ALL CHECKS PASSED ===" @echo "Safe to create PR." pre-pr-lint: lint-server lint-client @echo "--- Lint: PASS ---" pre-pr-build: build-server build-client @echo "--- Build: PASS ---" pre-pr-test: test-server test-client @echo "--- Tests: PASS ---" pre-pr-validate: validate-content check-fact-ids @echo "--- Content validation: PASS ---" pre-pr-fixtures: @echo "Checking Rust->GDScript fixture staleness..." @cd server && cargo test --test gen_fixtures -- --ignored @if git diff --quiet client/tests/fixtures/; then \ echo "--- Rust fixtures: UP TO DATE ---"; \ else \ echo ""; \ echo "--- RUST FIXTURES STALE ---"; \ echo " Protocol changed but fixtures not regenerated."; \ echo " Stale fixtures make all client tests FALSE POSITIVES."; \ echo ""; \ echo " Changed files:"; \ git diff --stat client/tests/fixtures/; \ echo ""; \ echo " Fix: commit the updated fixtures with your protocol change."; \ exit 1; \ fi @echo "Checking GDScript->Rust fixture staleness..." @$(MAKE) fixtures-client @if git diff --quiet server/tests/fixtures/gdscript/; then \ echo "--- GDScript fixtures: UP TO DATE ---"; \ else \ echo ""; \ echo "--- GDSCRIPT FIXTURES STALE ---"; \ echo " Protocol changed but GDScript fixtures not regenerated."; \ echo " Stale fixtures make cross-encoder tests FALSE POSITIVES."; \ echo ""; \ echo " Changed files:"; \ git diff --stat server/tests/fixtures/gdscript/; \ echo ""; \ echo " Fix: commit the updated fixtures with your protocol change."; \ exit 1; \ fi # Branch-specific variants (faster, scope-appropriate) pre-pr-server: lint-server build-server test-server pre-pr-fixtures @echo "=== Server pre-PR: PASSED ===" pre-pr-client: lint-client build-client test-client @echo "=== Client pre-PR: PASSED ===" pre-pr-content: validate-content check-fact-ids @echo "=== Content pre-PR: PASSED ===" # --- CI (run locally) --- ci: ci-server ci-client ci-server: lint-server build-server test-server ci-client: lint-client build-client test-client # --- Database --- db-backup: @tooling/db-backup db-install: @tooling/db-install # --- Decisions --- decisions-sync: @db/connectors/decisions-sync decisions-coverage: @db/connectors/sqlite-query "SELECT d.domain, COUNT(DISTINCT d.id) as decisions, COUNT(DISTINCT t.decision_ref) as with_tickets FROM decisions d LEFT JOIN tickets t ON d.id = t.decision_ref WHERE d.status='active' AND d.type='confirmed' GROUP BY d.domain" decisions-active: @db/connectors/sqlite-query "SELECT id, domain, title FROM decisions WHERE status='active' AND type='confirmed' ORDER BY domain, id" decisions-orphan: @db/connectors/sqlite-query "SELECT id, title FROM decisions WHERE type='confirmed' AND status='active' AND id NOT IN (SELECT DISTINCT decision_ref FROM tickets WHERE decision_ref IS NOT NULL)" # --- Content Validation --- validate-content: @tooling/validate-content check-fact-ids: @tooling/check-fact-ids content-ron: cd tooling/content-converter && cargo build --release tooling/content-converter/target/release/content-converter --input content --output content-ron --verbose # --- Clean --- clean: cd server && cargo clean || true rm -rf .cache/* rm -rf client/.godot client/reports @echo "Clean complete."