Skip to content

feat(backend): Maven 模型和依赖改由常驻 Java 后端提供并持久快照(1lck/Lithe-IDEA#1351) - #3

Merged
xiaoyumuxi merged 1 commit into
mainfrom
fix/1351-maven-backend-snapshot
Oct 20, 2026
Merged

xiaoyumuxi merged 1 commit into
mainfrom
fix/1351-maven-backend-snapshot

Conversation

@xiaoyumuxi

Copy link
Copy Markdown
Contributor

背景

1lck/Lithe-IDEA#1351:Maven 面板每次打开项目、每展开一个模块都要重新转圈。原因是 model / dependencies 声明为 source: "command",每次加载、每个模块都单独起一次 mvn … pre-clean,宿主不持久化结果(单依赖模块也要约 3 秒)。

本 PR 把读取项目改成常驻 Java 构建后端 + 持久快照:指纹不变时重启 Lithe 也不再运行 Maven。

依赖

  • feat(plugins): 官方 Java 插件后端 SDK(#1351) 1lck/Lithe-IDEA#1379(官方 Java 插件 SDK):后端基于它编写。SDK 尚未发布,sdk.lock.json 的 sha256 暂为 null,CI 从 #1379 的分支 feat/1351-java-sdk 构建 SDK。#1379 合并并打 sdk-java-v0.1.0 后,需要在本 PR 填入正式 Release 的 URL 和 SHA-256,并删除 CI 里的 “Build the unreleased plugin SDK” 一步,之后才能合并/发布(package.mjs --expect-tag 会拒绝没有锁定 SDK 的发布)。
  • PR B(主仓库,进行中):宿主给 provider 请求加可选的 refresh: true(Reload / 同步文件变化时)和 tool: { executable }(宿主按 invocation 解析出的 mvn)。
    • 没有 PR B 时后端也能工作:自己按 mvnw → MAVEN_HOME → PATH 查找 Maven;但用户在设置里覆盖的 Maven 不会生效,Reload 也不会绕过快照(POM/配置变化仍会通过指纹失效)。

改动

backend/(新增,Java 8)

  • 基于 SDK 的 Backend / Processes / StateStore,shade 成 lib/lithe-maven-backend.jar。SDK 未发布到 Maven 仓库,构建时用 system scope 编译,再用 shade 的 extraJars 合并进去,不往 ~/.m2 安装任何东西。
  • 一次 Maven,全量导出:同时传 -Dlithe.modelOutput 和 -Dlithe.dependenciesOutput,不传 dependenciesProject,一次拿到模型和每个模块的依赖树。项目依赖按 project.path → 模型里的模块坐标 → 工作区依赖树节点取子树,扩展本身不改。
  • 参数:tool.executable 优先,缺失时回退 mvnw / MAVEN_HOME / PATH;optionArguments 原样传给 Maven;environment 叠加在后端环境上;没有 JAVA_HOME 时设为启动后端的 Java。
  • 快照 maven-snapshot.json(snapshotVersion: 1,StateStore 原子写,按根目录分条,最多 32 个根)。损坏、版本未知或缺字段都按未命中处理。
  • 指纹:
    • 根目录下全部 pom.xml 的内容哈希。有界遍历:深度 24、5 万项,跳过 target/、.git/、node_modules/;
    • .mvn/maven.config、extensions.xml、jvm.config、wrapper/maven-wrapper.properties;
    • 用户 settings.xml / toolchains.xml,Maven 安装目录的 conf/settings.xml,以及选项或 maven.config 里 -s/-gs/-t/-gt 指定的文件;
    • 选项参数、宿主环境、JAVA_HOME/MAVEN_OPTS 等变量、mvn 路径与修改时间、java.version、扩展 JAR 哈希、后端版本。
  • 读取路径:
    • 命中且无 refresh → 直接返回;
    • 否则按“根目录 + 指纹”合并为同一次运行(single-flight)。Maven 运行不属于任何单个请求,全部等待者都取消才结束 Maven 及其进程树;
    • 运行前后指纹不一致 → 照常返回结果,但不写快照;
    • Maven 失败时返回 maven.loadFailed,附上 [ERROR] 行,旧快照保留。

plugin.json

  • model / dependencies 改为 source: "provider"。
  • 新增 provider:launch { kind: runtime, tool: java, minimumVersion: "8", entry: lib/lithe-maven-backend.jar },arguments: ["--state", "${workspaceStateDir}"],保留 executesProjectCode / mayUseNetwork: true。
  • requires 加 buildProviders: 1、serverProcess: 1,权限加 languageServer.launch,写法与 SDK 示例 sample-build 一致。任务、测试、诊断、同步、选项都不变。

打包与脚本

  • sdk.lock.json:记录 SDK 版本、下载 URL 和 SHA-256,以及发布前用来构建的源码分支。
  • scripts/package.mjs:
    • 检查三处版本一致(plugin.json、extension/pom.xml、backend/pom.xml);
    • 按锁文件下载 SDK 到 .cache/sdk/ 并校验 SHA-256;
    • LITHE_SDK_JAR 可指向本地 JAR,锁文件有哈希时也必须一致;
    • --expect-tag 拒绝本地 JAR 和未锁定的 SDK;
    • 打包结果新增 lib/lithe-maven-backend.jar。
  • scripts/backend-client.mjs(新增):按宿主方式(LSP 分帧、initialize 协商 litheBuild 1)驱动后端。所有等待都有上限;后端退出或发出坏帧时,立即让等待中的请求失败。

测试

  • scripts/test-backend.mjs(新增,不需要 Maven):用 test/fixtures/fake-maven-backend.mjs 记录每次运行,覆盖:
    • 首次加载只跑一次 Maven,模型、单模块依赖和工作区依赖都来自这一次;
    • 重启后端后命中快照,不跑 Maven;
    • 改 POM 后失效;
    • 改选项 / .mvn/maven.config 后失效;
    • refresh 绕过快照;
    • 并发请求合并为一次运行;
    • 运行期间改 POM:返回结果但不写快照;
    • 取消一个等待者时 Maven 继续;全部取消后 Maven 及其子进程被清理(Java 8 上只能结束 Maven 本身,测试会注明并自行清理子进程);
    • Maven 失败、或成功但没有写出可读文档 → 报错且保留旧快照;
    • 损坏的快照或未知版本的快照按未命中处理;
    • 未传 tool.executable 时回退到项目的 mvnw;
    • 关闭后没有残留的 Maven 进程。
  • scripts/test.mjs:模型和依赖改为经后端用真实 Maven 读取,断言不变,另外检查快照已写入;LITHE_CAPTURE_DIR 照常输出这三份文档。
  • scripts/test-process.mjs:原来的 “fake-maven 输出缺失/损坏” 回归改为 “provider 起不来/回坏帧” 时 lane 失败、报告齐全、scratch 已清理;Maven 输出缺失/损坏的场景移到 test-backend.mjs(maven.invalidOutput)。test/fixtures/fake-maven.mjs 改为 fake-java.mjs。
  • CI / Release:setup-java 同时装 8 和 17。CI 在 17 和 8 上各跑一次 test-backend.mjs,Maven 3.6.3 那一路的 test.mjs 用 Java 8 运行后端;Release 在 Java 8 上跑后端测试。verify-scripts.mjs 的源码副本加入 backend/ 和 sdk.lock.json。

验证

本地(macOS,Maven 3.10,SDK 由 #1379 分支 b7b8be1 构建,SHA-256 53af40a3…cb0b04a):

  • LITHE_SDK_JAR=… node scripts/package.mjs ✓
  • node scripts/test-backend.mjs:Java 17、Java 21 均 15/15 ✓。本机没有 JDK 8,Java 8 由 CI 验证。
  • 变异检查:让后端无条件写快照后,“运行期间改 POM 不写快照”一项按预期失败。
  • node scripts/test.mjs(真实 Maven)16/16 ✓
  • node scripts/test-process.mjs 8/8 ✓

尚未做:

  • Lithe Store 规则校验只在 CI 里跑(本地没有执行 Lithe-store 的脚本);
  • 在开发版 Lithe 里装包手工验收(打开 java-multi-repo-demo、计时、重开后确认无 mvn 进程)。

- 新增 backend/:Java 8 构建后端,基于 Lithe Java 插件 SDK,shade 进
  lib/lithe-maven-backend.jar。一次 Maven 运行同时导出模型和全部模块依赖,
  结果按输入指纹存入 maven-snapshot.json(StateStore 原子写);指纹不变时
  重启后也不再运行 Maven,refresh 绕过快照;并发请求共用一次运行,全部取消
  才结束 Maven;运行期间输入变化则不写快照。
- plugin.json:model / dependencies 改为 source: provider,provider 以
  runtime java 8 启动后端并传 --state ${workspaceStateDir},补
  languageServer.launch 权限;任务、测试、诊断不变。
- sdk.lock.json + package.mjs:按锁文件下载并校验 SDK;SDK 发布前用
  LITHE_SDK_JAR 指向本地构建,发布流程拒绝未锁定的 SDK。
- 测试:test-backend.mjs 用假 Maven 计数覆盖快照命中/失效/刷新/合并/取消;
  test.mjs 经后端用真实 Maven 读取;CI 在 Java 8 和 17 上跑后端测试。
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