227 lines
9.9 KiB
Python
227 lines
9.9 KiB
Python
"""Initial implementation delivery workflow with independent transport readbacks."""
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import re
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
from typing import Any
|
|
|
|
|
|
_SHA40 = re.compile(r"^[0-9a-f]{40}$")
|
|
_REQUIRED = ("push", "read_remote_head", "create_or_reuse_pr", "read_pr")
|
|
|
|
|
|
def _load_delivery_adoption() -> ModuleType:
|
|
path = Path(__file__).resolve().parent / "delivery_adoption.py"
|
|
spec = importlib.util.spec_from_file_location("gitea_delivery_adoption", path)
|
|
if spec is None or spec.loader is None:
|
|
raise AdoptionError("delivery adoption unavailable")
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
_delivery_adoption = _load_delivery_adoption()
|
|
AdoptionError = _delivery_adoption.AdoptionError
|
|
|
|
|
|
def _reject(message: str) -> None:
|
|
raise AdoptionError(message)
|
|
|
|
|
|
def _validate_adapter(adapter: Any) -> None:
|
|
for name in _REQUIRED:
|
|
try:
|
|
value = getattr(adapter, name)
|
|
except Exception:
|
|
_reject("trusted delivery adapter required")
|
|
else:
|
|
if not callable(value):
|
|
_reject("trusted delivery adapter required")
|
|
|
|
|
|
def _validate_head(head: Any) -> str:
|
|
if type(head) is not str or not _SHA40.fullmatch(head):
|
|
_reject("invalid delivery head")
|
|
return head
|
|
|
|
|
|
def _validate_pr_number(value: Any) -> int:
|
|
if type(value) is not int or isinstance(value, bool) or value < 1:
|
|
_reject("invalid pull request number")
|
|
return value
|
|
|
|
|
|
def _validate_readback(value: Any, number: int, head: str) -> dict[str, Any]:
|
|
if type(value) is not dict:
|
|
_reject("invalid pull request readback")
|
|
if value.get("number") != number or type(value.get("number")) is not int:
|
|
_reject("pull request number mismatch")
|
|
if value.get("head") != head or type(value.get("head")) is not str:
|
|
_reject("pull request head mismatch")
|
|
if value.get("state") != "open":
|
|
_reject("pull request state mismatch")
|
|
# Bound the returned artifact even when the trusted adapter exposes richer data.
|
|
return {"number": number, "head": head, "state": "open"}
|
|
|
|
|
|
def deliver_implementation(adapter: Any, *, head: str, declaration: Any) -> dict[str, Any]:
|
|
"""Push an implementation and create/reuse a PR only after fresh gates."""
|
|
_validate_head(head)
|
|
_validate_adapter(adapter)
|
|
|
|
# The canonical helper performs the explicit declaration/adoption check
|
|
# immediately before the first external write.
|
|
_delivery_adoption.deliver(lambda: adapter.push(head), head=head, declaration=declaration)
|
|
|
|
remote_head = adapter.read_remote_head()
|
|
if remote_head != head or type(remote_head) is not str or not _SHA40.fullmatch(remote_head):
|
|
_reject("remote head mismatch")
|
|
|
|
# Re-check fresh evidence immediately before the PR write.
|
|
pr_number = _delivery_adoption.deliver(
|
|
lambda: adapter.create_or_reuse_pr(head), head=head, declaration=declaration
|
|
)
|
|
pr_number = _validate_pr_number(pr_number)
|
|
|
|
readback = adapter.read_pr(pr_number)
|
|
bounded_pr = _validate_readback(readback, pr_number, head)
|
|
return {"remote_head": remote_head, "pr": bounded_pr}
|
|
|
|
|
|
def reconcile_implementation(adapter: Any, *, head: str, number: int,
|
|
declaration: Any) -> dict[str, Any]:
|
|
"""Explicit read-only recovery after uncertain write acknowledgement.
|
|
|
|
The trusted main-owned adapter must freshly verify identity/scope, exact
|
|
intended body, protected Issue snapshot, and complete files/commits via
|
|
verify_reconciliation(head, number). Only literal True accepts that proof.
|
|
These callbacks MUST be read-only. This is not a transport sandbox, an
|
|
automatic exception handler, or permission to retry a write.
|
|
"""
|
|
_validate_head(head)
|
|
_validate_pr_number(number)
|
|
for name in ("read_remote_head", "read_pr", "verify_reconciliation"):
|
|
callback = None
|
|
try:
|
|
callback = getattr(adapter, name)
|
|
except Exception:
|
|
_reject("trusted reconciliation adapter required")
|
|
if not callable(callback):
|
|
_reject("trusted reconciliation adapter required")
|
|
|
|
remote_head = _delivery_adoption.deliver(
|
|
adapter.read_remote_head, head=head, declaration=declaration
|
|
)
|
|
if type(remote_head) is not str or remote_head != head:
|
|
_reject("remote head mismatch")
|
|
readback = _delivery_adoption.deliver(
|
|
lambda: adapter.read_pr(number), head=head, declaration=declaration
|
|
)
|
|
bounded_pr = _validate_readback(readback, number, head)
|
|
verified = _delivery_adoption.deliver(
|
|
lambda: adapter.verify_reconciliation(head, number),
|
|
head=head, declaration=declaration
|
|
)
|
|
if verified is not True:
|
|
_reject("reconciliation intent or protected state mismatch")
|
|
return {"remote_head": remote_head, "pr": bounded_pr}
|
|
|
|
|
|
def deliver_approved_implementation(adapter: Any, *, head: str, declaration: Any,
|
|
push_intent: Any, push_approval: Any,
|
|
pr_intent: Any, pr_approval: Any,
|
|
coordinator: Any, on_attempt: Any) -> dict[str, Any]:
|
|
"""Compose two approved operations; callbacks own scope/lease/provider proof.
|
|
|
|
Legacy helpers remain stage-only. This path is not a transport sandbox or a
|
|
durable transaction. on_attempt must archive provenance before external write.
|
|
"""
|
|
import sys
|
|
root = str(Path(__file__).resolve().parents[6])
|
|
if root not in sys.path:
|
|
sys.path.insert(0, root)
|
|
from harness.lib.impl_operation import Adapter, Coordinator, validate
|
|
_validate_head(head)
|
|
for name in ("read_remote_head", "read_pr"):
|
|
if not callable(getattr(adapter, name, None)):
|
|
_reject("trusted operation readback adapter required")
|
|
if not isinstance(coordinator, Coordinator) or not callable(on_attempt):
|
|
_reject("caller-owned coordinator and attempt recorder required")
|
|
for name in ("preflight_operation", "verify_operation", "find_intended_pr", "push_operation", "create_or_reuse_pr_operation"):
|
|
if not callable(getattr(adapter, name, None)):
|
|
_reject("trusted operation adapter required")
|
|
try:
|
|
validate(push_intent, push_approval)
|
|
validate(pr_intent, pr_approval)
|
|
except (ValueError, TypeError):
|
|
_reject("invalid approved operation")
|
|
target = (push_intent.target, push_intent.target_id, push_intent.source_sha)
|
|
if push_intent.operation != "push" or pr_intent.operation != "pr.create":
|
|
_reject("only push and pr.create intents are supported")
|
|
if push_intent.source_sha != head or (pr_intent.target, pr_intent.target_id, pr_intent.source_sha) != target:
|
|
_reject("operation target/source mismatch")
|
|
if type(push_intent.payload) is not dict or type(pr_intent.payload) is not dict:
|
|
_reject("explicit operation payloads required")
|
|
if push_intent.payload.get("head") != head or pr_intent.payload.get("head") != head:
|
|
_reject("payload head differs from approved source")
|
|
if type(pr_intent.payload.get("body")) is not str or not pr_intent.payload["body"]:
|
|
_reject("explicit intended PR body required")
|
|
|
|
def stage(intent, approval, mutation, readback, expected):
|
|
entered = False
|
|
def preflight(item):
|
|
_delivery_adoption.require_adoption(declaration,
|
|
expected_source={"kind": "git_tree", "revision": head},
|
|
expected_stage="implementation-delivery")
|
|
return adapter.preflight_operation(item)
|
|
def mutate(item):
|
|
nonlocal entered
|
|
_delivery_adoption.require_adoption(declaration,
|
|
expected_source={"kind": "git_tree", "revision": head},
|
|
expected_stage="implementation-delivery")
|
|
entered = True
|
|
return mutation(item)
|
|
def verify(item, before, observed):
|
|
# A failed local approval gate is not an uncertain remote mutation.
|
|
return entered and expected(observed) and adapter.verify_operation(item, before, observed) is True
|
|
return coordinator.execute(intent, approval,
|
|
Adapter(preflight, True, mutate, readback, verify), on_attempt)
|
|
|
|
push = stage(push_intent, push_approval, lambda item: adapter.push_operation(item),
|
|
lambda item: adapter.read_remote_head(),
|
|
lambda value: type(value) is str and value == head)
|
|
if push.status != "verified":
|
|
return {"status": "partial", "push": push, "pr": None, "artifact": None}
|
|
|
|
intended_number = None
|
|
observed_pr = None
|
|
def create_pr(item):
|
|
nonlocal intended_number
|
|
intended_number = _validate_pr_number(adapter.create_or_reuse_pr_operation(item))
|
|
return {"number": intended_number}
|
|
def pr_readback(item):
|
|
nonlocal intended_number, observed_pr
|
|
if intended_number is None:
|
|
# The callback must return exactly one intended PR, or reject. GET only.
|
|
found = adapter.find_intended_pr(item)
|
|
if type(found) is not dict:
|
|
_reject("unique intended PR unavailable")
|
|
intended_number = _validate_pr_number(found.get("number"))
|
|
observed_pr = adapter.read_pr(intended_number)
|
|
return observed_pr
|
|
def pr_ok(value):
|
|
try:
|
|
_validate_readback(value, _validate_pr_number(intended_number), head)
|
|
except AdoptionError:
|
|
return False
|
|
return True
|
|
pr = stage(pr_intent, pr_approval, create_pr, pr_readback, pr_ok)
|
|
if pr.status != "verified":
|
|
return {"status": "partial", "push": push, "pr": pr, "artifact": None}
|
|
# Use the validated readback, not private adapter state or an unverified read.
|
|
return {"status": "verified", "push": push, "pr": pr,
|
|
"artifact": {"remote_head": head,
|
|
"pr": _validate_readback(observed_pr, _validate_pr_number(intended_number), head)}}
|