Files
Agentic-AI-Template/Codex/Coding/harness framework cs/scripts/dotnet_harness/metadata.py
T
2026-09-08 10:30:07 +09:00

79 lines
3.4 KiB
Python

"""Validate the SDK-evaluated project-reference graph before restore."""
import json
import subprocess
import time
from collections import deque
from pathlib import Path
from .discovery import DiscoveryError, validate_csproj
from .models import CommandSpec, ResultCheck, ResultCheckKind, ValidationPlan, ValidationStep
from .process import ValidationFailure, execute_plan
def _project_path(root, path):
try:
path = path.resolve()
if not path.is_relative_to(root):
raise ValidationFailure(f"ProjectReference resolves outside the repository: {path}")
validate_csproj(path)
except (DiscoveryError, OSError, ValueError) as exc:
raise ValidationFailure(f"invalid ProjectReference {path}: {exc}") from exc
return path
def validate_project_graph(
root, selection, config, tools, *, deadline, clock=time.monotonic, run=subprocess.run,
):
root = root.resolve()
pending = deque((path, None) for path in
(*selection.projects, *config.dotnet.test_projects))
visited = set()
while pending:
project, framework = pending.popleft()
project = _project_path(root, project)
key = (project, framework)
if key in visited:
continue
visited.add(key)
argv = (
str(tools.dotnet), "msbuild", str(project),
"-getProperty:TargetFramework,TargetFrameworks",
"-getItem:ProjectReference",
f"-p:Configuration={config.dotnet.configuration}",
)
if framework is not None:
argv += (f"-p:TargetFramework={framework}",)
plan = ValidationPlan(selection.kind, (ValidationStep(
CommandSpec(argv, root, f"metadata: {project.name}"),
(ResultCheck(ResultCheckKind.PROJECT_IS_NET8),),
),))
result = execute_plan(plan, root, deadline=deadline, clock=clock, run=run)[0]
try:
data = json.loads(result.stdout)
references = data["Items"]["ProjectReference"]
if not isinstance(references, list):
raise ValueError("expected an array")
for reference in references:
fields = {key.casefold(): value for key, value in reference.items()}
if any(fields.get(name) for name in (
"additionalproperties", "globalpropertiestoremove",
"setconfiguration", "setplatform", "settargetframework",
)):
raise ValueError("custom reference properties are unsupported; use the shared Configuration and project TFM")
raw = fields.get("fullpath") or fields.get("identity")
if not isinstance(raw, str) or not raw.strip():
raise ValueError("reference must have FullPath or Identity")
child = _project_path(root, project.parent / Path(raw))
pending.append((child, None))
# Outer builds omit references conditioned on an individual TFM.
if framework is None:
targets = data["Properties"].get("TargetFrameworks", "")
for target in targets.split(";"):
if target.strip():
pending.append((project, target.strip()))
except (ValueError, KeyError, TypeError, AttributeError) as exc:
raise ValidationFailure(
f"invalid evaluated ProjectReference metadata for {project}: {exc}"
) from exc