fix(cli): parse sandbox image registry ports#5325
Conversation
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Hi @tt-a1i — thanks for the fix! The actual code change (moving the image-name parser into a small tested helper that handles registry ports and digests) looks focused and correct. The bug in #5324 is real and the approach is clean.
However, this PR's description doesn't follow the PR template. The template is mandatory for all PRs because it gives reviewers the information they need to verify the change without having to read the diff in detail.
The current body uses ## Summary / ## Test plan / ## AI Assistance Disclosure. Please reformat it to match the template:
## What this PR doesand## Why it's needed— describe the change in prose and the motivation (you can largely reuse your Summary text).## Reviewer Test Plan— this is the main thing that's missing:### How to verify— the commands you already listed fit here.### Evidence (Before & After)— since this is non-user-visible,N/Ais fine, or paste the failing vs passing container-name output.### Tested on— fill in the OS table (Linux at minimum, since sandbox is Docker/Linux).
## Risk & Scope— one line each for main risk, what's out of scope, and whether there are breaking changes.## Linked Issues— useFixes #5324(orCloses #5324) so the issue auto-closes on merge. Your current body says "Fixes #5324" inline but it needs to be in this section.<details><summary>中文说明</summary>— full Chinese translation of the English body.
Quick test-plan note: the npm run typecheck --workspace=packages/cli / npm run build --workspace=packages/cli / npm run lint lines in your Test plan are good; they will be re-run by CI, but listing them under ### How to verify makes the reviewer flow explicit.
Once the template is filled in, I'll re-run triage and move to code review. Happy to answer questions here.
中文说明
@tt-a1i 你好 — 感谢提交修复!代码改动本身(把镜像名解析挪到一个有测试的小 helper 里,并正确处理 registry 端口和 digest)聚焦且正确,#5324 描述的 bug 确实存在,方案也很干净。
不过这个 PR 的描述没有遵循 PR 模板。模板对所有 PR 都是强制的,因为它能让 reviewer 在不细看 diff 的情况下也能完成验证。
当前正文使用的是 ## Summary / ## Test plan / ## AI Assistance Disclosure,请按模板改成以下结构:
## What this PR does和## Why it's needed—— 用自然语言描述改动和动机(基本可以复用你的 Summary)。## Reviewer Test Plan—— 这是目前缺失最多的部分:### How to verify—— 你已经列出的命令放在这里。### Evidence (Before & After)—— 非用户可见改动写N/A即可,也可以贴修复前后容器名的对比。### Tested on—— 填写 OS 表格(至少填 Linux,因为 sandbox 是 Docker/Linux)。
## Risk & Scope—— 用一行分别说明主要风险、不在范围内的事项、是否有破坏性变更。## Linked Issues—— 使用Fixes #5324(或Closes #5324),让 issue 在合并时自动关闭。你正文里写了 "Fixes #5324",但需要放在这个章节下。<details><summary>中文说明</summary>—— 英文正文的完整中文翻译。
小提示:Test plan 里的 npm run typecheck --workspace=packages/cli / npm run build --workspace=packages/cli / npm run lint 这些命令没问题;CI 会重跑,但把它们写在 ### How to verify 下能让 reviewer 的流程更清楚。
模板补齐之后我会重新跑一次 triage 并进入代码评审。有问题随时在这里沟通。
— Qwen Code · qwen3.7-max
|
@qwen-code /triage |
|
Thanks @tt-a1i for the fix! Template looks good ✓ — all required sections are filled in, including the bilingual Chinese translation. The prior Stage 1 gate flagged the old template format; the updated body addresses every point. On direction: this is a straightforward bug fix for #5324. The sandbox image-name parser breaks on registry ports ( On approach: the scope is tight and appropriate. Three files, all related to the fix:
No scope creep, no drive-by refactors. This is what a focused bugfix PR should look like. Moving on to code review. 🔍 中文说明感谢 @tt-a1i 的修复! 模板完整 ✓ —— 所有章节已填写,包括中英双语翻译。之前 Stage 1 指出的模板问题已全部解决。 方向:这是对 #5324 的直接 bug 修复。sandbox 镜像名解析在遇到 registry 端口时出错( 方案:范围紧凑合理。三个文件全部与修复相关:
没有范围蔓延,没有顺手重构。这就是一个聚焦 bugfix PR 应有的样子。 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewReviewed the diff independently. The fix is correct and clean:
No blockers. No AGENTS.md violations. The code is minimal, focused, and does exactly what the PR says. Test ResultsUnit TestsTypecheck & Lint
Before/After BehaviorRan the old The old parser was even worse than the issue described — it didn't just lose the tag on Note: full E2E sandbox testing (actual Docker container creation) requires a Docker daemon and sandbox configuration, which isn't available in this CI environment. The function is only called during container creation, so the unit tests + behavioral demo above cover the logic completely. 中文说明代码审查独立审查了 diff。修复正确且干净:
无阻塞问题,无 AGENTS.md 违规。代码最小化、聚焦,完全实现了 PR 描述的功能。 测试结果单元测试5/5 通过 ✅ Typecheck 和 Lint全部通过 ✅ 前后对比旧解析器比 issue 描述的更糟糕 —— 对 注:完整 E2E sandbox 测试(实际 Docker 容器创建)需要 Docker daemon 和 sandbox 配置,当前 CI 环境不可用。该函数仅在容器创建时调用,单元测试 + 行为对比 demo 已完整覆盖逻辑。 — Qwen Code · qwen3.7-max |
ReflectionStepping back to look at the whole picture: This PR fixes a genuine bug (#5324) that breaks sandbox container creation for anyone using a private registry with a port — a common setup in enterprise environments. The old parser was even more broken than the issue described: it produced container names with slashes (Docker-invalid) in addition to losing the tag. My independent proposal before reading the diff was the same approach: find the last The code is 21 lines of logic plus 44 lines of tests. Three files changed, all necessary. If I had to maintain this in six months, I'd thank the author for making it trivially readable and well-tested. Everything checks out:
Approving. ✅ 中文说明总结这个 PR 修复了 #5324 中的一个真实 bug —— 对使用带端口的私有 registry 的用户(企业环境中很常见),sandbox 容器创建会失败。旧解析器比 issue 描述的更严重:它生成的容器名包含斜杠(Docker 不允许),并且丢失了标签。 我在看 diff 之前的独立方案与 PR 的做法完全一致:取最后一个 逻辑 21 行,测试 44 行。改了三个文件,全部必要。如果六个月后要维护这段代码,我会感谢作者让它如此易读且有测试覆盖。 全部检查通过,批准合并。✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
✅ Maintainer verification — real sandbox-launch test under tmuxVerified this PR locally by driving the actual Environment
1. Static checks (reproducing the PR test plan)
2. Why this matters (the bug)Sandbox container names are derived from the image reference. The old 3. Real sandbox-launch A/B in tmuxRan the real The name appears both on stderr and in the actual 4. Function-level A/B (real
|
| image | OLD | NEW (PR) |
|---|---|---|
localhost:5000/team/qwen-code:dev |
localhost-5000/team/qwen-code ❌ |
qwen-code-dev ✅ |
localhost:5000/team/qwen-code (no tag) |
localhost-5000/team/qwen-code ❌ |
qwen-code ✅ |
registry.example.com:8443/a/b/c:1.2.3 |
registry.example.com-8443/a/b/c ❌ |
c-1.2.3 ✅ |
…/qwen-code-sandbox:dev@sha256:… |
qwen-code-sandbox-dev@sha256 ❌ (digest leaks) |
qwen-code-sandbox-dev ✅ |
qwen-code-sandbox@sha256:… |
qwen-code-sandbox@sha256-… ❌ |
qwen-code-sandbox ✅ |
ghcr.io/qwenlm/qwen-code:0.18.3 |
qwen-code-0.18.3 |
qwen-code-0.18.3 (unchanged) |
qwen-code:0.18.3 |
qwen-code-0.18.3 |
qwen-code-0.18.3 (unchanged) |
Every previously-correct case is unchanged (no regression); only the broken port/digest cases change — and they change from Docker-invalid names to valid ones.
Observations (non-blocking)
- Real-world severity: because the old names contained
/(and@for digests), Docker would reject them — so this is a hard "sandbox won't start" bug for port-qualified private-registry images, not just an ugly name. The fix restores functionality. - Good refactor: the logic is now a small, pure, exported function with direct unit coverage; only the parser changed, and
start_sandboxis otherwise untouched. - Cross-platform: pure string logic, platform-agnostic. PR is CI-green on macOS/Windows/Linux; I additionally exercised the real Linux sandbox flow end-to-end.
🇨🇳 中文版(点击展开)
✅ 维护者验证 —— 在 tmux 中进行真实的 sandbox 启动测试
我在本地通过驱动真实的 qwen sandbox 启动(用一个私有 registry 镜像,并观察真实派生出来的容器名;因本机没有 Docker,用了一个 fake docker 垫片)外加单测进行了验证。结论:这是一个把「硬失败」修成「可用」的正确修复,建议合并。 在 PR 的 CI 覆盖之上,我补了一次 🐧 Linux 运行时验证。
环境
- 在 PR head(
a8382207,基于当前上游main)上独立git worktree,全新npm ci(绝不软链node_modules),真实 esbuild 打包。 - Linux · Node
v22.22.2· 打包后的qwen0.18.3。 - 改动范围干净:恰好 3 个文件,+67 / −9,单个提交(3-dot diff 里看到的 env-var/注释改动是无关的上游提交,不属于本 PR)。
1. 静态检查(复现 PR 测试计划)
| 检查项 | 命令 | 结果 |
|---|---|---|
| 单元测试 | vitest run src/utils/sandbox.test.ts |
✅ 5/5(basename+tag、带/不带 tag 的端口、剥离 digest、tag+digest) |
| Lint | eslint sandbox.ts sandboxImageName.ts sandbox.test.ts |
✅ 干净 |
| 格式 | prettier --check(3 个文件) |
✅ 干净 |
| 空白字符 | git diff --check |
✅ 干净 |
| 类型检查 | npm run typecheck -w packages/cli |
✅ 退出码 0,0 错误 |
2. 为什么重要(这个 bug)
Sandbox 容器名是从镜像引用派生的。旧的 image.split(':') 把第一个 : 当作 tag 分隔符——但像 localhost:5000 这样的私有 registry 主机名里的 : 是端口。于是派生出的名字保留了端口和带 / 的 registry 路径。而 Docker 容器名只允许 [a-zA-Z0-9][a-zA-Z0-9_.-]+,所以旧名字会被 Docker 拒绝——也就是说,对任何带端口的 registry 镜像,sandbox 根本起不来(不只是名字难看的问题)。
3. tmux 中真实 sandbox 启动的 A/B
用 QWEN_SANDBOX=docker、QWEN_SANDBOX_IMAGE=localhost:5000/team/qwen-code:dev 跑真实的 qwen -p hi。PATH 上的 fake docker 满足镜像/网络的前置检查,使真实的 start_sandbox 走到容器命名那一步并产出真实的 docker run --name …——不会真正运行容器。
旧版(PR 前): ContainerName (regular): localhost-5000/team/qwen-code-0 <- 保留端口且含 '/' => Docker 会拒绝
docker run … --name localhost-5000/team/qwen-code-0
新版(本 PR): ContainerName (regular): qwen-code-dev-0 <- 干净、合法的容器名
docker run … --name qwen-code-dev-0
该名字同时出现在 stderr 和真实的 docker run --name 参数里。(注:真实的 localhost:5000/team/qwen-code:dev 在旧解析器下得到 localhost-5000/team/qwen-code——比描述里写的 localhost-5000 还要糟,因为它还保留了 /team/qwen-code 路径。)
4. 函数级 A/B(真实的 parseSandboxImageName vs 旧的 split(':'))
| 镜像 | 旧版 | 新版(PR) |
|---|---|---|
localhost:5000/team/qwen-code:dev |
localhost-5000/team/qwen-code ❌ |
qwen-code-dev ✅ |
localhost:5000/team/qwen-code(无 tag) |
localhost-5000/team/qwen-code ❌ |
qwen-code ✅ |
registry.example.com:8443/a/b/c:1.2.3 |
registry.example.com-8443/a/b/c ❌ |
c-1.2.3 ✅ |
…/qwen-code-sandbox:dev@sha256:… |
qwen-code-sandbox-dev@sha256 ❌(digest 泄漏) |
qwen-code-sandbox-dev ✅ |
qwen-code-sandbox@sha256:… |
qwen-code-sandbox@sha256-… ❌ |
qwen-code-sandbox ✅ |
ghcr.io/qwenlm/qwen-code:0.18.3 |
qwen-code-0.18.3 |
qwen-code-0.18.3(不变) |
qwen-code:0.18.3 |
qwen-code-0.18.3 |
qwen-code-0.18.3(不变) |
所有原本正确的情形都保持不变(无回归);只有原本损坏的端口/digest 情形发生变化——而且是从 Docker 非法名变成合法名。
观察项(非阻断)
- 真实严重性: 因为旧名字含
/(digest 情形还含@),Docker 会直接拒绝——所以对带端口的私有 registry 镜像,这是「sandbox 起不来」的硬 bug,不只是名字难看。修复后恢复了可用性。 - 不错的重构: 逻辑现在是一个小巧、纯粹、可导出的函数,有直接单测覆盖;只改了解析器,
start_sandbox其余部分未动。 - 跨平台: 纯字符串逻辑,与平台无关。PR 在 macOS/Windows/Linux 的 CI 全绿;我额外端到端验证了真实的 Linux sandbox 流程。
What this PR does
Moves the sandbox image-name parsing out of
sandbox.tsinto a small, dedicated and unit-tested helper (parseSandboxImageNameinsandboxImageName.ts), and fixes how the image reference is split into a Docker container-name prefix::that appears after the final/, so a registry port such aslocalhost:5000is correctly treated as part of the registry path rather than as an image tag.ghcr.io/qwenlm/qwen-code:0.18.3→qwen-code-0.18.3).@sha256:...) are stripped before parsing, so they no longer leak into the generated name, while any preceding tag is preserved (e.g.…/qwen-code-sandbox:dev@sha256:…→qwen-code-sandbox-dev).The previous implementation used
image.split(':'), which broke on registry ports. Behavior is otherwise unchanged.Why it's needed
Fixes #5324. Sandbox container names are derived from the configured image name, but the old parser split on the first
:. For common private-registry images that include a port, e.g.localhost:5000/team/qwen-code:dev, this collapsed the name down tolocalhost-5000, losing the real image basename and tag. After this change the registry port stays part of the registry path and the container name is correctly based on the image basename (qwen-code-dev).Reviewer Test Plan
How to verify
Repro: configure a sandbox image with a registry port, e.g.
localhost:5000/team/qwen-code:dev.localhost-5000.qwen-code-dev.The new unit tests cover this repro plus untagged ports, digest stripping, and tag+digest combinations. Verification commands from the PR:
npx vitest run src/utils/sandbox.test.tsfrompackages/clinpm run typecheck --workspace=packages/clinpm run build --workspace=packages/clinpm run lintnpx prettier --experimental-cli --check packages/cli/src/utils/sandbox.ts packages/cli/src/utils/sandboxImageName.ts packages/cli/src/utils/sandbox.test.tsgit diff --checkEvidence (Before & After)
N/A — non-visible logic fix (internal container-name derivation); covered by the unit tests above.
Tested on
✅ tested ·⚠️ not tested · N/A
Environment (optional)
Unit tests only (npm workspaces).
Risk & Scope
packages/cli. Behavior change is limited to how the image reference is parsed into a name prefix.Linked Issues
Fixes #5324
中文说明
这个 PR 做了什么
将 sandbox 镜像名解析逻辑从
sandbox.ts抽取到一个独立、带单元测试的小helper(sandboxImageName.ts中的parseSandboxImageName),并修复了将镜像引用切分为 Docker 容器名前缀的方式:/之后 出现的最后一个:,因此像localhost:5000这样的 registry 端口会被正确当作 registry 路径的一部分,而不是被误认为镜像标签。ghcr.io/qwenlm/qwen-code:0.18.3→qwen-code-0.18.3)。@sha256:...)在解析前会被剥离,因此不再混入生成的名字中,同时其前面的标签会被保留(例如…/qwen-code-sandbox:dev@sha256:…→qwen-code-sandbox-dev)。旧实现使用
image.split(':'),在遇到 registry 端口时会出错。除此之外行为保持不变。为什么需要它
修复 #5324。Sandbox 容器名是从配置的镜像名派生的,但旧解析器在第一个
:处切分。对于包含端口的常见私有 registry 镜像,例如localhost:5000/team/qwen-code:dev,这会把名字压缩成localhost-5000,丢失了真正的镜像 basename 和标签。改动之后,registry 端口会保留为 registry 路径的一部分,容器名正确地基于镜像 basename(qwen-code-dev)。Reviewer 测试计划
如何验证
复现:配置一个带 registry 端口的 sandbox 镜像,例如
localhost:5000/team/qwen-code:dev。localhost-5000。qwen-code-dev。新增的单元测试覆盖了该复现,以及不带标签的端口、digest 剥离、标签 + digest 组合等场景。PR 中的验证命令:
packages/cli下npx vitest run src/utils/sandbox.test.tsnpm run typecheck --workspace=packages/clinpm run build --workspace=packages/clinpm run lintnpx prettier --experimental-cli --check packages/cli/src/utils/sandbox.ts packages/cli/src/utils/sandboxImageName.ts packages/cli/src/utils/sandbox.test.tsgit diff --check证据(前后对比)
N/A —— 非可见的逻辑修复(内部容器名派生);已由上述单元测试覆盖。
测试平台
✅ 已测试 ·⚠️ 未测试 · N/A
环境(可选)
仅单元测试(npm workspaces)。
风险与范围
packages/cli的 sandbox 容器名派生。行为变化仅限于如何将镜像引用解析为名字前缀。关联 Issue
修复 #5324
AI Assistance Disclosure
I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.