| |
| """Record the isolated Netron/Playwright/Chromium environment and release provenance.""" |
|
|
| from __future__ import annotations |
|
|
| import hashlib |
| import importlib.metadata |
| import json |
| import os |
| import platform |
| import subprocess |
| import sys |
| import tempfile |
| import urllib.request |
| from pathlib import Path |
| from typing import Any |
|
|
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| ENV_DIR = ROOT / "environment/visualization/netron" |
|
|
|
|
| def sha256(path: Path) -> str: |
| digest = hashlib.sha256() |
| with path.open("rb") as handle: |
| for chunk in iter(lambda: handle.read(1024 * 1024), b""): |
| digest.update(chunk) |
| return digest.hexdigest() |
|
|
|
|
| def atomic_text(path: Path, text: str) -> None: |
| path.parent.mkdir(parents=True, exist_ok=True) |
| with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=path.parent, delete=False) as handle: |
| handle.write(text) |
| temporary = Path(handle.name) |
| os.replace(temporary, path) |
|
|
|
|
| def atomic_json(path: Path, value: Any) -> None: |
| atomic_text(path, json.dumps(value, indent=2, sort_keys=True, ensure_ascii=False) + "\n") |
|
|
|
|
| def pypi_artifact(package: str, version: str, matcher: str) -> dict[str, Any]: |
| with urllib.request.urlopen(f"https://pypi.org/pypi/{package}/{version}/json", timeout=30) as response: |
| value = json.load(response) |
| matches = [item for item in value["urls"] if matcher in item["filename"]] |
| if len(matches) != 1: |
| raise ValueError(f"expected one PyPI artifact for {package} {matcher}, found {[item['filename'] for item in matches]}") |
| item = matches[0] |
| return { |
| "package": package, |
| "version": version, |
| "filename": item["filename"], |
| "url": item["url"], |
| "bytes": item["size"], |
| "sha256": item["digests"]["sha256"], |
| "upload_time_iso_8601": item["upload_time_iso_8601"], |
| } |
|
|
|
|
| def main() -> int: |
| os.environ.setdefault("PLAYWRIGHT_BROWSERS_PATH", str(ENV_DIR / "browsers")) |
| from playwright.sync_api import sync_playwright |
|
|
| netron_bin = ENV_DIR / ".venv/bin/netron" |
| version_result = subprocess.run([str(netron_bin), "--version"], capture_output=True, text=True, check=False) |
| netron_version = (version_result.stdout + version_result.stderr).strip() |
| packages = sorted( |
| (distribution.metadata["Name"], distribution.version) |
| for distribution in importlib.metadata.distributions() |
| if distribution.metadata["Name"] |
| ) |
| atomic_text(ENV_DIR / "installed-packages.txt", "".join(f"{name}=={version}\n" for name, version in packages)) |
| with sync_playwright() as playwright: |
| browser = playwright.chromium.launch(headless=True) |
| chromium_version = browser.version |
| browser.close() |
| executable = Path(playwright.chromium.executable_path) |
| releases = { |
| "schema_version": "1.0", |
| "source": "PyPI JSON API", |
| "artifacts": [ |
| pypi_artifact("netron", netron_version, "py3-none-any.whl"), |
| pypi_artifact("playwright", importlib.metadata.version("playwright"), "manylinux1_x86_64.whl"), |
| ], |
| "official_release_tags": { |
| "netron": f"https://github.com/lutzroeder/netron/releases/tag/v{netron_version}", |
| "playwright_python": f"https://github.com/microsoft/playwright-python/releases/tag/v{importlib.metadata.version('playwright')}", |
| }, |
| } |
| atomic_json(ENV_DIR / "pypi_release_artifacts.json", releases) |
| tool_versions = { |
| "schema_version": "1.0", |
| "python": platform.python_version(), |
| "python_executable": sys.executable, |
| "netron": netron_version, |
| "playwright": importlib.metadata.version("playwright"), |
| "chromium": chromium_version, |
| "chromium_executable": str(executable), |
| "chromium_executable_bytes": executable.stat().st_size, |
| "chromium_executable_sha256": sha256(executable), |
| "capture_method": "official Netron browser Export as PNG via Control+Shift+E", |
| "converter": "NOT_RUN", |
| "model_conversion_performed": False, |
| } |
| atomic_json(ENV_DIR / "tool_versions.json", tool_versions) |
| environment_files = [ |
| ENV_DIR / "README.md", |
| ENV_DIR / "bootstrap.sh", |
| ENV_DIR / "requirements.in", |
| ENV_DIR / "requirements.lock", |
| ENV_DIR / "installed-packages.txt", |
| ENV_DIR / "pypi_release_artifacts.json", |
| ENV_DIR / "tool_versions.json", |
| ] |
| atomic_text( |
| ENV_DIR / "environment_files.sha256", |
| "".join(f"{sha256(path)} {path.relative_to(ROOT)}\n" for path in environment_files), |
| ) |
| print(json.dumps({"status": "PASS", **tool_versions}, sort_keys=True)) |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|