Implemented the next missing parity slice around setup-time runtime checks.

This commit is contained in:
Abdelrahman Abdallah
2026-04-20 00:23:45 +02:00
parent 266e1ac563
commit b2a5a431c6
5 changed files with 378 additions and 4 deletions
+77 -2
View File
@@ -2,11 +2,61 @@ from __future__ import annotations
import platform
import sys
from dataclasses import dataclass
from dataclasses import dataclass, field
from pathlib import Path
from .deferred_init import DeferredInitResult, run_deferred_init
from .prefetch import PrefetchResult, start_keychain_prefetch, start_mdm_raw_read, start_project_scan
from .release_notes import check_for_release_notes
MIN_PYTHON_VERSION: tuple[int, int] = (3, 10)
@dataclass(frozen=True)
class RuntimeRequirementCheck:
name: str
ok: bool
detail: str
def check_runtime_requirements() -> tuple[RuntimeRequirementCheck, ...]:
checks: list[RuntimeRequirementCheck] = []
py_major, py_minor = sys.version_info[:2]
py_ok = (py_major, py_minor) >= MIN_PYTHON_VERSION
checks.append(RuntimeRequirementCheck(
name='python_version',
ok=py_ok,
detail=(
f'Python {py_major}.{py_minor} >= {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]}'
if py_ok
else f'Python {py_major}.{py_minor} is below required '
f'{MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]}'
),
))
impl = platform.python_implementation()
checks.append(RuntimeRequirementCheck(
name='python_implementation',
ok=True,
detail=impl,
))
machine = platform.machine() or 'unknown'
system = platform.system() or 'unknown'
checks.append(RuntimeRequirementCheck(
name='platform',
ok=True,
detail=f'{system} on {machine}',
))
return tuple(checks)
def _read_package_version() -> str:
try:
from importlib.metadata import version
return version('claw-code-agent')
except Exception:
return '0.0.0'
@dataclass(frozen=True)
@@ -34,6 +84,11 @@ class SetupReport:
deferred_init: DeferredInitResult
trusted: bool
cwd: Path
runtime_checks: tuple[RuntimeRequirementCheck, ...] = field(default_factory=tuple)
release_notes: tuple[str, ...] = field(default_factory=tuple)
def has_blocking_issues(self) -> bool:
return any(not check.ok for check in self.runtime_checks)
def as_markdown(self) -> str:
lines = [
@@ -44,12 +99,21 @@ class SetupReport:
f'- Trusted mode: {self.trusted}',
f'- CWD: {self.cwd}',
'',
'Runtime checks:',
*(
f'- {check.name}: {"ok" if check.ok else "FAIL"}{check.detail}'
for check in self.runtime_checks
),
'',
'Prefetches:',
*(f'- {prefetch.name}: {prefetch.detail}' for prefetch in self.prefetches),
'',
'Deferred init:',
*self.deferred_init.as_lines(),
]
if self.release_notes:
lines.extend(['', 'Release notes (newer than last seen):'])
lines.extend(f'- {note}' for note in self.release_notes)
return '\n'.join(lines)
@@ -61,17 +125,28 @@ def build_workspace_setup() -> WorkspaceSetup:
)
def run_setup(cwd: Path | None = None, trusted: bool = True) -> SetupReport:
def run_setup(
cwd: Path | None = None,
trusted: bool = True,
last_seen_version: str | None = None,
) -> SetupReport:
root = cwd or Path(__file__).resolve().parent.parent
prefetches = [
start_mdm_raw_read(),
start_keychain_prefetch(),
start_project_scan(root),
]
release_notes_payload = check_for_release_notes(
current_version=_read_package_version(),
last_seen_version=last_seen_version,
cwd=root,
)
return SetupReport(
setup=build_workspace_setup(),
prefetches=tuple(prefetches),
deferred_init=run_deferred_init(trusted=trusted),
trusted=trusted,
cwd=root,
runtime_checks=check_runtime_requirements(),
release_notes=tuple(release_notes_payload['releaseNotes']),
)