#!/usr/bin/env python3 import hashlib import json import pathlib import sys import bpy TEXT_NAME = "WBV2-P1-001-RuntimeReceipt" RECEIPT_ID = "workflow:stabilize_runtime_variants_and_source_receipts" def sha256_file(path): return hashlib.sha256(path.read_bytes()).hexdigest() def runtime_receipt(): root = pathlib.Path(__file__).resolve().parents[3] source_hash = sha256_file(root / "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp") manifest = json.loads((root / "web/app/public/engine-manifest.json").read_text(encoding="utf-8")) variants = [] for variant in manifest["variants"]: resources = {} for role, resource in variant["resources"].items(): resource_path = root / "web/app/public" / resource["url"].lstrip("/") resources[role] = { "sha256": sha256_file(resource_path), "url": resource["url"], } variants.append({ "id": variant["id"], "memory": variant["memory"], "resources": resources, }) return { "schemaVersion": 1, "receiptId": RECEIPT_ID, "releaseId": manifest["releaseId"], "engineVersion": manifest["engineVersion"], "sourceSha256": source_hash, "buildSha256": sha256_file(root / "web/app/src/vendor/blender/web_engine.wasm"), "variants": variants, "fallback": { "policy": "AUTO", "deterministicOrder": ["pthread", "single"], "reasonCode": "PTHREAD_INITIALIZATION_FAILED", }, "session": { "generation": 1, "oneEnginePerSession": True, }, "extensions": {"futureField": {"kind": "opaque", "value": "preserve-me"}}, } def main(output): bpy.ops.wm.read_factory_settings(use_empty=True) text = bpy.data.texts.new(TEXT_NAME) text.write(json.dumps(runtime_receipt(), sort_keys=True, separators=(",", ":"))) text.use_fake_user = True bpy.ops.wm.save_as_mainfile(filepath=str(output.resolve()), check_existing=False, compress=True) if __name__ == "__main__": arguments = sys.argv[sys.argv.index("--") + 1 :] if len(arguments) != 1: raise SystemExit("usage: blender -b --factory-startup --python WBV2-P1-001.py -- OUTPUT") main(pathlib.Path(arguments[0]))