generated from coulomb/repo-seed
Backend fabric extension
This commit is contained in:
@@ -16,6 +16,11 @@ from markitect_tool.cache import (
|
||||
load_cache,
|
||||
save_cache,
|
||||
)
|
||||
from markitect_tool.backend import (
|
||||
BackendRegistryError,
|
||||
load_backend_registry,
|
||||
snapshot_identity_for_file,
|
||||
)
|
||||
from markitect_tool.content_class import (
|
||||
ContentClassResolutionError,
|
||||
load_content_class_file,
|
||||
@@ -458,6 +463,124 @@ def process(file: Path, root: Path, output_format: str) -> None:
|
||||
raise click.exceptions.Exit(0 if result.valid else 1)
|
||||
|
||||
|
||||
@main.group()
|
||||
def backend() -> None:
|
||||
"""Inspect optional backend manifests and snapshot identities."""
|
||||
|
||||
|
||||
@backend.command("list")
|
||||
@click.option(
|
||||
"--path",
|
||||
"paths",
|
||||
multiple=True,
|
||||
type=click.Path(path_type=Path),
|
||||
help="Backend manifest file or directory. Defaults to .markitect/backends and .markitect/backend.yaml.",
|
||||
)
|
||||
@click.option("--capability", help="Only show backends that declare this capability.")
|
||||
@click.option(
|
||||
"--format",
|
||||
"output_format",
|
||||
type=click.Choice(["json", "yaml", "text"], case_sensitive=False),
|
||||
default="text",
|
||||
show_default=True,
|
||||
)
|
||||
def backend_list(paths: tuple[Path, ...], capability: str | None, output_format: str) -> None:
|
||||
"""List registered optional backend manifests."""
|
||||
|
||||
try:
|
||||
registry = load_backend_registry(list(paths) or None)
|
||||
except BackendRegistryError as exc:
|
||||
raise click.ClickException(str(exc)) from exc
|
||||
manifests = (
|
||||
registry.find_by_capability(capability.replace("-", "_").lower())
|
||||
if capability
|
||||
else registry.list()
|
||||
)
|
||||
data = {
|
||||
"count": len(manifests),
|
||||
"backends": [manifest.to_dict() for manifest in manifests],
|
||||
}
|
||||
_emit_backend_list(data, output_format)
|
||||
|
||||
|
||||
@backend.command("inspect")
|
||||
@click.argument("backend_id")
|
||||
@click.option(
|
||||
"--path",
|
||||
"paths",
|
||||
multiple=True,
|
||||
type=click.Path(path_type=Path),
|
||||
help="Backend manifest file or directory. Defaults to .markitect/backends and .markitect/backend.yaml.",
|
||||
)
|
||||
@click.option(
|
||||
"--require",
|
||||
"required_capabilities",
|
||||
multiple=True,
|
||||
help="Required capability to check. May be repeated.",
|
||||
)
|
||||
@click.option(
|
||||
"--format",
|
||||
"output_format",
|
||||
type=click.Choice(["json", "yaml", "text"], case_sensitive=False),
|
||||
default="text",
|
||||
show_default=True,
|
||||
)
|
||||
def backend_inspect(
|
||||
backend_id: str,
|
||||
paths: tuple[Path, ...],
|
||||
required_capabilities: tuple[str, ...],
|
||||
output_format: str,
|
||||
) -> None:
|
||||
"""Inspect one backend manifest and optional compatibility check."""
|
||||
|
||||
try:
|
||||
registry = load_backend_registry(list(paths) or None)
|
||||
manifest = registry.get(backend_id)
|
||||
except BackendRegistryError as exc:
|
||||
raise click.ClickException(str(exc)) from exc
|
||||
data = manifest.to_dict()
|
||||
if required_capabilities:
|
||||
data["capability_check"] = manifest.check(list(required_capabilities)).to_dict()
|
||||
_emit_backend_manifest(data, output_format)
|
||||
|
||||
|
||||
@backend.command("snapshot-id")
|
||||
@click.argument("file", type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
||||
@click.option(
|
||||
"--parse-option",
|
||||
"parse_options",
|
||||
multiple=True,
|
||||
metavar="KEY=VALUE",
|
||||
help="Parse option included in the snapshot identity hash.",
|
||||
)
|
||||
@click.option("--contract-hash", help="Optional contract hash included in the snapshot identity.")
|
||||
@click.option(
|
||||
"--format",
|
||||
"output_format",
|
||||
type=click.Choice(["json", "yaml", "text"], case_sensitive=False),
|
||||
default="text",
|
||||
show_default=True,
|
||||
)
|
||||
def backend_snapshot_id(
|
||||
file: Path,
|
||||
parse_options: tuple[str, ...],
|
||||
contract_hash: str | None,
|
||||
output_format: str,
|
||||
) -> None:
|
||||
"""Compute a read-only content-addressed snapshot identity for a file."""
|
||||
|
||||
try:
|
||||
identity = snapshot_identity_for_file(
|
||||
file,
|
||||
parse_options=_parse_key_value_options(parse_options),
|
||||
contract_hash=contract_hash,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise click.ClickException(str(exc)) from exc
|
||||
data = identity.to_dict() | {"snapshot_id": identity.snapshot_id}
|
||||
_emit_snapshot_identity(data, output_format)
|
||||
|
||||
|
||||
@main.group("class")
|
||||
def class_group() -> None:
|
||||
"""Resolve deterministic content classes."""
|
||||
@@ -1070,6 +1193,51 @@ def _emit_processor_run(data: dict, output_format: str) -> None:
|
||||
click.echo(f" [{diagnostic['severity']}] {diagnostic['code']}: {diagnostic['message']}")
|
||||
|
||||
|
||||
def _emit_backend_list(data: dict, output_format: str) -> None:
|
||||
if output_format == "json":
|
||||
click.echo(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
elif output_format == "yaml":
|
||||
click.echo(yaml.safe_dump(data, sort_keys=False))
|
||||
else:
|
||||
click.echo(f"backends: {data['count']}")
|
||||
for backend_data in data["backends"]:
|
||||
capabilities = ", ".join(backend_data.get("capabilities", []))
|
||||
click.echo(f"- {backend_data['id']} [{capabilities}]")
|
||||
|
||||
|
||||
def _emit_backend_manifest(data: dict, output_format: str) -> None:
|
||||
if output_format == "json":
|
||||
click.echo(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
elif output_format == "yaml":
|
||||
click.echo(yaml.safe_dump(data, sort_keys=False))
|
||||
else:
|
||||
click.echo(data["id"])
|
||||
if data.get("name"):
|
||||
click.echo(f"name: {data['name']}")
|
||||
click.echo(f"kind: {data.get('kind', 'cache-backend')}")
|
||||
click.echo("capabilities: " + ", ".join(data.get("capabilities", [])))
|
||||
if data.get("storage"):
|
||||
click.echo(f"storage: {data['storage']}")
|
||||
if data.get("policy"):
|
||||
click.echo(f"policy: {data['policy']}")
|
||||
if data.get("capability_check"):
|
||||
check = data["capability_check"]
|
||||
click.echo("compatible" if check["compatible"] else "incompatible")
|
||||
if check.get("missing"):
|
||||
click.echo("missing: " + ", ".join(check["missing"]))
|
||||
|
||||
|
||||
def _emit_snapshot_identity(data: dict, output_format: str) -> None:
|
||||
if output_format == "json":
|
||||
click.echo(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
elif output_format == "yaml":
|
||||
click.echo(yaml.safe_dump(data, sort_keys=False))
|
||||
else:
|
||||
click.echo(data["snapshot_id"])
|
||||
click.echo(f"content_hash: {data['content_hash']}")
|
||||
click.echo(f"parser: {data['parser']} {data['parser_version']}")
|
||||
|
||||
|
||||
def _emit_content_class_result(data: dict, output_format: str) -> None:
|
||||
if output_format == "json":
|
||||
click.echo(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
|
||||
Reference in New Issue
Block a user