feat(git-proxy): stop 只杀本 CLI 启的 fastgithub,并入 en0 主 LAN IP 供 Parallels 桥接
This commit is contained in:
@@ -112,6 +112,24 @@ def _restore_baseline(proxy: str, port: int | None) -> str:
|
||||
return proxy
|
||||
|
||||
|
||||
def _primary_lan_ip() -> str | None:
|
||||
"""Wi-Fi/Ethernet IPv4 (en0). Bridged Parallels guests use this, not *.*.*.2."""
|
||||
if sys.platform != "darwin":
|
||||
return None
|
||||
try:
|
||||
out = subprocess.check_output(
|
||||
["ipconfig", "getifaddr", "en0"],
|
||||
text=True,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
return None
|
||||
ip = out.strip()
|
||||
if not ip or ip.startswith("127."):
|
||||
return None
|
||||
return ip
|
||||
|
||||
|
||||
def _parallels_host_ips() -> list[str]:
|
||||
"""Mac IPs that Parallels guests use to reach this host (auto from ifconfig)."""
|
||||
if sys.platform != "darwin":
|
||||
@@ -130,6 +148,9 @@ def _parallels_host_ips() -> list[str]:
|
||||
ip = match.group(1)
|
||||
if ip not in ips:
|
||||
ips.append(ip)
|
||||
lan = _primary_lan_ip()
|
||||
if lan and lan not in ips:
|
||||
ips.append(lan)
|
||||
return ips
|
||||
|
||||
|
||||
@@ -483,6 +504,20 @@ def _collect_forward_pids(port: int) -> list[int]:
|
||||
return pids
|
||||
|
||||
|
||||
def _should_kill_proxy_process(state: dict[str, Any] | None, name: str) -> bool:
|
||||
"""Kill FastGitHub only if this CLI spawned it.
|
||||
|
||||
Native `fastgithub start` / CI `ensure-fastgithub.sh` / a leftover instance
|
||||
must keep running when `mytoolkit git proxy stop` only tears down socat
|
||||
and git config.
|
||||
"""
|
||||
if not state or state.get("name") != name:
|
||||
return False
|
||||
if state.get("adopted"):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _start_daemon(name: str, *, set_git: bool) -> None:
|
||||
# Clear a proxy left pointing at a dead daemon by an earlier aborted run.
|
||||
_restore_stale_git_proxy()
|
||||
@@ -498,6 +533,7 @@ def _start_daemon(name: str, *, set_git: bool) -> None:
|
||||
old_https = _restore_baseline(old_https, port)
|
||||
proxy_pid: int | None = None
|
||||
forward_pids: list[int] = []
|
||||
adopted = False
|
||||
|
||||
if port is None:
|
||||
click.secho(
|
||||
@@ -526,10 +562,12 @@ def _start_daemon(name: str, *, set_git: bool) -> None:
|
||||
pids = _procs_matching(_pids_listening_on("127.0.0.1", port), name)
|
||||
if pids:
|
||||
click.secho(
|
||||
f"{name} already listening on 127.0.0.1:{port} (pid={pids[0]})",
|
||||
f"{name} already listening on 127.0.0.1:{port} (pid={pids[0]}) "
|
||||
"— adopting, stop will not kill it",
|
||||
fg="cyan",
|
||||
)
|
||||
proxy_pid = pids[0]
|
||||
adopted = True
|
||||
else:
|
||||
click.secho(
|
||||
f"WARN: 127.0.0.1:{port} is held by an unknown process — "
|
||||
@@ -585,6 +623,7 @@ def _start_daemon(name: str, *, set_git: bool) -> None:
|
||||
"port": port,
|
||||
"proxy_pid": proxy_pid,
|
||||
"forward_pids": forward_pids,
|
||||
"adopted": adopted,
|
||||
"set_git": set_git,
|
||||
"old_http": old_http if set_git else None,
|
||||
"old_https": old_https if set_git else None,
|
||||
@@ -623,6 +662,7 @@ def _stop_daemon(name: str | None) -> None:
|
||||
click.secho(f"{name} socat: nothing listening on guest IPs", fg="yellow")
|
||||
|
||||
proxy_pids = _procs_matching(_pids_listening_on("127.0.0.1", port), name)
|
||||
if _should_kill_proxy_process(state if state_matches else None, name):
|
||||
if proxy_pids:
|
||||
_kill_pids(proxy_pids, name)
|
||||
else:
|
||||
@@ -631,6 +671,18 @@ def _stop_daemon(name: str | None) -> None:
|
||||
"(or port is held by an unrelated process — left untouched)",
|
||||
fg="yellow",
|
||||
)
|
||||
elif proxy_pids:
|
||||
click.secho(
|
||||
f"Leaving {name} pid={proxy_pids[0]} running "
|
||||
"(started outside mytoolkit; stop only tears down socat/git)",
|
||||
fg="cyan",
|
||||
)
|
||||
else:
|
||||
click.secho(
|
||||
f"{name} is not listening on 127.0.0.1:{port} "
|
||||
"(or port is held by an unrelated process — left untouched)",
|
||||
fg="yellow",
|
||||
)
|
||||
else:
|
||||
click.secho(f"{name}: no local loopback daemon to stop", fg="yellow")
|
||||
|
||||
@@ -930,6 +982,7 @@ def proxy_run(name: str, command: tuple[str, ...]) -> None:
|
||||
(_pids_listening_on("127.0.0.1", port) or [None])[0]
|
||||
),
|
||||
"forward_pids": [p.pid for p in forward_procs if p.pid],
|
||||
"adopted": proxy_proc is None,
|
||||
"set_git": True,
|
||||
"old_http": old_http,
|
||||
"old_https": old_https,
|
||||
|
||||
@@ -85,3 +85,31 @@ def test_restore_stale_git_proxy_noop_when_alive(monkeypatch):
|
||||
)
|
||||
assert _restore_stale_git_proxy() is False
|
||||
assert restored == {}
|
||||
|
||||
|
||||
def test_parallels_host_ips_includes_lan(monkeypatch):
|
||||
from mytoolkit.commands.git import _parallels_host_ips
|
||||
|
||||
monkeypatch.setattr("mytoolkit.commands.git.sys.platform", "darwin")
|
||||
monkeypatch.delenv("XIAOHE_CI_PARALLELS_HOST_IP", raising=False)
|
||||
monkeypatch.setattr(
|
||||
"mytoolkit.commands.git.subprocess.check_output",
|
||||
lambda *a, **k: "inet 10.211.55.2 netmask\n",
|
||||
)
|
||||
monkeypatch.setattr("mytoolkit.commands.git._primary_lan_ip", lambda: "192.168.1.168")
|
||||
assert _parallels_host_ips() == ["10.211.55.2", "192.168.1.168"]
|
||||
|
||||
|
||||
def test_should_kill_proxy_only_if_we_spawned():
|
||||
from mytoolkit.commands.git import _should_kill_proxy_process
|
||||
|
||||
assert _should_kill_proxy_process(None, "fastgithub") is False
|
||||
assert _should_kill_proxy_process({"name": "other"}, "fastgithub") is False
|
||||
assert _should_kill_proxy_process(
|
||||
{"name": "fastgithub", "adopted": True}, "fastgithub"
|
||||
) is False
|
||||
assert _should_kill_proxy_process(
|
||||
{"name": "fastgithub", "adopted": False}, "fastgithub"
|
||||
) is True
|
||||
# Old state files without adopted: keep previous stop-kills-daemon behavior
|
||||
assert _should_kill_proxy_process({"name": "fastgithub"}, "fastgithub") is True
|
||||
|
||||
Reference in New Issue
Block a user