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"