feat(delivery): final report + reserved-label guard + Windows test fixes (v2 phase A3-final)
Gap-closing on top of the merged A3-3..A3-6 work: - ProjectService.create_preview_domain now refuses a label that is a reserved platform name (git, mcp, api, panel, ...) via PROTECTED_HOST_LABELS. The rules set existed but only the owner-facing validate_extra_host consulted it, so the tenant path could reserve one. - process.py: signal.SIGKILL is resolved through getattr with a SIGTERM fallback, so the module imports and type-checks on Windows where SIGKILL does not exist. - Three more Windows test repairs: a git-created .git must be unlinked and recreated rather than overwritten in place; POSIX-only chmod and symlink assertions are gated on os.name (the security property they assert is checked everywhere, the platform-specific mechanics only where they apply). - docs/plan/reports/agent-3-final.md: the integration hand-off — host requirements, every setting, every endpoint added or changed, the tables, the open CHANGE REQUESTS, a 12-step manual QA script and the known limitations. Checks: ruff clean, mypy clean (136 files), 521 API tests pass, 83 web tests pass, tsc clean, next lint clean.
This commit is contained in:
@@ -60,6 +60,11 @@ _TERM_GRACE_SECONDS = 5.0
|
||||
#: pasted into a command line). Refused rather than silently truncated.
|
||||
MAX_ARGV_ELEMENT_CHARS = 100_000
|
||||
|
||||
#: ``SIGKILL`` does not exist on Windows (it has no equivalent), so it is looked
|
||||
#: up rather than referenced directly. POSIX is the only *supported* target, but
|
||||
#: the module must still import — and type-check — on a developer's Windows box.
|
||||
_SIGKILL: int = getattr(signal, "SIGKILL", signal.SIGTERM)
|
||||
|
||||
|
||||
class ProcessError(RuntimeError):
|
||||
"""A host command could not be started at all (binary missing, not executable)."""
|
||||
@@ -184,7 +189,7 @@ async def _terminate(proc: asyncio.subprocess.Process) -> None:
|
||||
return
|
||||
except TimeoutError:
|
||||
pass
|
||||
_signal_group(signal.SIGKILL)
|
||||
_signal_group(_SIGKILL)
|
||||
with contextlib.suppress(Exception):
|
||||
await proc.wait()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user