Skip to content

cli: fail fast instead of hanging on a busy or wrong port - #9

Merged
cadywang merged 4 commits into
mainfrom
tests/no-hang
Oct 19, 2026
Merged

cadywang merged 4 commits into
mainfrom
tests/no-hang

Conversation

@cadywang

Copy link
Copy Markdown
Owner

变更说明

1. 合并方式改为只允许 Rebase and merge(governance/repository-settings.json,线上设置已同步)
Squash 会把整个 PR 压成一个提交:标题变成 PR 标题、每个提交的 Signed-off-by: 也不再是原文。
#7 就是这么把 main 判红的,而受保护的历史改不回来。rebase 让 PR 里的提交原样落到 main,
代价是提交信息必须一开始就写对(pr-gate 与本地钩子仍会在合并前把关)。

2. cli/devctl.ps1 连接加超时
devctl.py 一直是 15 秒放弃,PowerShell 版却是阻塞等待:端口不通时脚本一直挂着,
正是「client 上的端口被占」被感知成「工具卡死」的原因。现在两版一致:15 秒内报错退出 1,
错误信息也去掉了 .NET 包装(直接显示 socket 的错误)。

3. 两套自测不再卡死
selftest.ps1 曾默认用 17801,而本机这个端口常已被 VS Code 的端口转发占住(Windows 上
更具体的 127.0.0.1 监听会盖过 agent 的 0.0.0.0),devctl 于是连到陌生人身上、永远等回复。
现在两套自测默认向系统要一个空闲端口(-Port / --port 仍可指定),
指定的端口已被占用时立刻报错退出(退出码 2)。CI 的第一次 PowerShell / Python 运行改走默认路径。

本 PR 的提交:

  • docs: document the two fail-fast paths
  • tests: pick a free port for the self tests
  • cli: time out the PowerShell connect instead of hanging
  • repo: land pull requests by rebasing, never by squashing

变更类型

  • 缺陷修复(fix)
  • 构建、CI、打包(build / ci)
  • 仅文档(docs)

影响面清单

  • cli/devctl.ps1
  • tests/*
  • 文档 / 仓库治理文件

agent/*、cli/devctl.py、PROTOCOL.md 未改动:协议与 agent 行为不变。

测试

selftest.ps1  (auto port)              : 12 passed, 0 failed
selftest.ps1  (-Port 17852 -Codec hex) : 12 passed, 0 failed
test_e2e.py   (auto port)              : 11 passed, 0 failed
test_e2e.py   (--codec hex)            : 11 passed, 0 failed
tests/test_check_commits.py            : 18 passed, 0 failed
tests/test_check_pr.py                 : 18 passed, 0 failed
commit 规范门禁 origin/main..HEAD       : 4/4 commits follow the kernel guidelines
hygiene (BOM/CRLF/compileall/ps parse/bash -n/yaml/json): all green

失败路径(本机实测,重点是「不再卡死」):

selftest.ps1 -Port 17801   (已被 VS Code 占用) : 秒退,退出码 2,并说明端口已被占用
test_e2e.py  --port 17801                      : 同上
devctl.ps1 -Tcp 127.0.0.1:17899 (无人监听)      : 立即报错,退出码 1
devctl.ps1 -Tcp 10.255.255.1:7801 (黑洞地址)    : "connect timed out after 15000 ms",15 秒退出 1
devctl.ps1 -Tcp <mock agent> info / shutdown   : 正常(退出码 0)
  • 已在真实设备上验证(如适用),命令:bash tests/verify-live.sh <client>:<port>

设备与链路信息(涉及传输改动时必填):

项 值
client OS 不适用(未改动传输路径)
串口 / 波特率 不适用
设备 shell / 系统 不适用
可用工具(base64/awk/od/md5sum) 不适用

合并前清单

CI 强制逐条勾选(pr-gate):少一项就不能合并。

  • 本地 mock 自测已全部通过,输出贴在「测试」小节
  • 协议 / 传输行为有改动时,PROTOCOL.md 与 agent/*、cli/* 四个实现已同步更新
  • 代码与注释为英文,文档为中文
  • 文件为 UTF-8(无 BOM)+ LF
  • 未提交临时产物(.tmp-*/、*.log、__pycache__/)
  • 每个 commit 遵循内核规范:<subsystem>: 祈使句摘要、正文 72 列折行,并带 Signed-off-by:(git commit -s)
  • PR 标题遵循内核规范 <subsystem>: 祈使句摘要>(网页端 squash 会把它写成提交标题)
  • 已更新 CHANGELOG.md(如面向用户的行为有变化)
  • 已阅读并遵守 SECURITY.md(不引入默认对外暴露、无鉴权的行为)

