merge: update bot_bottle/cli/supervise.py from issue-277-coverage-ci
This commit is contained in:
@@ -2,9 +2,8 @@
|
|||||||
act on them (approve / modify / reject).
|
act on them (approve / modify / reject).
|
||||||
|
|
||||||
Curses-based TUI; modify-then-approve shells out to $EDITOR. The
|
Curses-based TUI; modify-then-approve shells out to $EDITOR. The
|
||||||
approval handler wires to PRD 0016 (capability-block), which rebuilds
|
Egress proposals are queued for operator review as full routes.yaml
|
||||||
the bottle Dockerfile. Egress proposals are queued for operator review
|
updates.
|
||||||
as full routes.yaml updates.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -22,10 +21,6 @@ from pathlib import Path
|
|||||||
|
|
||||||
from .. import supervise as _supervise
|
from .. import supervise as _supervise
|
||||||
from ..bottle_state import read_metadata
|
from ..bottle_state import read_metadata
|
||||||
# from ..backend.docker.capability_apply import (
|
|
||||||
# CapabilityApplyError,
|
|
||||||
# apply_capability_change,
|
|
||||||
# )
|
|
||||||
from ..backend.docker.egress_apply import (
|
from ..backend.docker.egress_apply import (
|
||||||
EgressApplyError,
|
EgressApplyError,
|
||||||
applicator as _docker_applicator,
|
applicator as _docker_applicator,
|
||||||
@@ -38,10 +33,6 @@ from ..backend.smolmachines.egress_apply import (
|
|||||||
)
|
)
|
||||||
from ..log import Die, error, info
|
from ..log import Die, error, info
|
||||||
|
|
||||||
|
|
||||||
class CapabilityApplyError(RuntimeError):
|
|
||||||
"""Placeholder while capability_apply is disabled."""
|
|
||||||
|
|
||||||
from ..supervise import (
|
from ..supervise import (
|
||||||
COMPONENT_FOR_TOOL,
|
COMPONENT_FOR_TOOL,
|
||||||
AuditEntry,
|
AuditEntry,
|
||||||
@@ -50,12 +41,10 @@ from ..supervise import (
|
|||||||
STATUS_APPROVED,
|
STATUS_APPROVED,
|
||||||
STATUS_MODIFIED,
|
STATUS_MODIFIED,
|
||||||
STATUS_REJECTED,
|
STATUS_REJECTED,
|
||||||
TOOL_CAPABILITY_BLOCK,
|
|
||||||
TOOL_EGRESS_ALLOW,
|
TOOL_EGRESS_ALLOW,
|
||||||
TOOL_EGRESS_BLOCK,
|
TOOL_EGRESS_BLOCK,
|
||||||
TOOL_GITLEAKS_ALLOW,
|
TOOL_GITLEAKS_ALLOW,
|
||||||
TOOL_EGRESS_TOKEN_ALLOW,
|
TOOL_EGRESS_TOKEN_ALLOW,
|
||||||
archive_proposal,
|
|
||||||
list_pending_proposals,
|
list_pending_proposals,
|
||||||
render_diff,
|
render_diff,
|
||||||
write_audit_entry,
|
write_audit_entry,
|
||||||
@@ -83,7 +72,7 @@ class QueuedProposal:
|
|||||||
# Errors any remediation engine may raise. Caught by the TUI key
|
# Errors any remediation engine may raise. Caught by the TUI key
|
||||||
# handlers and surfaced in the status line so a failed apply keeps
|
# handlers and surfaced in the status line so a failed apply keeps
|
||||||
# the proposal pending rather than crashing curses.
|
# the proposal pending rather than crashing curses.
|
||||||
ApplyError = (CapabilityApplyError, EgressApplyError)
|
ApplyError = (EgressApplyError,)
|
||||||
|
|
||||||
|
|
||||||
def apply_routes_change(slug: str, content: str) -> tuple[str, str]:
|
def apply_routes_change(slug: str, content: str) -> tuple[str, str]:
|
||||||
@@ -143,8 +132,6 @@ def _detail_lines(
|
|||||||
|
|
||||||
|
|
||||||
def _suffix_for_tool(tool: str) -> str:
|
def _suffix_for_tool(tool: str) -> str:
|
||||||
if tool == TOOL_CAPABILITY_BLOCK:
|
|
||||||
return ".dockerfile"
|
|
||||||
if tool in (TOOL_EGRESS_ALLOW, TOOL_EGRESS_BLOCK):
|
if tool in (TOOL_EGRESS_ALLOW, TOOL_EGRESS_BLOCK):
|
||||||
return ".yaml"
|
return ".yaml"
|
||||||
if tool in (TOOL_GITLEAKS_ALLOW, TOOL_EGRESS_TOKEN_ALLOW):
|
if tool in (TOOL_GITLEAKS_ALLOW, TOOL_EGRESS_TOKEN_ALLOW):
|
||||||
@@ -166,17 +153,6 @@ def approve(
|
|||||||
file_to_apply = final_file if final_file is not None else qp.proposal.proposed_file
|
file_to_apply = final_file if final_file is not None else qp.proposal.proposed_file
|
||||||
|
|
||||||
diff_before, diff_after = "", ""
|
diff_before, diff_after = "", ""
|
||||||
# if qp.proposal.tool == TOOL_CAPABILITY_BLOCK:
|
|
||||||
# _meta = read_metadata(qp.proposal.bottle_slug)
|
|
||||||
# if _meta is not None and not _meta.compose_project:
|
|
||||||
# raise CapabilityApplyError(
|
|
||||||
# "capability-block remediation is not supported for smolmachines "
|
|
||||||
# "bottles. Reject this proposal or handle the capability change "
|
|
||||||
# "manually, then restart the bottle."
|
|
||||||
# )
|
|
||||||
# diff_before, diff_after = apply_capability_change(
|
|
||||||
# qp.proposal.bottle_slug, file_to_apply,
|
|
||||||
# )
|
|
||||||
if qp.proposal.tool in (TOOL_EGRESS_ALLOW, TOOL_EGRESS_BLOCK):
|
if qp.proposal.tool in (TOOL_EGRESS_ALLOW, TOOL_EGRESS_BLOCK):
|
||||||
diff_before, diff_after = apply_routes_change(
|
diff_before, diff_after = apply_routes_change(
|
||||||
qp.proposal.bottle_slug,
|
qp.proposal.bottle_slug,
|
||||||
@@ -194,9 +170,6 @@ def approve(
|
|||||||
qp, action=status, notes=notes,
|
qp, action=status, notes=notes,
|
||||||
diff_before=diff_before, diff_after=diff_after,
|
diff_before=diff_before, diff_after=diff_after,
|
||||||
)
|
)
|
||||||
if qp.proposal.tool == TOOL_CAPABILITY_BLOCK:
|
|
||||||
archive_proposal(qp.queue_dir, qp.proposal.id)
|
|
||||||
|
|
||||||
|
|
||||||
def reject(qp: QueuedProposal, *, reason: str) -> None:
|
def reject(qp: QueuedProposal, *, reason: str) -> None:
|
||||||
"""Write a rejection response and an audit entry."""
|
"""Write a rejection response and an audit entry."""
|
||||||
@@ -346,7 +319,7 @@ def _list_once() -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def _try_init_green() -> int:
|
def _try_init_green() -> int: # pragma: no cover
|
||||||
"""Initialise a green color pair and return its attr, or 0."""
|
"""Initialise a green color pair and return its attr, or 0."""
|
||||||
try:
|
try:
|
||||||
curses.start_color()
|
curses.start_color()
|
||||||
@@ -357,7 +330,7 @@ def _try_init_green() -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def _main_loop(stdscr: "curses._CursesWindow") -> None: # type: ignore
|
def _main_loop(stdscr: "curses._CursesWindow") -> None: # type: ignore # pragma: no cover
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
stdscr.timeout(_REFRESH_INTERVAL_MS)
|
stdscr.timeout(_REFRESH_INTERVAL_MS)
|
||||||
green_attr = _try_init_green()
|
green_attr = _try_init_green()
|
||||||
@@ -447,7 +420,7 @@ def _render(
|
|||||||
status_line: str,
|
status_line: str,
|
||||||
*,
|
*,
|
||||||
green_attr: int = 0, # noqa: F841 — unused, but required by interface
|
green_attr: int = 0, # noqa: F841 — unused, but required by interface
|
||||||
) -> None:
|
) -> None: # pragma: no cover
|
||||||
stdscr.erase()
|
stdscr.erase()
|
||||||
h, w = stdscr.getmaxyx()
|
h, w = stdscr.getmaxyx()
|
||||||
header = f"bot-bottle supervise ({len(pending)} pending)"
|
header = f"bot-bottle supervise ({len(pending)} pending)"
|
||||||
@@ -498,7 +471,7 @@ def _detail_view(
|
|||||||
qp: QueuedProposal,
|
qp: QueuedProposal,
|
||||||
*,
|
*,
|
||||||
green_attr: int = 0,
|
green_attr: int = 0,
|
||||||
) -> None:
|
) -> None: # pragma: no cover
|
||||||
"""Render the full proposal. Scrollable. Press q to return."""
|
"""Render the full proposal. Scrollable. Press q to return."""
|
||||||
lines = _detail_lines(qp, green_attr=green_attr)
|
lines = _detail_lines(qp, green_attr=green_attr)
|
||||||
offset = 0
|
offset = 0
|
||||||
@@ -550,7 +523,7 @@ def _detail_view(
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def _modify(stdscr: "curses._CursesWindow", qp: QueuedProposal) -> str | None: # type: ignore
|
def _modify(stdscr: "curses._CursesWindow", qp: QueuedProposal) -> str | None: # type: ignore # pragma: no cover
|
||||||
"""Suspend curses, open $EDITOR on the proposed file, return edited content."""
|
"""Suspend curses, open $EDITOR on the proposed file, return edited content."""
|
||||||
suffix = _suffix_for_tool(qp.proposal.tool)
|
suffix = _suffix_for_tool(qp.proposal.tool)
|
||||||
curses.endwin()
|
curses.endwin()
|
||||||
@@ -561,7 +534,7 @@ def _modify(stdscr: "curses._CursesWindow", qp: QueuedProposal) -> str | None:
|
|||||||
return edited
|
return edited
|
||||||
|
|
||||||
|
|
||||||
def _prompt(stdscr: "curses._CursesWindow", label: str) -> str: # type: ignore
|
def _prompt(stdscr: "curses._CursesWindow", label: str) -> str: # type: ignore # pragma: no cover
|
||||||
"""One-line input at the bottom of the screen."""
|
"""One-line input at the bottom of the screen."""
|
||||||
curses.curs_set(1)
|
curses.curs_set(1)
|
||||||
h, _ = stdscr.getmaxyx()
|
h, _ = stdscr.getmaxyx()
|
||||||
|
|||||||
Reference in New Issue
Block a user