from __future__ import annotations
import os
import pathlib
import subprocess
import sys
import tempfile
from typing import Tuple
REJECTION_MARKERS: Tuple[str, ...] = (
"unresolved import",
"cannot find",
"private",
)
OFFLINE_RETRY_MARKERS: Tuple[str, ...] = (
"can't find crate",
"no matching package",
"failed to download",
)
def workspace_root() -> pathlib.Path:
return pathlib.Path(__file__).resolve().parents[1]
def runtime_crate_path(root: pathlib.Path) -> pathlib.Path:
return root / "crates" / "rill-runtime"
SMOKE_MAIN_RS = """\
// Smoke crate: prove that legitimate public runtime API compiles when
// rill-runtime is consumed as an external dependency with --features wasm.
use rill_runtime::handler::wasm::WasmInvokeHandler;
fn main() {
let _ = std::any::TypeId::of::<WasmInvokeHandler>();
}
"""
PROBE_MAIN_RS = """\
// Probe crate: attempt to import the test-only ticker probe. This MUST
// fail to compile; if it compiles, the probe has leaked into the public
// API (including via `#[doc(hidden)] pub`, which rustdoc grep cannot
// detect).
use rill_runtime::handler::wasm::active_epoch_ticker_count;
fn main() {
let _ = active_epoch_ticker_count();
}
"""
def _cargo_toml(crate_name: str, runtime_path: pathlib.Path) -> str:
return (
"[package]\n"
f'name = "{crate_name}"\n'
'version = "0.0.0"\n'
'edition = "2024"\n'
'rust-version = "1.94"\n'
'publish = false\n'
"\n"
"[dependencies]\n"
f'rill-runtime = {{ path = "{runtime_path}", features = ["wasm"] }}\n'
)
def write_crate(
parent: pathlib.Path, crate_name: str, main_rs: str, runtime_path: pathlib.Path
) -> pathlib.Path:
crate_dir = parent / crate_name
crate_dir.mkdir(parents=True, exist_ok=True)
src_dir = crate_dir / "src"
src_dir.mkdir(exist_ok=True)
(crate_dir / "Cargo.toml").write_text(
_cargo_toml(crate_name, runtime_path), encoding="utf-8"
)
(src_dir / "main.rs").write_text(main_rs, encoding="utf-8")
return crate_dir
def run_cargo_check(crate_dir: pathlib.Path) -> Tuple[int, str, str]:
env = os.environ.copy()
manifest = str(crate_dir / "Cargo.toml")
offline = subprocess.run(
["cargo", "check", "--offline", "--manifest-path", manifest],
capture_output=True,
text=True,
env=env,
check=False,
)
if offline.returncode != 0 and any(
marker in offline.stderr for marker in OFFLINE_RETRY_MARKERS
):
online = subprocess.run(
["cargo", "check", "--manifest-path", manifest],
capture_output=True,
text=True,
env=env,
check=False,
)
return online.returncode, online.stdout, online.stderr
return offline.returncode, offline.stdout, offline.stderr
def classify_probe_result(returncode: int, stderr: str) -> str:
if returncode == 0:
return "leaked"
return "rejected"
def is_rejection(returncode: int, stderr: str) -> bool:
if returncode == 0:
return False
return any(marker in stderr for marker in REJECTION_MARKERS)
def verify_smoke_succeeds(returncode: int, stderr: str) -> bool:
return returncode == 0
def main() -> int:
root = workspace_root()
runtime_path = runtime_crate_path(root)
if not (runtime_path / "Cargo.toml").is_file():
print(
f"error: rill-runtime crate not found at {runtime_path}", file=sys.stderr
)
return 3
try:
with tempfile.TemporaryDirectory(prefix="rill-runtime-public-api-") as temp_name:
temp = pathlib.Path(temp_name)
smoke_dir = write_crate(
temp, "smoke_normal_api", SMOKE_MAIN_RS, runtime_path
)
smoke_rc, smoke_out, smoke_err = run_cargo_check(smoke_dir)
if not verify_smoke_succeeds(smoke_rc, smoke_err):
print(
"error: normal public API smoke crate failed to compile; "
"dependency configuration is broken",
file=sys.stderr,
)
print("--- smoke crate stderr ---", file=sys.stderr)
print(smoke_err, file=sys.stderr)
return 2
print("normal public API smoke crate: PASS")
probe_dir = write_crate(
temp, "probe_ticker_import", PROBE_MAIN_RS, runtime_path
)
probe_rc, probe_out, probe_err = run_cargo_check(probe_dir)
if probe_rc == 0:
print(
"error: ticker probe compiled successfully — public API leak",
file=sys.stderr,
)
print("--- probe crate stdout ---", file=sys.stderr)
print(probe_out, file=sys.stderr)
return 1
if not is_rejection(probe_rc, probe_err):
print(
"error: ticker probe was rejected, but stderr did not contain "
f"any of {REJECTION_MARKERS}",
file=sys.stderr,
)
print("--- probe crate stderr ---", file=sys.stderr)
print(probe_err, file=sys.stderr)
return 1
print("ticker probe external import: rejected as expected")
except FileNotFoundError as exc:
print(f"error: required tool not found: {exc}", file=sys.stderr)
return 3
except OSError as exc:
print(f"error: filesystem error: {exc}", file=sys.stderr)
return 3
return 0
if __name__ == "__main__":
sys.exit(main())