Files
LangBot/plugins/Runner/certification-tests/verify_localagent_candidate.py

159 lines
6.5 KiB
Python

"""Verify an already-built LocalAgent archive. Never rebuild or sign it.
Real SDK artifact/dependency stores and direct OSS dependency installer; this
is NOT shared-worker nsjail or live Core/model acceptance. All packaged tests
run from the read-only extraction in a fresh process.
"""
from __future__ import annotations
import argparse
import asyncio
import base64
import hashlib
import importlib.metadata
import json
import os
import subprocess
import sys
import zipfile
from pathlib import Path
import yaml
from langbot_plugin.cli.utils.page_components import discover_plugin_components, populate_plugin_pages
from langbot_plugin.entities.io.context import PluginWorkerPolicy
from langbot_plugin.runtime.plugin.artifact import PluginArtifactStore
from langbot_plugin.runtime.plugin.dependency_environment import PluginDependencyEnvironmentStore
from langbot_plugin.runtime.plugin.worker_launcher import PluginWorkerLauncher
from langbot_plugin.utils.discover.engine import ComponentDiscoveryEngine
def sha(data):
return hashlib.sha256(data).hexdigest()
def verify_sdk():
dist = importlib.metadata.distribution("langbot-plugin")
from inspect import signature
from langbot_plugin.api.entities.builtin.runner.context import RunnerContext
assert "paths" in signature(RunnerContext.export_box_files).parameters
checked = {}
for entry in dist.files or []:
if entry.hash is None:
continue
data = dist.locate_file(entry).read_bytes()
digest = hashlib.new(entry.hash.mode, data).digest()
assert base64.urlsafe_b64encode(digest).decode().rstrip("=") == entry.hash.value, str(entry)
checked[str(entry)] = sha(data)
assert checked
return {"version": dist.version, "record_checked_files": checked, "python": sys.version}
async def verify(args):
source = args.source.resolve()
evidence = args.evidence.resolve()
evidence.mkdir(parents=True, exist_ok=False)
package = args.package.resolve()
raw = package.read_bytes()
digest = sha(raw)
def git(*arguments):
return subprocess.check_output(["git", "-C", str(source), *arguments])
commit = git("rev-parse", "HEAD").decode().strip()
assert not git("status", "--porcelain", "--", "."), "Candidate source must be clean"
prefix = git("rev-parse", "--show-prefix").decode().strip()
tracked = git("ls-tree", "-r", "--name-only", "HEAD", "--", ".").decode().splitlines()
entries = {}
with zipfile.ZipFile(package) as archive:
assert len(archive.namelist()) == len(set(archive.namelist()))
# Compare every payload byte with the commit; SDK only rewrites manifest.
assert set(archive.namelist()) == set(tracked)
for name in tracked:
payload = archive.read(name)
if name != "manifest.yaml":
assert payload == git("show", f"HEAD:{prefix}{name}"), name
entries[name] = sha(payload)
os.chdir(source)
discovery = ComponentDiscoveryEngine()
manifest = discovery.load_component_manifest("manifest.yaml", owner="builtin", no_save=True)
populate_plugin_pages(manifest, discover_plugin_components(manifest, discovery))
generated = yaml.safe_dump(manifest.manifest, allow_unicode=True, sort_keys=False).encode()
assert archive.read("manifest.yaml") == generated
assert manifest.manifest["execution"]["sharedRuntime"] == "shared-runtime-v1"
sdk = verify_sdk()
artifact_store = PluginArtifactStore(evidence / "runtime")
artifact = artifact_store.install_package(raw, digest)
assert artifact_store.require_verified(digest) == artifact
store = PluginDependencyEnvironmentStore(evidence / "runtime")
launcher = PluginWorkerLauncher()
launcher.configure(
PluginWorkerPolicy(max_cpus=1, max_memory_mb=512, max_pids=32, max_open_files=128, max_file_size_mb=32),
"oss_dev",
)
ready = await launcher.prepare_dependency_environment(store, artifact)
assert store.get_ready(ready.digest) == ready
assert await launcher.prepare_dependency_environment(store, artifact) == ready
assert not (ready.site_packages_path / "langbot_plugin").exists()
assert (ready.site_packages_path / "pydantic").is_dir()
(evidence / "sdk-record.json").write_text(json.dumps(sdk, indent=2) + "\n")
env = {
**os.environ,
"PYTHONDONTWRITEBYTECODE": "1",
"PYTHONPATH": os.pathsep.join(
(str(launcher.runtime_import_root), str(ready.site_packages_path), str(artifact.code_path))
),
}
commands = [
sys.executable,
"-m",
"pytest",
"tests",
"-q",
"-p",
"no:cacheprovider",
"--junitxml=" + str(evidence / "tests.xml"),
]
result = subprocess.run(
commands,
cwd=artifact.code_path,
env=env,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
timeout=180,
)
(evidence / "packaged-tests.log").write_text(result.stdout)
report = {
"source_commit": commit,
"package": str(package),
"sha256": digest,
"size": len(raw),
"entry_sha256": entries,
"artifact_code": str(artifact.code_path),
"sdk_version": sdk["version"],
"sdk_record_checked_count": len(sdk["record_checked_files"]),
"dependency_environment": str(ready.root_path),
"dependency_environment_digest": ready.digest,
"dependency_admission": True,
"cache_readback": True,
"sdk_shadow_installed": False,
"packaged_tests_exit": result.returncode,
"command": commands,
"limitations": "Direct OSS dependency installer and one in-process real SDK component over loopback RPC; simulated Host/model/storage/credentials/Box only. No supervisor/nsjail, live Core DB, signer, upstream model, or live Workspace admission.",
}
(evidence / "inventory.json").write_text(json.dumps(report, indent=2) + "\n")
print(json.dumps({k: v for k, v in report.items() if k != "entry_sha256"}, indent=2))
assert result.returncode == 0, result.stdout
assert sha(package.read_bytes()) == digest
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--package", type=Path, required=True)
parser.add_argument("--evidence", type=Path, required=True, help="New directory, must not already exist")
parser.add_argument("--source", type=Path, default=Path(__file__).resolve().parents[1] / "LocalAgent")
asyncio.run(verify(parser.parse_args()))