Isolate Godot validation profile

This commit is contained in:
gamer147
2026-08-02 15:26:34 -04:00
parent df1e289a2d
commit 8c8ae0f56b
3 changed files with 25 additions and 6 deletions

View File

@@ -26,7 +26,7 @@ whenever a tool's inputs/outputs change.**
| Tool | Purpose | Run | Reads → Writes |
|---|---|---|---|
| `validate.py` | Layered project validation front door. `core` regenerates/runtime-checks opcode metadata, lints canonical registries, runs pure Python tooling tests and all .NET engine tests, checks generated opcode references, and runs `git diff --check`. `workspace` adds corpus-derived global generation, the real-data Python suites, full SYS4 decode, and Python RECOVER. `runtime` adds the Godot C# build and forced-portable threaded self-test. `full` combines all phases and adds the booted faithful-wait C# scene sweep. Selected prerequisites are strict: an unavailable game/corpus/Godot requirement fails before execution instead of becoming a green skip. Each gate has a timeout and UTF-8 log under `build/validation/validate-<timestamp>/`; the final table reports results/durations and a before/after Godot-process leak audit. | `validate.py` (defaults to `--level full`) · `--level core|workspace|runtime|full` · `--godot <console>` · `--game-root <install>` · `--verbose` · `--fail-fast` | sources + selected toolchain/game/corpus prerequisites → console summary + ⚙ `build/validation/validate-*/<gate>.log` |
| `validate.py` | Layered project validation front door. `core` regenerates/runtime-checks opcode metadata, lints canonical registries, runs pure Python tooling tests and all .NET engine tests, checks generated opcode references, and runs `git diff --check`. `workspace` adds corpus-derived global generation, the real-data Python suites, full SYS4 decode, and Python RECOVER. `runtime` adds the Godot C# build and forced-portable threaded self-test. `full` combines all phases and adds the booted faithful-wait C# scene sweep. Selected prerequisites are strict: an unavailable game/corpus/Godot requirement fails before execution instead of becoming a green skip. Each gate has a timeout and UTF-8 log under `build/validation/validate-<timestamp>/`; Godot receives an isolated validation-owned user-data/log root there so it cannot read or modify the developer's saves/settings. The final table reports results/durations and a before/after Godot-process leak audit. | `validate.py` (defaults to `--level full`) · `--level core|workspace|runtime|full` · `--godot <console>` · `--game-root <install>` · `--verbose` · `--fail-fast` | sources + selected toolchain/game/corpus prerequisites → console summary + ⚙ `build/validation/validate-*/<gate>.log` |
| `test_validate.py` | Pure tests for launcher-equivalent explicit/environment/PATH/conventional resolution precedence, invalid-explicit hard failure, level composition, and final gate ordering. | `test_validate.py` | temporary files only |
Levels are cumulative around `core`: `workspace` means core+workspace-corpus, `runtime` means core+Godot,

View File

@@ -90,11 +90,16 @@ class GatePlanTests(unittest.TestCase):
def test_full_contains_every_phase(self) -> None:
fake_godot = Path("godot")
fake_root = Path("game")
keys = {gate.key for gate in validate.build_gate_plan("full", fake_godot, fake_root)}
state_root = Path("state")
plan = validate.build_gate_plan("full", fake_godot, fake_root, state_root)
keys = {gate.key for gate in plan}
self.assertIn("sys4-corpus-validate", keys)
self.assertIn("godot-selftest", keys)
self.assertIn("age-cli-sweep", keys)
self.assertEqual("diff-check", validate.build_gate_plan("full", fake_godot, fake_root)[-1].key)
self.assertEqual("diff-check", plan[-1].key)
selftest = next(gate for gate in plan if gate.key == "godot-selftest")
self.assertIn(str(state_root / "godot.log"), selftest.command)
self.assertIn(("APPDATA", str(state_root / "appdata")), selftest.environment)
if __name__ == "__main__":

View File

@@ -56,6 +56,7 @@ class Gate:
label: str
command: tuple[str, ...]
timeout_seconds: int = 180
environment: tuple[tuple[str, str], ...] = ()
@dataclass(frozen=True)
@@ -124,6 +125,7 @@ def build_gate_plan(
level: str,
godot: Path | None = None,
game_root: Path | None = None,
runtime_state_root: Path | None = None,
) -> list[Gate]:
phases = selected_phases(level)
gates = [
@@ -185,6 +187,13 @@ def build_gate_plan(
if "runtime" in phases:
if godot is None or game_root is None:
raise ValueError("runtime validation requires resolved Godot and game-root paths")
runtime_root = runtime_state_root or paths.BUILD / "validation" / "runtime-user"
runtime_environment = (
("APPDATA", str(runtime_root / "appdata")),
("LOCALAPPDATA", str(runtime_root / "local-appdata")),
("XDG_DATA_HOME", str(runtime_root / "xdg-data")),
("XDG_CONFIG_HOME", str(runtime_root / "xdg-config")),
)
gates.extend((
Gate(
"godot-build",
@@ -196,10 +205,12 @@ def build_gate_plan(
"godot-selftest",
"Godot threaded self-test",
(
str(godot), "--headless", "--path", str(REPO / "godot"), "--",
str(godot), "--headless", "--log-file", str(runtime_root / "godot.log"),
"--path", str(REPO / "godot"), "--",
"--selftest", "--game-root", str(game_root), "--text-backend", "portable",
),
180,
300,
runtime_environment,
),
))
@@ -274,6 +285,7 @@ def run_gate(gate: Gate, log_dir: Path, verbose: bool) -> GateResult:
"DOTNET_CLI_TELEMETRY_OPTOUT": "1",
"DOTNET_NOLOGO": "1",
})
environment.update(dict(gate.environment))
popen_arguments = {
"args": gate.command,
"cwd": REPO,
@@ -401,7 +413,9 @@ def main(argv: list[str] | None = None) -> int:
log_dir.mkdir(parents=True, exist_ok=True)
initial_godot = snapshot_godot_processes()
results: list[GateResult] = []
for gate in build_gate_plan(arguments.level, godot, game_root):
runtime_state_root = log_dir / "godot-user"
runtime_state_root.mkdir(parents=True, exist_ok=True)
for gate in build_gate_plan(arguments.level, godot, game_root, runtime_state_root):
result = run_gate(gate, log_dir, arguments.verbose)
results.append(result)
if result.status == "FAIL" and arguments.fail_fast: