Ship portable CPU dispatch and verified shared runtime dependencies
This commit is contained in:
1 parent
278b09f82c
commit
0bba1a0aa7
19 files changed
+675
-17
No files matched your search
@@ -7,12 +7,14 @@ import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
CRT_LIBRARIES = {"concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll", "msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll", "vcruntime140_1.dll", "vcruntime140_threads.dll"}
|
||||
|
||||
|
||||
def pe_machine(path):
|
||||
@@ -32,11 +34,49 @@ def pe_machine(path):
|
||||
|
||||
def package(root, executable, output):
|
||||
resources = root / "apps/desktop/src-tauri/resources"
|
||||
inventory = resources / "runtime-libraries.json"
|
||||
if not inventory.is_file() or inventory.stat().st_size > 65536:
|
||||
raise ValueError("Required runtime dependency inventory is missing or oversized")
|
||||
inventory_bytes = inventory.read_bytes()
|
||||
manifest = json.loads(inventory_bytes)
|
||||
if not isinstance(manifest, dict):
|
||||
raise ValueError("Invalid runtime dependency inventory")
|
||||
libraries = manifest.get("libraries")
|
||||
if not isinstance(libraries, list) or not 1 <= len(libraries) <= 64:
|
||||
raise ValueError("Invalid runtime dependency inventory")
|
||||
expected = {"searchi-gemma-worker.exe": {"sha256": manifest.get("worker_sha256")}}
|
||||
for record in libraries:
|
||||
if not isinstance(record, dict):
|
||||
raise ValueError("Invalid runtime library metadata")
|
||||
name = record.get("path", "")
|
||||
if not isinstance(name, str) or not re.fullmatch(r"[A-Za-z0-9_-]+\.dll", name) or name.lower() in {key.lower() for key in expected}:
|
||||
raise ValueError("Invalid or duplicate runtime library path")
|
||||
if type(record.get("bytes")) is not int or record["bytes"] <= 0:
|
||||
raise ValueError("Invalid runtime library size")
|
||||
expected[name] = record
|
||||
if not ({"ggml-cpu-x64.dll", "ggml-vulkan.dll", "vulkan-1.dll", "ggml.dll", "ggml-base.dll", "llama.dll", "mtmd.dll"} | CRT_LIBRARIES).issubset(expected):
|
||||
raise ValueError("Runtime inventory lacks required CPU/GPU libraries")
|
||||
expected[inventory.name] = {"bytes": len(inventory_bytes), "sha256": hashlib.sha256(inventory_bytes).hexdigest()}
|
||||
for name, record in expected.items():
|
||||
path = resources / name
|
||||
if path.is_symlink() or not path.resolve().is_relative_to(resources.resolve()) or not path.is_file():
|
||||
raise ValueError(f"Required runtime library is missing or outside staging: {name}")
|
||||
checksum = record.get("sha256", "")
|
||||
if not isinstance(checksum, str) or not re.fullmatch(r"[0-9a-f]{64}", checksum):
|
||||
raise ValueError("Invalid runtime checksum")
|
||||
with path.open("rb") as stream:
|
||||
if hashlib.file_digest(stream, "sha256").hexdigest() != checksum:
|
||||
raise ValueError(f"Runtime checksum mismatch: {name}")
|
||||
if "bytes" in record and path.stat().st_size != record["bytes"]:
|
||||
raise ValueError(f"Runtime size mismatch: {name}")
|
||||
selected = [(executable, "searchi.exe"), (root / "LICENSE", "LICENSE.txt")]
|
||||
for name in ["searchi-gemma-worker.exe", "LLAMA-LICENSE.txt", "vulkan-1.dll", "VULKAN-LICENSE.txt", "media-runtime/manifest.json",
|
||||
for name in ["searchi-gemma-worker.exe", "LLAMA-LICENSE.txt", "MSVC-REDISTRIBUTION.txt", "vulkan-1.dll", "VULKAN-LICENSE.txt", "media-runtime/manifest.json",
|
||||
"media-runtime/bin/ffmpeg.exe", "media-runtime/bin/ffprobe.exe",
|
||||
"licenses/Inter-OFL.txt", "licenses/Fluent-MIT.txt", "media-runtime/licenses/LICENSE.txt"]:
|
||||
selected.append((resources / name, name))
|
||||
selected.append((inventory, inventory.name))
|
||||
selected_names = {name for _, name in selected}
|
||||
selected.extend((resources / name, name) for name in expected if name not in selected_names)
|
||||
for directory in ["licenses", "media-runtime/licenses"]:
|
||||
notices = list((resources / directory).rglob("*.txt"))
|
||||
if not notices:
|
||||
@@ -73,11 +113,13 @@ def package(root, executable, output):
|
||||
checksum.update(block)
|
||||
count += len(block)
|
||||
records.append({"path": name, "bytes": count, "sha256": checksum.hexdigest()})
|
||||
if name in expected and (checksum.hexdigest() != expected[name]["sha256"] or ("bytes" in expected[name] and count != expected[name]["bytes"])):
|
||||
raise ValueError(f"Runtime changed while packaging: {name}")
|
||||
archive.writestr("PACKAGE.json", json.dumps({"platform": "windows", "architecture": architecture,
|
||||
"status": "unsigned development package; clean-machine verification pending", "files": records}, indent=2))
|
||||
archive.writestr("START-HERE.txt", "searchi — Find what matters. Locally.\n\n"
|
||||
"Extract the entire folder, then open searchi.exe. Keep its helpers alongside it.\n"
|
||||
"Windows needs WebView2 and the applicable Microsoft C++ runtime.\n"
|
||||
"Windows needs WebView2. The Microsoft C++ runtime ships with these helpers; Visual Studio is not required.\n"
|
||||
"First launch downloads approximately 865 MB of verified Google model files.\n"
|
||||
"Choose Add folder to index your own files; ordinary search stays local.\n"
|
||||
"This is an unsigned development build, not a certified production release.\n")
|
||||
|
||||
Reference in new issue
Block a user