File size: 4,757 Bytes
ed3aeeb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | #!/usr/bin/env python3
"""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())
|