Files
settled-reach/tooling/check-client-version
T
jpmschweitzerandClaude Opus 5 086d9ed56e fix(client): bake the version into the build, so an export can invalidate its cache (T-1241)
current_schema_version() line-scanned res://../project.yaml at runtime. That
resolves to the repo root in a dev run and to nothing in an exported build, so a
shipped game got the "?.?.?" fallback every time. Since that tag is the Atlas
disk cache's ONLY invalidation signal, every exported build stamped and compared
the same sentinel: a canvas cached by one build would be served by every later
build, forever. T-1239 is what that failure looks like once it happens.

loading_screen.gd carried a byte-for-byte copy of the same function, so the
version shown to the player was "?.?.?" in exactly the builds where a version
string is worth showing. Both call sites now share client/scripts/build_version.gd,
which reads application/config/version out of ProjectSettings — a value Godot
bakes into the PCK, identical in the editor and in an export by construction
rather than by luck. No file IO, no fallback branch.

project.yaml stays the source of truth (CLAUDE.md); client/project.godot mirrors
it. A mirror nobody checks would be worse than the bug it replaces -- the old
code failed loudly everywhere, a stale mirror fails silently -- so
tooling/check-client-version compares the two and the pre-push hook runs it
unconditionally. Not gated on "were those files in this push": drift persists on
main once introduced, and gating would let an existing drift ride along.

The test this replaces asserted that current_schema_version() did not return its
fallback, and passed -- in the one environment where the code under test worked.
Three tests now pin the property that actually matters: a real version, sourced
from the baked setting, matching project.yaml.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 23:35:33 +02:00

72 lines
2.8 KiB
Python
Executable File

#!/usr/bin/env python3
"""Fail if client/project.godot's baked version has drifted from project.yaml.
project.yaml is the version source of truth (CLAUDE.md). The client cannot read
it at runtime — an exported build has no repo root — so the value is mirrored
into `application/config/version` in client/project.godot, which Godot bakes
into the PCK (T-1241).
A mirror nobody checks is worse than the bug it replaced: the old code failed
LOUDLY in an export ("?.?.?" everywhere, no cache invalidation ever), whereas a
stale mirror fails SILENTLY — the Atlas disk cache would keep serving canvases
under a version that stopped matching the build. That is precisely the T-1239
failure, which cost eight days of a map drawn from a canvas whose generating
code no longer existed. Hence this check, wired into the pre-push hook.
Exit: 0 = in sync, 1 = drifted or unreadable.
"""
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
PROJECT_YAML = ROOT / "project.yaml"
PROJECT_GODOT = ROOT / "client" / "project.godot"
# Anchored to line start so the commentary above `version:` (which mentions
# earlier versions by number) can never be mistaken for the field itself.
YAML_VERSION = re.compile(r"^version:\s*(\S+)\s*$", re.MULTILINE)
GODOT_VERSION = re.compile(r'^config/version\s*=\s*"([^"]*)"\s*$', re.MULTILINE)
def read(path: Path, pattern: re.Pattern, label: str) -> str | None:
if not path.exists():
print(f"check-client-version: {path} not found", file=sys.stderr)
return None
match = pattern.search(path.read_text(encoding="utf-8"))
if not match:
print(f"check-client-version: no {label} in {path}", file=sys.stderr)
return None
return match.group(1)
def main() -> int:
yaml_version = read(PROJECT_YAML, YAML_VERSION, "`version:` line")
godot_version = read(PROJECT_GODOT, GODOT_VERSION, "`config/version=` line")
if yaml_version is None or godot_version is None:
return 1
if yaml_version != godot_version:
print(
"check-client-version: version drift\n"
f" project.yaml {yaml_version}\n"
f" client/project.godot {godot_version}\n"
"\n"
"project.yaml is the source of truth. Set config/version in\n"
"client/project.godot's [application] section to match it.\n"
"\n"
"This matters beyond cosmetics: the Atlas disk cache keys its\n"
"invalidation on this version, so a stale mirror makes a shipped\n"
"build serve canvases generated by code it no longer runs (T-1239).",
file=sys.stderr,
)
return 1
print(f"check-client-version: OK — {yaml_version}")
return 0
if __name__ == "__main__":
sys.exit(main())