from __future__ import annotations import json from pathlib import Path import pytest from buildledger.adapters import GitHubActionsAdapter from buildledger.anchors import AnchorStore from buildledger.ledger import SQLiteLedger from buildledger.models import Artifact, BuildContract, BuildEnvironment, BuildLog, BuildObject, DependencyGraph, DependencyNode, ReproManifest from buildledger.schemas import SchemaRegistry from buildledger.sync import SyncCursor, apply_delta, prepare_delta def make_contract() -> BuildContract: build_object = BuildObject( source_repo="https://example.com/repo.git", commit="abc123", builder="gha", target="artifact.tar.gz", build_tool="python", flags=["--locked"], ) environment = BuildEnvironment( platform="linux", operating_system="ubuntu-24.04", architecture="x86_64", toolchain={"python": "3.11.8"}, container_image="ghcr.io/example/build:1", env_hash="f" * 64, ) manifest = ReproManifest( source_commit="abc123", deterministic_inputs_hash="a" * 64, toolchain_versions={"python": "3.11.8"}, env_hash="f" * 64, reproducibility_score=1.0, run_count=2, matched_artifact_bytes=100, total_artifact_bytes=100, ) return BuildContract( contract_id="contract-1", build_object=build_object, environment=environment, dependency_graph=DependencyGraph(nodes=[DependencyNode(name="dep", version="1.0.0", digest="d" * 64)]), artifacts=[Artifact(name="artifact", path="dist/artifact.tar.gz", sha256="b" * 64, size_bytes=100)], logs=[BuildLog(stream="stdout", sha256="c" * 64, line_count=1, excerpt=["ok"])], manifest=manifest, ) def test_schema_registry_validates_contract() -> None: registry = SchemaRegistry.with_defaults() contract = make_contract() loaded = registry.validate("BuildContract", "1.0", contract.model_dump(mode="python")) assert loaded.contract_id == "contract-1" assert registry.manifest()["BuildContract"] == ["1.0"] def test_ledger_chain_and_anchor_round_trip(tmp_path: Path) -> None: ledger = SQLiteLedger(tmp_path / "ledger.sqlite") first = ledger.append("build.contract", make_contract().model_dump(mode="python")) second = ledger.append("build.proof", {"status": "verified", "contract": first.entry_hash}) ok, issues = ledger.verify_chain() assert ok assert issues == [] assert second.sequence == 1 anchor = AnchorStore(ledger).record("cloud-anchor", "s3://bucket/buildledger") assert anchor.anchored_hash == second.entry_hash assert ledger.anchors()[0]["anchored_hash"] == second.entry_hash def test_delta_sync_applies_to_fresh_ledger(tmp_path: Path) -> None: source = SQLiteLedger(tmp_path / "source.sqlite") source.append("build.contract", make_contract().model_dump(mode="python")) source.append("build.proof", {"status": "verified"}) packet = prepare_delta(source, SyncCursor(sequence=-1, entry_hash="0" * 64)) target = SQLiteLedger(tmp_path / "target.sqlite") cursor = apply_delta(target, packet) assert cursor.sequence == 1 assert target.verify_chain()[0] assert len(target.all_entries()) == 2 def test_ci_adapter_generates_contract() -> None: context = { "repo": "https://example.com/repo.git", "commit": "abc123", "builder": "gha", "target": "artifact.tar.gz", "build_tool": "python", "platform": "github-actions", "os": "ubuntu-24.04", "arch": "x86_64", "toolchain.python": "3.11.8", "env.PYTHONHASHSEED": "0", "flags": "--locked", "run_count": "2", "reproducibility_score": "1.0", } contract = GitHubActionsAdapter().contract_from_context(context) assert contract.build_object.commit == "abc123" assert contract.environment.variables["env.PYTHONHASHSEED"] == "0" assert contract.manifest.run_count == 2