feat(skills): add artifact presenter
This commit is contained in:
+195
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Open one review artifact in a reusable right-side Herdr mcat pane."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Callable, Mapping, Sequence
|
||||
|
||||
PANE_LABEL = "artifact presenter"
|
||||
|
||||
RunCommand = Callable[..., subprocess.CompletedProcess[str]]
|
||||
FindExecutable = Callable[[str], str | None]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PresentationResult:
|
||||
status: str
|
||||
artifact: Path
|
||||
pane_id: str | None = None
|
||||
reason: str | None = None
|
||||
|
||||
def message(self) -> str:
|
||||
if self.status == "artifact-only":
|
||||
return f"artifact-only: {self.reason}; {self.artifact}"
|
||||
return f"{self.status}: pane {self.pane_id}; {self.artifact}"
|
||||
|
||||
|
||||
def run_json(
|
||||
executable: str,
|
||||
args: Sequence[str],
|
||||
run: RunCommand,
|
||||
) -> dict[str, object]:
|
||||
completed = run(
|
||||
[executable, *args],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
value = json.loads(completed.stdout)
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError("Herdr returned a non-object response")
|
||||
return value
|
||||
|
||||
|
||||
def panes_with_label(response: Mapping[str, object]) -> list[str]:
|
||||
result = response.get("result")
|
||||
if not isinstance(result, dict):
|
||||
return []
|
||||
panes = result.get("panes")
|
||||
if not isinstance(panes, list):
|
||||
return []
|
||||
pane_ids: list[str] = []
|
||||
for pane in panes:
|
||||
if not isinstance(pane, dict) or pane.get("label") != PANE_LABEL:
|
||||
continue
|
||||
pane_id = pane.get("pane_id")
|
||||
if isinstance(pane_id, str) and pane_id:
|
||||
pane_ids.append(pane_id)
|
||||
return pane_ids
|
||||
|
||||
|
||||
def created_pane_id(response: Mapping[str, object]) -> str:
|
||||
result = response.get("result")
|
||||
pane = result.get("pane") if isinstance(result, dict) else None
|
||||
pane_id = pane.get("pane_id") if isinstance(pane, dict) else None
|
||||
if not isinstance(pane_id, str) or not pane_id:
|
||||
raise ValueError("Herdr split response is missing result.pane.pane_id")
|
||||
return pane_id
|
||||
|
||||
|
||||
def run_mcat(
|
||||
herdr: str,
|
||||
mcat: str,
|
||||
pane_id: str,
|
||||
artifact: Path,
|
||||
run: RunCommand,
|
||||
) -> None:
|
||||
run(
|
||||
[herdr, "pane", "run", pane_id, mcat, str(artifact)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
def present_document(
|
||||
document: Path,
|
||||
*,
|
||||
env: Mapping[str, str] = os.environ,
|
||||
which: FindExecutable = shutil.which,
|
||||
run: RunCommand = subprocess.run,
|
||||
) -> PresentationResult:
|
||||
artifact = document.expanduser().resolve()
|
||||
if not artifact.is_file():
|
||||
raise ValueError(f"artifact is not a file: {artifact}")
|
||||
|
||||
herdr = which("herdr")
|
||||
mcat = which("mcat")
|
||||
workspace_id = env.get("HERDR_WORKSPACE_ID")
|
||||
source_pane_id = env.get("HERDR_PANE_ID")
|
||||
if not herdr:
|
||||
return PresentationResult("artifact-only", artifact, reason="herdr is unavailable")
|
||||
if not mcat:
|
||||
return PresentationResult("artifact-only", artifact, reason="mcat is unavailable")
|
||||
if not workspace_id or not source_pane_id:
|
||||
return PresentationResult("artifact-only", artifact, reason="not running in a Herdr pane")
|
||||
|
||||
try:
|
||||
pane_list = run_json(herdr, ["pane", "list", "--workspace", workspace_id], run)
|
||||
existing_panes = panes_with_label(pane_list)
|
||||
except (json.JSONDecodeError, OSError, subprocess.SubprocessError, ValueError) as error:
|
||||
return PresentationResult("artifact-only", artifact, reason=f"could not list Herdr panes: {error}")
|
||||
|
||||
for existing_pane in existing_panes:
|
||||
try:
|
||||
run_mcat(herdr, mcat, existing_pane, artifact, run)
|
||||
return PresentationResult("reused", artifact, pane_id=existing_pane)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
try:
|
||||
run(
|
||||
[herdr, "pane", "rename", existing_pane, f"{PANE_LABEL} stale"],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
try:
|
||||
split = run_json(
|
||||
herdr,
|
||||
[
|
||||
"pane",
|
||||
"split",
|
||||
"--pane",
|
||||
source_pane_id,
|
||||
"--direction",
|
||||
"right",
|
||||
"--cwd",
|
||||
str(artifact.parent),
|
||||
"--no-focus",
|
||||
],
|
||||
run,
|
||||
)
|
||||
pane_id = created_pane_id(split)
|
||||
except (json.JSONDecodeError, OSError, subprocess.SubprocessError, ValueError) as error:
|
||||
return PresentationResult("artifact-only", artifact, reason=f"could not create Herdr viewer: {error}")
|
||||
|
||||
try:
|
||||
run(
|
||||
[herdr, "pane", "rename", pane_id, PANE_LABEL],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
run_mcat(herdr, mcat, pane_id, artifact, run)
|
||||
return PresentationResult("opened", artifact, pane_id=pane_id)
|
||||
except (OSError, subprocess.SubprocessError) as error:
|
||||
try:
|
||||
run(
|
||||
[herdr, "pane", "close", pane_id],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
except OSError:
|
||||
pass
|
||||
return PresentationResult("artifact-only", artifact, reason=f"could not initialize Herdr viewer: {error}")
|
||||
|
||||
|
||||
def create_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("artifact", type=Path, help="Local file to present with mcat")
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = create_parser().parse_args()
|
||||
try:
|
||||
result = present_document(args.artifact)
|
||||
except ValueError as error:
|
||||
print(error, file=os.sys.stderr)
|
||||
return 2
|
||||
print(result.message())
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user