Skip to content

fix(maven): 依赖树改为读取插件输出文件,日志与数据分离(macOS + Windows,#890) - #908

Open
1lck wants to merge 3 commits into
previewfrom
fix/890-maven-dependency-tree
Open

1lck wants to merge 3 commits into
previewfrom
fix/890-maven-dependency-tree

Conversation

@1lck

@1lck 1lck commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

概要

修复 #890:Maven 工具窗口的依赖树以前从 Maven 进程的整段控制台输出里解析,依赖树和普通日志共用 500,000 字符的上限。大型项目(6,000 个节点约 700 KB 文本)会报 Maven dependency output exceeded the supported limit.,依赖打不开。macOS 和 Windows 都有这个问题(MavenService.swift 与 maven.store.ts 各有一份 500,000 上限,Core 里还有第三份),本 PR 两端一起修。

现在依赖树由 maven-dependency-plugin 通过 -DoutputFile 写进平台临时文件,Rust Core 从文件读取;控制台输出只当日志。这与 VS Code Maven 扩展的做法一致(后台运行用户的 mvn、把树写到临时文件再解析)。数据模型按 IntelliJ IDEA MavenArtifactNode 的字段语义补全。

Closes #890

改动点

Rust Core

  • maven.dependencyPlan 新增必填的绝对路径 outputFile,并显式传入所有影响格式的属性:-DoutputType=text -Dtokens=standard -DoutputFile=… -DoutputEncoding=UTF-8 -DappendOutput=false,避免用户或项目默认值改变解析格式。
  • reactor 根查询加 -N:否则 reactor 中每个工程都会覆盖同一个文件,根查询会显示最后一个模块的树(以前按控制台解析时,根查询会把所有模块的树混在一起,这次一并修正)。
  • maven.dependencies 的请求从 { modulePath, output } 改为 { modulePath, outputFile },逻辑移到新模块 project/maven_dependency_tree.rs:
    • 严格校验:第一行必须是模块坐标,其后每行都必须是合法节点,且只带固定版本插件会写出的标注。不认识的内容(例如 POM 把输出类型改成 dot、换了树形符号、混入日志、未知标注、非法 UTF-8)返回 parse_failed,不再静默跳过、把残缺的树当成功显示。
    • 上限来自树本身:10,000 节点、64 层、单行 4 KiB,文件总字节数由这些上限推出,与日志量无关。
    • Maven 正常退出但文件不存在时返回 process_failed。
    • 兼容 Windows 上 JVM 写出的 CRLF。
  • 每个节点新增 premanagedVersion、premanagedScope、originalScope、ignoredScope,对应 verbose 输出里原本被丢掉的 version managed from / scope managed from / scope updated from / scope not updated to。
  • 共享 fixture 从 dependency-tree-v1.json 换成 dependency-tree-v2.json(输入改为树文件内容,覆盖全部 6 种标注);shared/contracts/rust-core-api.md 同步更新。

Windows

  • Tauri 宿主(src-tauri/src/maven.rs)新增 maven_create_dependency_output / maven_remove_dependency_output:每个依赖会话一个文件,放在应用缓存目录 maven-dependency-trees/ 下;会话 ID 严格校验,防止路径逃逸;应用是单实例,启动时清空该目录。
  • maven.store.ts 不再累积依赖输出(删除 dependencyOutput 状态与 appendDependencyOutput);所有结束路径(读完、取消、超时、失败、配置失效、被新请求取代)统一走 releaseDependencySession,只在 Core 读完之后才删文件;删除失败只记日志,不影响已读出的结果。
  • use-maven-process-events.ts 不再把依赖会话的控制台输出当数据。
  • 依赖行副标题抽成 utils/maven-dependency-subtitle.ts(便于测试),显示新标注;中英文文案。

macOS

  • LitheCoreContracts 新增端口 MavenDependencyOutputStoring,由 Platform/MacOS/Persistence/MacMavenDependencyOutputStore.swift 实现。macOS 允许多个 Lithe(例如 preview 与正式版)同时运行,所以每个进程使用 PID 命名的子目录,创建时只清理已退出进程的目录。
  • MavenService 的生命周期与 Windows 相同:删除 500,000 字符缓冲,结束路径统一走 releaseDependencyOutputFile,读取开始后由读取任务负责删文件。未注入存储时明确报错,而不是悄悄失败。
  • MavenDependencyLocalization 显示新标注;中文使用全角括号与分号,与原有写法一致。删除已不会产生的 “Maven dependency output exceeded the supported limit.” 文案。

文档与记录

  • 新增 Agent Note .agents/notes/implemented/architecture/2026-09-26-maven-dependency-tree-output-file.md,记录决策和被否方案:JDT LS/m2e(会使用 m2e 内嵌的 Maven 而不是用户的 Maven/mvnd,且需新建 Java bundle 链路)、插件 JSON 输出(3.8.1/3.9.0 不含冲突与依赖管理信息)、IntelliJ 式 Maven Server(成本过高)、调大上限(Windows Maven:依赖树不应依赖整段控制台输出,复用上游结构化依赖模型 #890 明确不接受)。
  • 更新 shared/platform-feature-matrix.json 中 java-maven-profiles-dependencies 的证据路径与验证步骤,并重新生成 docs/development/platform-parity-matrix.{md,csv}。

验证

本地(Linux)已通过:

  • cargo test -p lithe-core:678 个测试通过,其中新增/改写 7 个依赖树测试(计划参数与 -N、outputFile 校验、LF/CRLF fixture、6,000 与 10,000 节点、字节/节点/行长/深度上限、格式漂移、文件缺失)。
  • bun test --preload ./src/test-utils/vite-assets.ts src/features/maven:109 个测试通过(与 CI 调用方式一致);tsc --noEmit 通过。
  • Swift 6.3.3(Docker,Swift 6 语言模式)编译 LitheCoreContracts 与 LitheExecutionModule,运行 LitheExecutionModuleTests:57 个测试全部通过,包括新增的“失败/被取代时删除文件”和“缺少存储时明确失败”。
  • MacMavenDependencyOutputStore 的 PID 目录清理测试在 Linux 替身环境中通过。Tauri 宿主新增的纯函数在独立 crate 中通过测试。
  • verify-agent-notes.sh、verify-rust-core-comments.sh、verify-shared-contracts.sh、verify-platform-feature-matrix.sh、verify-windows-boundaries.sh、测试稳定性静态检查(macOS/Windows)通过。

需要 CI 或真机验证:

  • macOS App target(RustCoreBridge、MacMavenDependencyOutputStore、界面文案)需要 macOS CI。经过真实 Core 规划并读取树文件的端到端测试 MavenDependencyTreeBridgeTests 只在链接了 Core 的 lane 中运行(已加入 scripts/verify-rust-core.sh 的 filter;普通 Swift 单测不链接 Core,会自动跳过)。
  • Tauri 宿主在 Linux 上缺少 GTK 无法编译,Windows 构建与 cargo test 需要 Windows CI。
  • 尚未在真实的大型 Maven 项目上做端到端验证,建议在 Windows 和 macOS 上各打开一个依赖超过 6,000 个节点的项目,确认依赖树完整加载,冲突、重复、依赖管理标注正确显示,并且取消或重试后缓存目录中没有残留文件。
  • verify-service-boundaries.sh 中的 “AppModel extension 超过 600 行” 检查在当前 preview 上本来就失败(本 PR 未修改 AppModel),其余边界检查通过。

兼容性

  • maven.dependencyPlan 新增必填字段,maven.dependencies 请求字段 output 替换为 outputFile。macOS 与 Windows 两个已合入的调用方都已同步更新。
  • 尚未合入的 Linux PR feat(linux): 新增 Linux GPUI 原生产品适配 #886 在 linux/src/workbench/maven.rs 里以 { modulePath, output: "" } 调用 maven.dependencies(占位实现),合入前需要改成新的请求形状;旧请求现在会得到明确的 invalid_request,而不是空树。

🤖 Generated with Claude Code

The Maven tool window parsed the dependency tree out of the whole console
output, so the tree shared one 500,000-character budget with Maven's log.
Large projects (6,000 nodes is about 700 KB) failed with "Maven dependency
output exceeded the supported limit." on both macOS and Windows.

The dependency plugin now writes the verbose text tree to a platform-owned
scratch file through -DoutputFile, and Core reads that file. Console output
is log only.

Core
- maven.dependencyPlan takes a required absolute outputFile and pins every
  format-affecting property (text, standard tokens, UTF-8, overwrite). The
  reactor root runs with -N so only one project writes the file.
- maven.dependencies takes { modulePath, outputFile } and validates the file
  strictly: the root line, then only well-formed nodes with annotations the
  pinned plugin writes. Unknown content fails with parse_failed instead of
  producing a partial tree; a missing file is process_failed.
- Bounds come from the tree (10,000 nodes, 64 levels, 4 KiB lines, a byte
  limit derived from those), not from log volume.
- Nodes now carry premanagedVersion, premanagedScope, originalScope, and
  ignoredScope from the verbose annotations that were previously dropped.
- The shared fixture moves to dependency-tree-v2.json.

Windows
- The Tauri host creates and removes one scratch file per dependency session
  in the app cache directory and clears the directory at startup.
- The Maven store no longer buffers dependency output; every path that ends
  a session (read, cancel, timeout, failure, invalidation, supersession)
  removes the file, and only after Core has read it.

macOS
- New MavenDependencyOutputStoring port, implemented by
  MacMavenDependencyOutputStore with a per-process directory; directories of
  exited processes are removed so concurrent Lithe builds keep their files.
- MavenService follows the same lifecycle as Windows.

Both dependency trees show the new management and scope annotations. The
decision and rejected alternatives (JDT LS/m2e, JSON output, a Maven Server,
raising the limit) are recorded in an Agent Note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Sep 26, 2026
Removing the dependency output buffer left four closing braces of set()
calls indented one level too deep in maven.store.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ordinary Swift unit lane does not link Rust Core, so the end-to-end
dependency-tree test failed there with "Rust Core is unavailable". Move it
into MavenDependencyTreeBridgeTests, gate it on RustCoreBridge.isAvailable as
the other bridge tests do, and add the suite to the Core-linked lane in
verify-rust-core.sh so CI still runs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant