Files
plezy/scripts/check_codegen.py
T
edde746 09656fa4d3 fix(supply-chain): verify CI and production inputs
Pin external actions, images, toolchains, native archives, and tvOS engine artifacts; enforce fail-closed CI checks and keep website privacy disclosures aligned with shipped behavior.
2026-07-24 03:56:40 +02:00

152 lines
5.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""Check generated outputs in an isolated worktree without mutating the caller."""
from __future__ import annotations
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
EXPLICIT_GENERATED = {
"lib/data/ducet_order.dart",
"lib/data/hid_key_labels.dart",
"lib/data/iso_639_data.dart",
"server/relay_protocol_gen.go",
}
DEPENDENCY_STATE = ("package_config.json", "package_graph.json")
def _run(root: Path, *args: str) -> subprocess.CompletedProcess[bytes]:
return subprocess.run(args, cwd=root, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def _is_generated(relative: str) -> bool:
return relative in EXPLICIT_GENERATED or (
relative.startswith("lib/") and (relative.endswith(".g.dart") or relative.endswith(".freezed.dart"))
)
def _generated_state(root: Path) -> dict[str, bytes]:
state = {}
lib = root / "lib"
if lib.exists():
for path in lib.rglob("*.dart"):
relative = path.relative_to(root).as_posix()
if _is_generated(relative) and path.is_file():
state[relative] = path.read_bytes()
for relative in EXPLICIT_GENERATED:
path = root / relative
if path.is_file():
state[relative] = path.read_bytes()
return state
def _nul_paths(result: bytes) -> list[str]:
return [os.fsdecode(value) for value in result.split(b"\0") if value]
def _copy_overlay_path(source_root: Path, target_root: Path, relative: str) -> None:
source = source_root / relative
target = target_root / relative
if not source.exists() and not source.is_symlink():
if target.is_dir() and not target.is_symlink():
shutil.rmtree(target)
else:
target.unlink(missing_ok=True)
return
target.parent.mkdir(parents=True, exist_ok=True)
if target.is_dir() and not target.is_symlink():
shutil.rmtree(target)
else:
target.unlink(missing_ok=True)
if source.is_symlink():
target.symlink_to(os.readlink(source))
else:
shutil.copy2(source, target)
def _copy_dependency_state(source_root: Path, target_root: Path) -> None:
source = source_root / ".dart_tool"
target = target_root / ".dart_tool"
for relative in DEPENDENCY_STATE:
source_path = source / relative
if source_path.is_file():
target.mkdir(parents=True, exist_ok=True)
shutil.copy2(source_path, target / relative)
def _create_isolated_checkout(root: Path, destination: Path) -> None:
_run(root, "git", "worktree", "add", "--detach", "--quiet", str(destination), "HEAD")
changed = _nul_paths(
_run(root, "git", "diff", "--no-renames", "--name-only", "-z", "HEAD", "--").stdout
)
untracked = _nul_paths(_run(root, "git", "ls-files", "--others", "--exclude-standard", "-z").stdout)
for relative in sorted(set(changed + untracked)):
_copy_overlay_path(root, destination, relative)
_copy_dependency_state(root, destination)
def _dirty_generated_paths(root: Path) -> set[str]:
changed = _nul_paths(_run(root, "git", "diff", "--name-only", "-z", "--").stdout)
untracked = _nul_paths(
_run(
root,
"git",
"ls-files",
"--others",
"--exclude-standard",
"-z",
"--",
"lib",
*EXPLICIT_GENERATED,
).stdout
)
return {relative for relative in changed + untracked if _is_generated(relative)}
def main(argv: list[str] | None = None) -> int:
arguments = list(sys.argv[1:] if argv is None else argv)
root = Path.cwd().resolve()
caller_state = _generated_state(root)
temporary = Path(tempfile.mkdtemp(prefix="plezy-codegen-check-"))
checkout = temporary / "checkout"
registered = True
try:
_create_isolated_checkout(root, checkout)
result = subprocess.run(["bash", "scripts/codegen.sh", *arguments], cwd=checkout, check=False)
if result.returncode != 0:
return result.returncode
expected_state = _generated_state(checkout)
stale = {
relative
for relative in caller_state.keys() | expected_state.keys()
if caller_state.get(relative) != expected_state.get(relative)
}
stale.update(_dirty_generated_paths(root))
if stale:
print("Generated files are out of date:", file=sys.stderr)
for relative in sorted(stale):
print(f" {relative}", file=sys.stderr)
print("Run 'scripts/codegen.sh' and commit the result.", file=sys.stderr)
return 1
return 0
finally:
if registered:
subprocess.run(
["git", "worktree", "remove", "--force", str(checkout)],
cwd=root,
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
shutil.rmtree(temporary, ignore_errors=True)
if __name__ == "__main__":
raise SystemExit(main())