chore: tighten upkeep boundaries and static checks
prd-number-check / require-numbered-prds (pull_request) Successful in 8s
test / integration-docker (pull_request) Successful in 12s
test / unit (pull_request) Successful in 49s
test / coverage (pull_request) Successful in 15s
tracker-policy-pr / check-pr (pull_request) Successful in 5s
test / integration-docker (push) Successful in 13s
test / unit (push) Successful in 48s
lint / lint (push) Successful in 56s
Update Quality Badges / update-badges (push) Successful in 50s
test / coverage (push) Successful in 13s
prd-number-check / require-numbered-prds (pull_request) Successful in 8s
test / integration-docker (pull_request) Successful in 12s
test / unit (pull_request) Successful in 49s
test / coverage (pull_request) Successful in 15s
tracker-policy-pr / check-pr (pull_request) Successful in 5s
test / integration-docker (push) Successful in 13s
test / unit (push) Successful in 48s
lint / lint (push) Successful in 56s
Update Quality Badges / update-badges (push) Successful in 50s
test / coverage (push) Successful in 13s
This commit was merged in pull request #491.
This commit is contained in:
@@ -11,7 +11,12 @@ import subprocess
|
|||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
|
||||||
from ...log import die, info
|
from ...log import die, info
|
||||||
from ...util import slugify
|
from ...util import slugify as _slugify
|
||||||
|
|
||||||
|
|
||||||
|
def slugify(name: str) -> str:
|
||||||
|
"""Compatibility wrapper; new generic callers import ``bot_bottle.util``."""
|
||||||
|
return _slugify(name)
|
||||||
|
|
||||||
|
|
||||||
def run_docker(
|
def run_docker(
|
||||||
|
|||||||
@@ -67,11 +67,14 @@ class BottlePreparationPlanner:
|
|||||||
|
|
||||||
def prepare(self, spec: BottleSpec) -> PreparedBottle:
|
def prepare(self, spec: BottleSpec) -> PreparedBottle:
|
||||||
backend = self._backend
|
backend = self._backend
|
||||||
manifest = backend._validate(spec)
|
# These are deliberately protected backend hooks: only this shared
|
||||||
|
# planner orchestrates them, while concrete backends provide the
|
||||||
|
# implementation.
|
||||||
|
manifest = backend._validate(spec) # pylint: disable=protected-access
|
||||||
if not backend.supports_nested_containers:
|
if not backend.supports_nested_containers:
|
||||||
reject_nested_containers(backend.name, manifest)
|
reject_nested_containers(backend.name, manifest)
|
||||||
|
|
||||||
backend._preflight()
|
backend._preflight() # pylint: disable=protected-access
|
||||||
manifest = GitGate().preflight_host_keys(
|
manifest = GitGate().preflight_host_keys(
|
||||||
manifest,
|
manifest,
|
||||||
headless=spec.headless,
|
headless=spec.headless,
|
||||||
@@ -98,7 +101,9 @@ class BottlePreparationPlanner:
|
|||||||
state_dir=agent_dir,
|
state_dir=agent_dir,
|
||||||
instance_name=f"bot-bottle-{slug}",
|
instance_name=f"bot-bottle-{slug}",
|
||||||
prompt_file=prompt_file,
|
prompt_file=prompt_file,
|
||||||
guest_env=backend._build_guest_env(resolved_env),
|
guest_env=backend._build_guest_env( # pylint: disable=protected-access
|
||||||
|
resolved_env
|
||||||
|
),
|
||||||
forward_host_credentials=provider_config.forward_host_credentials,
|
forward_host_credentials=provider_config.forward_host_credentials,
|
||||||
auth_token=provider_config.auth_token,
|
auth_token=provider_config.auth_token,
|
||||||
host_env=dict(os.environ),
|
host_env=dict(os.environ),
|
||||||
|
|||||||
@@ -508,7 +508,10 @@ def _detail_view(
|
|||||||
return "reject aborted (empty reason)"
|
return "reject aborted (empty reason)"
|
||||||
|
|
||||||
|
|
||||||
def _modify(stdscr: "curses._CursesWindow", qp: QueuedProposal) -> str | None: # type: ignore # pragma: no cover
|
def _modify(
|
||||||
|
stdscr: "curses._CursesWindow", # type: ignore
|
||||||
|
qp: QueuedProposal,
|
||||||
|
) -> str | None: # 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()
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ def _path_matches(pm: PathMatch, request_path: str) -> bool:
|
|||||||
if not pm.value.endswith("/"):
|
if not pm.value.endswith("/"):
|
||||||
return request_path.startswith(pm.value + "/")
|
return request_path.startswith(pm.value + "/")
|
||||||
return request_path.startswith(pm.value)
|
return request_path.startswith(pm.value)
|
||||||
return pm.type == "regex" and pm.compiled is not None and pm.compiled.search(request_path) is not None
|
return (
|
||||||
|
pm.type == "regex"
|
||||||
|
and pm.compiled is not None
|
||||||
|
and pm.compiled.search(request_path) is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _entry_matches(
|
def _entry_matches(
|
||||||
|
|||||||
@@ -347,5 +347,3 @@ def load_config(text: str) -> "Config":
|
|||||||
except YamlSubsetError as e:
|
except YamlSubsetError as e:
|
||||||
raise ValueError(f"routes payload: invalid YAML: {e}") from e
|
raise ValueError(f"routes payload: invalid YAML: {e}") from e
|
||||||
return parse_config(payload)
|
return parse_config(payload)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ class TestCliBackendBoundaries(unittest.TestCase):
|
|||||||
for path in (ROOT / "bot_bottle" / "cli").rglob("*.py"):
|
for path in (ROOT / "bot_bottle" / "cli").rglob("*.py"):
|
||||||
tree = ast.parse(path.read_text(), filename=str(path))
|
tree = ast.parse(path.read_text(), filename=str(path))
|
||||||
for node in ast.walk(tree):
|
for node in ast.walk(tree):
|
||||||
module = node.module if isinstance(node, ast.ImportFrom) else None
|
if isinstance(node, ast.ImportFrom):
|
||||||
if module and module.startswith(forbidden):
|
module = node.module
|
||||||
violations.append(f"{path.relative_to(ROOT)}:{node.lineno}: {module}")
|
if module and module.startswith(forbidden):
|
||||||
|
violations.append(
|
||||||
|
f"{path.relative_to(ROOT)}:{node.lineno}: {module}"
|
||||||
|
)
|
||||||
if isinstance(node, ast.Import):
|
if isinstance(node, ast.Import):
|
||||||
violations.extend(
|
violations.extend(
|
||||||
f"{path.relative_to(ROOT)}:{node.lineno}: {alias.name}"
|
f"{path.relative_to(ROOT)}:{node.lineno}: {alias.name}"
|
||||||
|
|||||||
@@ -672,13 +672,13 @@ class TestReconcileRoute(unittest.TestCase):
|
|||||||
self.orch, "POST", "/reconcile",
|
self.orch, "POST", "/reconcile",
|
||||||
_body({"live_source_ips": [None, 7, "10.0.0.9"]}))
|
_body({"live_source_ips": [None, 7, "10.0.0.9"]}))
|
||||||
self.assertEqual(400, status)
|
self.assertEqual(400, status)
|
||||||
self.assertIn("live_source_ips", payload["error"])
|
self.assertIn("live_source_ips", str(payload["error"]))
|
||||||
|
|
||||||
def test_empty_live_source_ip_is_rejected(self) -> None:
|
def test_empty_live_source_ip_is_rejected(self) -> None:
|
||||||
status, payload = dispatch(
|
status, payload = dispatch(
|
||||||
self.orch, "POST", "/reconcile", _body({"live_source_ips": [""]}))
|
self.orch, "POST", "/reconcile", _body({"live_source_ips": [""]}))
|
||||||
self.assertEqual(400, status)
|
self.assertEqual(400, status)
|
||||||
self.assertIn("live_source_ips", payload["error"])
|
self.assertIn("live_source_ips", str(payload["error"]))
|
||||||
|
|
||||||
def test_invalid_grace_seconds_is_rejected(self) -> None:
|
def test_invalid_grace_seconds_is_rejected(self) -> None:
|
||||||
for value in (True, "30", -1, float("inf"), float("nan")):
|
for value in (True, "30", -1, float("inf"), float("nan")):
|
||||||
@@ -687,4 +687,4 @@ class TestReconcileRoute(unittest.TestCase):
|
|||||||
self.orch, "POST", "/reconcile",
|
self.orch, "POST", "/reconcile",
|
||||||
_body({"live_source_ips": [], "grace_seconds": value}))
|
_body({"live_source_ips": [], "grace_seconds": value}))
|
||||||
self.assertEqual(400, status)
|
self.assertEqual(400, status)
|
||||||
self.assertIn("grace_seconds", payload["error"])
|
self.assertIn("grace_seconds", str(payload["error"]))
|
||||||
|
|||||||
Reference in New Issue
Block a user