Skip to content

fix(updater): 打通华为内网镜像回退链路 / wire up the intranet PyPI mirror fallback - #72

Open
370025263 wants to merge 1 commit into
mainfrom
feat/robust-intranet-update
Open

fix(updater): 打通华为内网镜像回退链路 / wire up the intranet PyPI mirror fallback#72
370025263 wants to merge 1 commit into
mainfrom
feat/robust-intranet-update

Conversation

@370025263

Copy link
Copy Markdown
Collaborator

背景 / Background

0.6.5 加了 team server wheel 回退(#70),但排查华为内网部署场景时发现三个没打通的地方,导致对纯内网(连不上公网 PyPI)用户来说,回退实际上不work:

  1. AutoUpdater.pypi_url 从没在任何调用点被设成公网默认值以外的东西——daemon.py 构造 AutoUpdater 时压根不传这个参数。公网 PyPI 查询失败时,代码直接跳到 server wheel 回退,配了内网镜像也用不上。
  2. _install_wheel 装 server 下发的 wheel 完全不带 -i——wheel 本体虽然来自 server,但它的依赖解析会悄悄吃 pip 默认索引(通常是不可达的公网 pypi.org)。纯内网机器上这一步大概率直接卡死/失败,等于回退没真正回退成。
  3. 手动 xskill update(CLI 命令)是完全独立的一条逻辑,没有任何回退——公网 PyPI JSON 查询失败就直接报错退出,和后台每小时的自动检查行为不一致。skill 安装向导的"日常维护"一节推荐用户用这条命令,但它在内网环境下必挂。

改动 / Changes

  • ClientState 新增可选字段 pypi_url,通过 xskill connect --pypi-url <url> 设置,落盘到 ~/.xskill/team_client.json,重连时若不重新传参会沿用上次的值。
  • AutoUpdater 现在按 公网 PyPI → 已配置的内网镜像(用 pip index versions,兼容大多数只有 PEP 503 /simple/ 索引、没有 pypi.org legacy JSON API 的内网镜像)→ team server wheel 三级顺序回退。
  • _install_wheel 现在总是带 -i self.pypi_url,wheel 的依赖解析走一个明确可达的索引,不会悄悄吃 pip 默认配置。
  • cmd_updatexskill update)重写为复用 AutoUpdater.run_once(),加载已持久化的 server/mirror 配置,不再是一条更窄、没有回退的独立逻辑。

测试 / Testing

  • 新增/调整单测覆盖:镜像查询顺序、_install_wheel-i 参数、ClientState 新字段的持久化与向后兼容(老 json 没有 pypi_url 字段时默认 None)、cmd_update 委托给 AutoUpdater 且正确复用持久化配置。
  • make test(不含 docker_e2e / live):1144 passed
  • 跳过了 tests/test_e2e_xskill_serve_auto.py::test_canary_flip_promote_and_install_new_version——这个测试会真的 spawn claude -p 子进程,在当前沙箱环境(已经跑在一个 Claude Code 会话里,嵌套调用 claude CLI)里会一直挂起,和本次改动无关(该改动完全没碰灰度/canary 相关代码)。建议在真实 CI 里补跑一遍确认。
  • 没有跑 make e2e(docker),建议合并前在有网络访问的环境里跑一遍。

兼容性 / Compatibility

  • 不带 --pypi-urlxskill connect 行为完全不变(pypi_url 默认 None,回退链路退化为"公网 PyPI → server wheel",和现在一样)。
  • 老版本 team_client.json(没有 pypi_url 字段)能正常读取,字段默认 None
  • xskill-internal-install-guide 那边的 install.ps1 已经改成在 connect 时传 --pypi-url,并且做了老版本 xskill(argparse 报 unrecognized argument,退出码 2)时自动降级重试,所以这个 PR 和已部署的旧版本 server/client 之间不会因为参数不认识而报错——但只有等这个版本真正发布、部署到 server 上之后,--pypi-url 才会真正生效。

不要求立即合并,先看一下改动方向对不对。

🤖 Generated with Claude Code

server wheel 回退虽然在 0.6.5 加了,但三处没打通华为纯内网场景:
1) `AutoUpdater.pypi_url` 从没被真正配置过(daemon.py 构造时不传),
   公网 PyPI 不可达时永远直接跳到 server wheel,内网镜像形同虚设;
2) `_install_wheel` 装 server 下的 wheel 时完全不带 `-i`,wheel 的依赖
   解析会悄悄吃 pip 默认索引(通常是不可达的公网 pypi.org),纯内网机器
   上大概率装不上;
3) 手动 `xskill update` 是完全独立的一条逻辑,没有任何回退——公网 PyPI
   查询失败直接报错退出,和后台自动检查的行为完全不一致。

fix(updater): the intranet PyPI mirror was configured but never actually
wired into the fallback chain

The server-wheel fallback landed in 0.6.5, but three gaps kept it from
working for a pure-intranet (no public internet) deployment:
1) `AutoUpdater.pypi_url` was never actually set to anything but the
   public PyPI default — daemon.py never passed it through — so when
   public PyPI is unreachable the updater always jumped straight to the
   server-wheel path without ever trying a configured mirror.
2) `_install_wheel` installed the server wheel with no `-i` flag at all,
   so resolving the wheel's dependencies silently fell back to pip's
   default index (usually unreachable public pypi.org) instead of the
   mirror.
3) The manual `xskill update` CLI command was a completely separate,
   simpler code path with zero fallback — it just errored out if the
   public PyPI JSON query failed, unlike the background updater.

Changes:
- `ClientState` gains an optional `pypi_url` field, settable via
  `xskill connect --pypi-url <url>` and persisted across reconnects.
- `AutoUpdater` now tries (1) public PyPI, (2) the configured mirror via
  `pip index versions` (works against any PEP 503 index, unlike the
  legacy JSON API most mirrors don't implement), (3) server wheel — in
  that order — before giving up. `_install_wheel` now always passes
  `-i self.pypi_url` so wheel dependencies resolve through a reachable
  index.
- `cmd_update` now delegates to `AutoUpdater.run_once()` (loading the
  persisted server/mirror config) instead of duplicating a narrower,
  fallback-less version of the same logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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