关联 Issue

无独立 Issue。起因:#7 合并后 main 的 Hygiene 变红(PR 标题被 squash 成提交标题),
以及本机跑 tests/selftest.ps1 会永久卡住。合并方式与两个卡死点都在本 PR 里修掉。

Squash replaces the pull request with one commit whose subject is the pull
request title: the individual commits, their messages and their sign-offs
are gone, and the title is written into a history that cannot be rewritten.
#7 is the proof -- "Agent/shutdown op (#7)" reached main that way and the
commit gate could only complain afterwards, on a protected branch.

Only Rebase and merge stays enabled, so the commits a reviewer approved
are the commits that land, with their authors intact. The price is that
the message has to be right before the merge, which is what the pull
request gate and the local hook are for.

The settings, the helper script's description, the gates table, the merge
step in CONTRIBUTING and the measured pitfalls all said "squash"; they say
"rebase" now, including what it costs to forget.

Signed-off-by: cadywang <cadywang@users.noreply.github.com>
devctl.py has always given up on the TCP connect after 15 seconds
(socket.create_connection); devctl.ps1 called TcpClient.Connect and waited
for the OS. Against an address that swallows packets that is a 20-second
stall, and against a port somebody else already answers -- a VS Code port
forward, say -- the script simply sat there, which is how "the port is
busy on the client" turned into "the tool hangs" for whoever ran it.

Connect asynchronously, give up after the same 15 seconds, report the
socket error unwrapped (a .NET wrapper message reads as a stack trace,
not as an error) and exit 1. Both implementations now wait the same.

Checked by hand: nothing listening -> refusal reported, exit 1; a
blackholed address -> "connect timed out after 15000 ms", exit 1 in 15 s;
a mock agent -> info and shutdown still work.

Signed-off-by: cadywang <cadywang@users.noreply.github.com>
Both self tests defaulted to a fixed port (17801 and 17870). The agent
binds 0.0.0.0 and on Windows a more specific 127.0.0.1 listener wins over
it, so the usual way to run the tests locally -- on the machine that also
has the IDE open -- could talk to a stranger instead: VS Code forwards the
ports it reads from the terminal, and the whole test then hangs inside
devctl with nothing to say why. 17801 is exactly the port the bridge
documents for its own agent, so the collision was not hypothetical.

Ask the OS for a free port when none is given, and refuse to run against
an explicit port that already answers. A wrong peer is now a message and
exit 2 instead of a hang, and a plain "tests/selftest.ps1" works on a busy
developer machine again.

The end-to-end test notices the same way, and the ports it reports now
include the one it picked. CI keeps explicit ports for the cross checks
but runs the first PowerShell and Python pass on the default path, so the
auto-pick is exercised on both runners.

Signed-off-by: cadywang <cadywang@users.noreply.github.com>
The connect timeout and the self-test port choice change what people see
when something is wrong: a wrong endpoint now says so instead of waiting,
and the self tests no longer need a port picked by hand. Say it where the
options are listed and where the tests are introduced, so the next reader
does not take the 15-second exit for a broken bridge or wonder which port
the tests used.

The changelog records the failure paths that were checked on Windows, not
just the passing runs.

Signed-off-by: cadywang <cadywang@users.noreply.github.com>
@cadywang
cadywang merged commit 335d175 into main Oct 19, 2026
6 checks passed
@cadywang
cadywang deleted the tests/no-hang branch October 19, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant