personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

doctor: warn-only on port 5015 collisions

`port_5015_free` is now an advisory check that returns status="warn"
on every failure path instead of blocker/fail, so `make doctor` and
`make install-service` no longer hard-fail on hosts where solstone is
already running at an install path doctor cannot recognize as
self-owned. The check still surfaces the colliding pid and exe path;
it just stops gating downstream make targets.

+18 -12
+9 -8
scripts/doctor.py
··· 31 31 import shutil 32 32 import subprocess 33 33 import sys 34 - from dataclasses import dataclass 34 + from dataclasses import dataclass, replace 35 35 from pathlib import Path 36 36 from typing import Callable, Literal, Sequence 37 37 ··· 481 481 fix="kill <pid> # or run 'sol service stop' if this is your install", 482 482 ) 483 483 if isinstance(probe, CheckResult): 484 - return probe 484 + return replace(probe, status="warn") 485 485 pids = [ 486 486 line[1:].strip() for line in probe.stdout.splitlines() if line.startswith("p") 487 487 ] ··· 491 491 try: 492 492 pid = int(pid_text) 493 493 except ValueError: 494 - return unexpected_output_result( 494 + result = unexpected_output_result( 495 495 check, 496 496 probe.stdout, 497 497 fix="kill <pid> # or run 'sol service stop' if this is your install", 498 498 ) 499 + return replace(result, status="warn") 499 500 expected_repo_sol = (ROOT / ".venv" / "bin" / "sol").resolve() 500 501 alias_target = resolve_alias_target() 501 502 if platform_tag() == "darwin": ··· 506 507 alias_target=alias_target, 507 508 ) 508 509 if isinstance(resolved, CheckResult): 509 - return resolved 510 + return replace(resolved, status="warn") 510 511 exe_path = resolved 511 512 else: 512 513 try: ··· 514 515 except OSError as exc: 515 516 return make_result( 516 517 check, 517 - "fail", 518 + "warn", 518 519 f"could not verify ownership (pid={pid}): {type(exc).__name__}: {exc}", 519 520 f"kill {pid} # or run 'sol service stop' if this is your install", 520 521 ) ··· 532 533 ) 533 534 return make_result( 534 535 check, 535 - "fail", 536 - f"port {port} held by pid {pid} ({exe_path})", 536 + "warn", 537 + f"port {port} is in use by pid {pid} ({exe_path}); solstone may already be installed and active on this system", 537 538 f"kill {pid} # or run 'sol service stop' if this is your install", 538 539 ) 539 540 ··· 780 781 Check("npx_non_interactive", "advisory", ("linux", "darwin")), 781 782 npx_non_interactive_check, 782 783 ), 783 - (Check("port_5015_free", "blocker", ("linux", "darwin")), port_5015_free_check), 784 + (Check("port_5015_free", "advisory", ("linux", "darwin")), port_5015_free_check), 784 785 (Check("disk_space", "advisory", ("linux", "darwin")), disk_space_check), 785 786 ( 786 787 Check("config_dir_readable", "blocker", ("linux", "darwin")),
+9 -4
tests/test_doctor.py
··· 269 269 270 270 271 271 class TestPortCheck: 272 + def test_severity_is_advisory(self, doctor): 273 + assert doctor.CHECK_MAP["port_5015_free"].severity == "advisory" 274 + 272 275 def test_skip_when_lsof_missing(self, doctor, monkeypatch): 273 276 monkeypatch.setattr(doctor.shutil, "which", lambda _name: None) 274 277 result = doctor.port_5015_free_check(args(doctor)) ··· 307 310 assert result.status == "ok" 308 311 assert "this repo's solstone" in result.detail 309 312 310 - def test_fail_when_exe_not_owned_even_if_name_mentions_sol( 313 + def test_warn_when_exe_not_owned_even_if_name_mentions_sol( 311 314 self, doctor, monkeypatch, tmp_path 312 315 ): 313 316 monkeypatch.setattr(doctor, "ROOT", tmp_path) ··· 320 323 monkeypatch.setattr(doctor, "resolve_alias_target", lambda: None) 321 324 monkeypatch.setattr(doctor.os, "readlink", lambda _path: "/usr/bin/python3") 322 325 result = doctor.port_5015_free_check(args(doctor)) 323 - assert result.status == "fail" 326 + assert result.status == "warn" 327 + assert result.severity == "advisory" 324 328 assert "/usr/bin/python3" in result.detail 325 329 326 - def test_fail_on_lsof_timeout(self, doctor, monkeypatch): 330 + def test_warn_on_lsof_timeout(self, doctor, monkeypatch): 327 331 monkeypatch.setattr( 328 332 doctor, 329 333 "import_install_guard", ··· 336 340 337 341 monkeypatch.setattr(doctor.subprocess, "run", raise_timeout) 338 342 result = doctor.port_5015_free_check(args(doctor)) 339 - assert result.status == "fail" 343 + assert result.status == "warn" 344 + assert result.severity == "advisory" 340 345 assert "timed out" in result.detail 341 346 342 347