Skip to content

chore: dev → main(三产品彻底拆分落地 + Redis 二进制键等 8 项修复) - #491

Merged
feigeCode merged 125 commits into
mainfrom
dev
Oct 31, 2026
Merged

feigeCode merged 125 commits into
mainfrom
dev

Conversation

@feigeCode

@feigeCode feigeCode commented Oct 31, 2026 •

Copy link
Copy Markdown
Owner

内容

dev 领先 main 124 个提交:三产品彻底拆分落地(#489 及其后续收尾)+ 8 项修复(含 #485)。逐项如下。

1. 三产品彻底拆分:navop / navop-shell / navop-data

契约层与启动

  • dc3c7e98d feat(domain_api):新增三产品共用领域契约层(Product/Domain/Hook/DomainRegistry、TabOpener 打开模式、d572a14ed 端口转发运行时改为可选供 Data 实现)。
  • e4181254e + 6ef0f60a6 + 4a9c41b57:HomePage 实现 TabHost/HomeContext,main 实现 Product 并经 app_kit::bootstrap 初始化(领域 init 支持错误传播),run_with(product, version) 统一入口;main 抽出 navop_app 库,全量产品移到 apps/navop。
  • ca747569b + 09d8bd1c2:app_kit/core 按产品身份配置 keychain 与数据目录(便携/开发/缓存),修 ssh_form 移动后的源码断言路径。

main 收敛为纯应用外壳(39 个 refactor(product-split))

  • 领域 init、主页入口、连接打开策略、连接编辑表单、数据库/终端设置页、侧栏终端配色、文件打开/标签复制/临时连接/键位、端口转发运行时、远程桌面窗口、导入编辑器、工作台终端、会话日志、KnownHosts 全部改由 DomainRegistry 与 ShellExtras/FileOpenHandler/ActiveResourceProvider 契约驱动,main/src 已无 use 领域crate。
  • a21ea7cbf/128741fb1:main 引入 database/shell 领域 feature 并下沉视觉与驱动资产,独立产品 default-features = false 只开自己的领域。
  • b0dc6b379 + 85d9b02ca:裁剪 main 的领域依赖,加独立产品依赖树隔离契约测试(database/shell 不许互相夹带对方依赖)。
  • 579da7841:托盘图标按产品加角标、退出提示带产品名。

两个独立产品

  • fa0a70f9d + 05195c80a + 731d97209:新增 apps/navop-shell、apps/navop-data 可执行包,分别装配 Shell 主页与 Database/Redis/MongoDB 主页;7f4c661ce 补齐新建连接、基础设置与 Shell 连接类型覆盖。

领域实现下沉

  • shell_domain:SSH 终端、SFTP 双栏、RDP/VNC、端口转发、本地终端、KnownHosts、会话日志、远程桌面窗口全部经领域注册表打开;4bc1361c8 从 terminal_view 抽出独立的 ssh_form(SSH 表单与主机密钥 UI)。
  • data_domain:Database/Redis/MongoDB 单连接与工作区打开路径、Redis 工作区标签与连接排序、6b1efc09d 驱动检查与安装提示、e147f19b1 Mongo 原生驱动工厂。
  • 运行时工具包拆分:d3930398c runtime_core(跨域共用)、7a29ea95a runtime_database、e7d3d48f6 runtime_redis/runtime_sftp;e12c7945b/312aa8e8b/62d38c9ce 把数据库与远程桌面能力改为可选特性,切断 terminal_view 到 db 的编译链。

Public MCP 三产品化

  • 0a5b4e472 三产品 Public MCP 宿主 + 独立发现身份;0c958ddcf 独立产品注册 connection 管理工具集;0c56a0b98 工具集开关接入设置并加逐项 UI;cbb72d954 Mongo/Redis live provider 下沉并复用通用审批 UI;2260fcc53 数据领域工具集改由领域契约提供(main 不再依赖 data_domain/mongodb_view);66bab9beb 独立产品按领域包写入默认工具集。

打包与 CI

  • 5974b0d3a macOS 应用包按产品生成独立身份;04fdc8c95 Linux 包资源与独立打包脚本;ed7614d22 Windows 安装器身份参数化;355be3cf7 增加 Shell/Data 的 macOS 与 Linux 构建矩阵;7e371c60a packaging 支持按平台筛选与分支构建;8480e16b7 校验 navop 产品包与主包版本一致。

2. e982a939e fix(redis):键名含非法 UTF-8 时列表加载、读取与删除不再失败(#485)

根因:SCAN/KEYS 回复用 Vec<String> 解析,redis-rs 对 bulk string 做严格 UTF-8 校验;遇到 JDK 原生序列化等二进制键直接报 Command error: ... Cannot convert from UTF-8 - TypeError,整页列表加载失败(#485 日志的 key scan failed)。

修法:新增 crates/redis-runtime/src/key_codec.rs 做二进制安全 key 编解码——合法 UTF-8 且不以 U+0001 开头的键原样透传(decode 零拷贝 Cow::Borrowed),其余编码成 U+0001 + 每字节 \xNN 的可读转义形态。SCAN/KEYS 回复改按 Vec<Vec<u8>> 解析后逐键 encode;所有真正下发命令的入口(TYPE/TTL/STRLEN 族/GET/GETRANGE/SSCAN/HSCAN/LRANGE/ZRANGE/XRANGE/EXPIRE/PERSIST/RENAME/HSET/HDEL/LPUSH/RPUSH/LSET/SADD/SREM/ZADD/ZREM/DEL 等)在命令边界 decode 回真实字节,普通文本键恒等、零行为变化;KeyInfo.name 保持与树显示一致。

验证:cargo test -p redis-runtime 32 passed(新增 codec 单元测试 + SCAN 回复二进制键保持转义并反解 + 本机真实 Redis 端到端:写二进制键 → SCAN 列出 → 点开读到值 → DEL 成功 → 再扫不到);cargo test -p redis_view --lib 80 passed;cargo clippy -p redis-runtime -p redis_view -- -D warnings 在这两个 crate 零告警(其余 error 均为仓库既有基线,未触碰)。

3. 其余 7 项修复

  • 205d97f55 fix(app_kit):独立产品启动时初始化应用路径(否则便携/开发目录解析为空)。
  • 0209bdb91 fix(domain_api):惰性打开已存在的连接标签,避免重复建连。
  • f7faecea2 fix(domain_api):HomeContext::notify 暴露 Window/App,独立产品才能推送通知。
  • 6bcc309b2 + af3d3dc51:无系统凭据库的平台上不再读 KeyringServices 字段,并补 Shell 领域的 Windows 凭据管理器依赖。
  • ce374ccc6 + 83adeb140:Windows 单实例失败提示改用 &str 入参,并同步源码契约断言。
  • cbfcf7f61 fix(product-split):修领域 feature 组合下 Linux/Windows 的 CI 编译失败。
  • 测试:9a3215839 目标包默认 feature 与 update 模块路径对齐;85d9b02ca 独立产品依赖树隔离契约。
  • e98d18122 fix(db_view):复制为 UPDATE / DELETE 时选区不含主键不再输出空字符串。

4. 与 main 的同步(b3ef361a3、806c30780)

main 上由贡献者直接合入的 #482 / #483 / #484 已并入 dev:

  • f0773f1df 标签栏空间不足时非活动标签可收缩(crates/core/src/tab_container.rs,无冲突)。
  • a5b135a90 快捷键复制改为走标签菜单的复制路径。三产品拆分里 HomePage::duplicate_active_tab 原来调 ShellExtras::duplicate_terminal_tab(会新建连接、丢当前工作目录),现改为直接调用 TabContainer::duplicate_active_tab,由标签内容自己克隆;复制出的终端标签在 main/src/home_tab/lifecycle.rs 订阅 TabContainerEvent::TabDuplicated 后补回 OpenSftp / SaveAsConnection 响应。因此不再需要 ShellExtras::duplicate_terminal_tab 这一契约,已删除(含 domain_api 默认实现与 shell 领域实现)。
  • ae4824ced Esc 只在编辑器上下文生效(markdown-editor / notes / 快速打开弹窗测试,无冲突)。
  • 159149d07 terminal 集成测试各自隔离临时目录(无冲突)。
  • 顺带修 main/src/home/home_tabs.rs 中 notify_unsupported 属性与文档注释之间的空行(clippy 告警)。

说明

  • 37 条 docs(design) 是 docs/design/ 下的实施进度记录(三产品依赖图、MCP 隔离、打包矩阵、启动冒烟等),无代码变更。
  • 已知限制(沿用 refactor(product-split): 三产品共用全量主页 + 领域 feature 拆分与 UI 下沉 #489 及其后续):public_mcp_runtime 仍留在 main/src/public_mcp_runtime 按 feature 装配工具集;main 里仍有少量 #[cfg(feature)] 领域分支(Mongo 驱动工厂、扩展连接终端启动、远程桌面协议映射等);CI 依赖树 guard job 仍以契约测试 + script/check-product-deps.sh 等价替代。
  • 普通 dev → main 合并,不含 CHANGELOG 与版本 bump;发版按 .github/RELEASE.md 走同一个 dev → main release PR(版本号与 CHANGELOG 一起改)。

验证

  • 合并冲突解决后本地实跑:cargo test -p main --lib 592 passed、cargo test -p terminal -p markdown-editor -p notes -p shell_domain -p domain_api -p one-core --lib 全绿(557 / 87 / 963 / 42 / 530 / 11)、cargo check -p navop-shell -p navop-data --all-targets 通过、clippy 在改动文件零告警。
  • 本 PR 触发完整三平台 CI 矩阵(linux / macos / windows)。
  • 本地结果见 refactor(product-split): 三产品共用全量主页 + 领域 feature 拆分与 UI 下沉 #489 body 与各提交正文:cargo build -p navop -p navop-shell -p navop-data 通过、cargo test -p main --lib 591 passed、领域包 141/18/42/11 passed、两个独立产品临时 HOME 启动 RUNNING_OK、cargo tree 校验独立产品不含对方领域依赖。

navop and others added 29 commits October 29, 2026 22:24
- main/Cargo.toml 删除 6 个已无引用的 optional 领域依赖:
  db、ssh、sftp_view、known_hosts_view、port_forwarding、port_forwarding_view
  (builtin-duckdb 改由 db_view 转发)
- 修 shell-only 构建下的 Redis 资源池 helper cfg、外观页误加的 cfg
- 新增 script/check-product-deps.sh(本地依赖树守卫)
- 新增两个 manifest 契约测试:独立产品只开自己领域 feature、
  database/shell feature 互不包含对方领域依赖
- 文档补 §27(main 已无领域 use、cfg 161→57、验证与剩余项)
- main 默认 feature 追加 database/shell 后,同步 script/test-release-packaging.mjs 断言
- update/install.rs 已下沉到 crates/app_kit,修正契约脚本读取路径
- crates/core/key_storage.rs 的 keyring_services 只在 test/macOS/Windows
  下引用钥匙串服务名常量,补上同样的 cfg,修 Linux 构建的 E0425
- app_kit 单实例 Windows 模块在迁移后丢了可见性:forward_attempt /
  create_pipe_instance / serve_forever 改 pub(super),补 ForwardAttempt import
Linux CI 用 -D warnings,keyring_services/keyring_service_for 在非
test/macOS/Windows 平台被 cfg 掉后 KeyringServices 的 installed/development
两个字段变成 never read,补 allow(dead_code)。
remote_desktop_window/mstsc_credentials_windows.rs 用 windows crate 的
Win32_Security_Credentials,迁移时漏了 Cargo 依赖,Windows CI 报
cannot find module or crate `windows`。按 main 原配置补到 shell_domain
的 windows target 依赖。
RDP probe 用 --locked 构建,lock 未记录 shell_domain → windows 0.62 这条边
会导致 cannot update the lock file because --locked was passed。
report_single_instance_failure 收 &str,app_kit 的 user_message() 返回
String,Windows CI 报 E0308 expected &str, found String。
把人工 cargo tree | grep 的验收固化成集成测试:navop-shell 的依赖树不能
出现 db_view/redis_view/mongodb_view,navop-data 不能出现
terminal_view/sftp_view/remote_desktop_view/known_hosts_view/
port_forwarding_view。feature 写漏时编译不会报错,只有依赖树能拦住。
refactor(product-split): 三产品共用全量主页 + 领域 feature 拆分与 UI 下沉
- 删除 ProductKind/ProductProfile/run()/run_product():身份与领域装配只存在于
  apps/* 的 Product 实现,main 只保留 run_with(product, version)。
- 删除 main/src/main.rs:main 包只提供 navop_app 库,入口统一到 apps/*。
- main 的 shell feature 移除 shell_domain 依赖(main 不再构造 ShellDomain);
  data_domain 暂留,公共 MCP 的 Redis/Mongo 工具集实现还在 data_domain::mcp。
- 同步更新 domain feature 契约测试与文档 §26。
- data_domain 新增 builtin-mongodb feature,DataDomain::init 在
  mongodb_view::init 之后按同一 cfg 安装原生驱动工厂。
- main 删除 init_native_data_driver_factories 及其调用,改用弱依赖
  data_domain?/builtin-mongodb 转发 feature,保持「领域初始化 → 公共 MCP」
  的既有顺序。
- 契约测试改为断言 domains < public_mcp,工厂实现由 data_domain 单测覆盖。
SCAN 的返回值原先按 `(u64, Vec<String>)` 解析,redis-rs 会对每个 bulk string
做严格 UTF-8 校验;只要库里存在一个 JDK 原生序列化对象之类的二进制键,整批
回复就变成 `Cannot convert from UTF-8`,列表加载直接报错(issue #485)。

改为在 redis-runtime 引入二进制安全的键编解码:
- 新增 `key_codec`:`encode` 把服务端键字节转成可显示、可还原的字符串
  (可打印 UTF-8 键原样,其余为 `U+0001` 前缀 + 逐字节 `\xNN` 转义);
  `decode` 在真正下发命令前还原原始字节,普通键恒等借用、零分配。
- SCAN / KEYS 回复改按 `Vec<Vec<u8>>` 解析,逐键 `encode` 后进入树与列表。
- 所有下发键的命令(TYPE/TTL/GET/SET/DEL/EXPIRE/PERSIST/RENAME/HSCAN/SSCAN/
  LRANGE/ZRANGE/XRANGE 等)在 trait 边界 `decode`,因此列表显示形态可以原样
  回传到读取、改 TTL、重命名、删除等入口。
- `key_value_view` 等 UI 侧无需改动,`KeyInfo.name` 保持显示形态与树一致。

测试:新增 key_codec 单元测试,以及 SCAN 回复解析回归测试与二进制键
「扫描 → 读取 → 删除」端到端测试(本机 127.0.0.1:6379 上实测通过)。
根因:单格选区只带一列,`TableMetadata::select_columns` 只保留「落在选区里的主键」,
`primary_key_indices` 因此变成空,`copy_sql_format::format_update` / `format_delete`
立即返回空字符串;而 `copy_with_format` 仍把空串写入剪贴板并提示「已复制到剪贴板」,
用户看到的现象就是「复制为 → UPDATE 复制不出来」。

修法:UPDATE/DELETE 复制前用新增的 `TableMetadata::indices_with_primary_keys` 把缺失的
主键列追加进复制上下文(SET 仍只用选区列,只有 WHERE 使用主键);表本身没有主键,或
生成结果为空(主键值为空 / 选区只有主键列)时改为明确 warning,不再写空剪贴板并谎报成功。
其他复制格式(TSV/CSV/JSON/Markdown/INSERT/IN)行为不变。

新增 `TableData.copy_sql_requires_primary_key`、`TableData.copy_sql_unavailable` 三语文案,
以及 `sql_mutations_include_primary_keys_missing_from_the_selection` 回归测试。
冲突解决:main/src/home/home_tabs.rs 保留三产品拆分布局,快捷键复制改走
TabContainer::duplicate_active_tab(等价 #483:与标签菜单共用复制路径,终端保留当前工作目录,
并对任何可复制标签生效),复制出的终端标签在 main/src/home_tab/lifecycle.rs 补回
OpenSftp / SaveAsConnection 订阅;随之删除不再被使用的 ShellExtras::duplicate_terminal_tab 契约。

origin/main 上的 #482 / #483 / #484 其余改动(标签收缩、Esc 只在编辑器上下文生效、
terminal 测试隔离)均无冲突合入。

顺带修 main/src/home/home_tabs.rs 里 notify_unsupported 的属性与文档注释空行(clippy 告警)。
服务端把测试版清单单独落在 updates/beta/latest.json,稳定清单
updates/latest.json 完全不动,老客户端行为不变:

- release.yml 从 tag 形态推导通道,beta 发布给 GitHub Release 加
  --prerelease,并把 channel 显式派发给 upload-r2.yml;
- upload-r2.yml 新增 channel 输入(留空按 tag 推导 + 白名单校验),
  清单按通道落位;prune 额外保留 beta 清单指向的安装包,避免 404;
- script/beta-release.sh 一条命令完成 beta 版本号 + changelog +
  commit + tag + push,--dry-run 可预演;
- script/test-release-packaging.mjs 补 beta 清单与 prune 场景断言。

客户端新增「更新通道」设置(稳定版/测试版,默认跟随构建版本):

- AppSettings.update_channel,预发布构建默认 Beta;
- feed 由通道派生,stable 永远只接受正式版本号,beta 才接受预发布;
- beta 通道不回落 GitHub Releases(该端点语义排除 prerelease),
  beta 清单 404 视为「还没有 beta 版本」而非检查失败;
- 「跳过此版本」按通道分别记录(beta:/stable: 前缀),互不影响;
- 关于页更新分组加通道下拉(切 Beta 先确认)、预发布构建版本号加
  Beta 标签,切回稳定通道时给出说明条;更新弹窗标题与徽标区分测试版。

文档:docs/design/beta-channel.md 记录分层、推导规则与客户端消费方式;
.github/RELEASE.md 补 beta 发版流程。
@feigeCode
feigeCode merged commit 945d998 into main Oct 31, 2026
8 checks passed
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