71 lines
2.7 KiB
Python
71 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from .models import BuildContract, BuildEnvironment, BuildObject, ReproManifest, sha256_text, canonical_json
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RegistryCoordinate:
|
|
registry: str
|
|
name: str
|
|
version: str
|
|
|
|
def canonical(self) -> str:
|
|
return f"{self.registry}/{self.name}@{self.version}"
|
|
|
|
|
|
class _CIAdapter:
|
|
provider: str
|
|
|
|
def manifest_from_context(self, context: dict[str, str]) -> ReproManifest:
|
|
source_commit = context["commit"]
|
|
env_hash = sha256_text(canonical_json(context.get("environment", {})))
|
|
inputs_hash = sha256_text(canonical_json({k: context[k] for k in sorted(context) if k != "entropy_seed"}))
|
|
score = float(context.get("reproducibility_score", "1.0"))
|
|
return ReproManifest(
|
|
source_commit=source_commit,
|
|
deterministic_inputs_hash=inputs_hash,
|
|
toolchain_versions={"ci": self.provider},
|
|
env_hash=env_hash,
|
|
entropy_seed=context.get("entropy_seed"),
|
|
reproducibility_score=score,
|
|
run_count=int(context.get("run_count", "1")),
|
|
matched_artifact_bytes=int(context.get("matched_artifact_bytes", "0")),
|
|
total_artifact_bytes=int(context.get("total_artifact_bytes", "0")),
|
|
)
|
|
|
|
def contract_from_context(self, context: dict[str, str]) -> BuildContract:
|
|
manifest = self.manifest_from_context(context)
|
|
build_object = BuildObject(
|
|
source_repo=context["repo"],
|
|
commit=context["commit"],
|
|
builder=context.get("builder", self.provider),
|
|
target=context.get("target", context["repo"]),
|
|
build_tool=context.get("build_tool", self.provider),
|
|
flags=[flag for flag in context.get("flags", "").split() if flag],
|
|
)
|
|
environment = BuildEnvironment(
|
|
platform=context.get("platform", self.provider),
|
|
operating_system=context.get("os", "unknown"),
|
|
architecture=context.get("arch", "unknown"),
|
|
toolchain={k: v for k, v in context.items() if k.startswith("toolchain.")},
|
|
container_image=context.get("container_image"),
|
|
env_hash=manifest.env_hash,
|
|
variables={k: v for k, v in context.items() if k.startswith("env.")},
|
|
)
|
|
contract_id = sha256_text(canonical_json({"provider": self.provider, "repo": context["repo"], "commit": context["commit"]}))
|
|
return BuildContract(contract_id=contract_id, build_object=build_object, environment=environment, manifest=manifest)
|
|
|
|
|
|
class GitHubActionsAdapter(_CIAdapter):
|
|
provider = "github-actions"
|
|
|
|
|
|
class GitLabCIAdapter(_CIAdapter):
|
|
provider = "gitlab-ci"
|
|
|
|
|
|
class CircleCIAdapter(_CIAdapter):
|
|
provider = "circleci"
|