from __future__ import annotations
import hashlib
import pathlib
import re
import sys
from typing import Optional
FROZEN_HANDLER_API_VERSION = 1
FROZEN_WIT_PACKAGE = "rill:handler"
FROZEN_WIT_VERSION = "1.0.0"
FROZEN_WIT_WORLD = "invoke-handler"
FROZEN_WIT_SHA256 = "108a68dfd6bcf86e3b63ad630508b2bbf407d00e8634067366e53dbc257cc90c"
def workspace_root() -> pathlib.Path:
return pathlib.Path(__file__).resolve().parents[1]
def wit_path(root: pathlib.Path) -> pathlib.Path:
return root / "crates" / "rill-handler-api" / "wit" / "rill-handler.wit"
def runtime_wit_copy_path(root: pathlib.Path) -> pathlib.Path:
return root / "crates" / "rill-runtime" / "wit" / "rill-handler.wit"
def handler_api_lib_path(root: pathlib.Path) -> pathlib.Path:
return root / "crates" / "rill-handler-api" / "src" / "lib.rs"
def runtime_protocol_lib_path(root: pathlib.Path) -> pathlib.Path:
return root / "crates" / "rill-runtime-protocol" / "src" / "lib.rs"
def normalise_wit(text: str) -> str:
lines = [line.rstrip() for line in text.splitlines()]
collapsed: list[str] = []
prev_blank = False
for line in lines:
is_blank = line == ""
if is_blank and prev_blank:
continue
collapsed.append(line)
prev_blank = is_blank
while collapsed and collapsed[0] == "":
collapsed.pop(0)
while collapsed and collapsed[-1] == "":
collapsed.pop()
return "\n".join(collapsed) + "\n"
def sha256_hex(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def parse_wit_package_version(text: str) -> Optional[tuple[str, str]]:
match = re.search(r"^package\s+([\w:-]+)@([\d.]+)\s*;", text, re.MULTILINE)
if match:
return match.group(1), match.group(2)
return None
def parse_wit_world(text: str) -> Optional[str]:
match = re.search(r"^world\s+([\w-]+)\s*\{", text, re.MULTILINE)
if match:
return match.group(1)
return None
def parse_rust_const(text: str, name: str) -> Optional[str]:
pattern = rf'pub\s+const\s+{re.escape(name)}\s*:\s*&?[\w]+\s*=\s*("([^"]*)"|(\d+))\s*;'
match = re.search(pattern, text)
if match:
if match.group(2) is not None:
return match.group(2)
return match.group(3)
return None
def fail(message: str) -> int:
print(f"error: {message}", file=sys.stderr)
return 1
def main() -> int:
root = workspace_root()
wit_file = wit_path(root)
if not wit_file.is_file():
return fail(f"WIT file not found: {wit_file}")
wit_text = wit_file.read_text(encoding="utf-8")
handler_api_lib = handler_api_lib_path(root)
if not handler_api_lib.is_file():
return fail(f"rill-handler-api lib.rs not found: {handler_api_lib}")
handler_api_text = handler_api_lib.read_text(encoding="utf-8")
runtime_protocol_lib = runtime_protocol_lib_path(root)
if not runtime_protocol_lib.is_file():
return fail(f"rill-runtime-protocol lib.rs not found: {runtime_protocol_lib}")
runtime_protocol_text = runtime_protocol_lib.read_text(encoding="utf-8")
pkg = parse_wit_package_version(wit_text)
if pkg is None:
return fail("could not parse 'package <name>@<version>;' from WIT")
wit_package, wit_version = pkg
if wit_package != FROZEN_WIT_PACKAGE:
return fail(
f"WIT package '{wit_package}' != frozen '{FROZEN_WIT_PACKAGE}'"
)
if wit_version != FROZEN_WIT_VERSION:
return fail(
f"WIT version '{wit_version}' != frozen '{FROZEN_WIT_VERSION}'"
)
wit_world = parse_wit_world(wit_text)
if wit_world is None:
return fail("could not parse 'world <name> {' from WIT")
if wit_world != FROZEN_WIT_WORLD:
return fail(
f"WIT world '{wit_world}' != frozen '{FROZEN_WIT_WORLD}'"
)
normalised = normalise_wit(wit_text)
actual_hash = sha256_hex(normalised.encode("utf-8"))
if actual_hash != FROZEN_WIT_SHA256:
return fail(
f"normalised WIT SHA-256 mismatch: expected {FROZEN_WIT_SHA256}, "
f"got {actual_hash}. The WIT text was edited; either revert the "
f"edit or, for an intentional additive change within v1, update "
f"FROZEN_WIT_SHA256 in this script after review."
)
runtime_wit_copy = runtime_wit_copy_path(root)
if not runtime_wit_copy.is_file():
return fail(
f"rill-runtime WIT copy not found: {runtime_wit_copy}. "
f"Run: cp crates/rill-handler-api/wit/rill-handler.wit "
f"crates/rill-runtime/wit/"
)
copy_text = runtime_wit_copy.read_text(encoding="utf-8")
if copy_text != wit_text:
return fail(
f"rill-runtime WIT copy differs from canonical source. "
f"Run: cp crates/rill-handler-api/wit/rill-handler.wit "
f"crates/rill-runtime/wit/"
)
rust_api_version = parse_rust_const(handler_api_text, "HANDLER_API_VERSION")
rust_wit_package = parse_rust_const(handler_api_text, "WIT_PACKAGE")
rust_wit_version = parse_rust_const(handler_api_text, "WIT_VERSION")
rust_wit_world = parse_rust_const(handler_api_text, "WIT_WORLD")
if rust_api_version != str(FROZEN_HANDLER_API_VERSION):
return fail(
f"rill-handler-api HANDLER_API_VERSION={rust_api_version} != {FROZEN_HANDLER_API_VERSION}"
)
if rust_wit_package != FROZEN_WIT_PACKAGE:
return fail(
f"rill-handler-api WIT_PACKAGE={rust_wit_package!r} != {FROZEN_WIT_PACKAGE!r}"
)
if rust_wit_version != FROZEN_WIT_VERSION:
return fail(
f"rill-handler-api WIT_VERSION={rust_wit_version!r} != {FROZEN_WIT_VERSION!r}"
)
if rust_wit_world != FROZEN_WIT_WORLD:
return fail(
f"rill-handler-api WIT_WORLD={rust_wit_world!r} != {FROZEN_WIT_WORLD!r}"
)
proto_api_version = parse_rust_const(runtime_protocol_text, "HANDLER_API_VERSION")
if proto_api_version != str(FROZEN_HANDLER_API_VERSION):
return fail(
f"rill-runtime-protocol HANDLER_API_VERSION={proto_api_version} != {FROZEN_HANDLER_API_VERSION}"
)
print(f"WIT package: {wit_package}@{wit_version}")
print(f"WIT world: {wit_world}")
print(f"HANDLER_API_VERSION: {FROZEN_HANDLER_API_VERSION}")
print(f"normalised WIT SHA-256: {actual_hash}")
print("WIT ABI v1 freeze: PASS")
return 0
if __name__ == "__main__":
sys.exit(main())