Skip to content

fix(macos): discover Maven and JDK from version managers (#1514) - #1559

Closed
mvanhorn wants to merge 2 commits into
1lck:previewfrom
mvanhorn:fix/1514-macos-managed-runtime-discovery
Closed

mvanhorn wants to merge 2 commits into
1lck:previewfrom
mvanhorn:fix/1514-macos-managed-runtime-discovery

Conversation

@mvanhorn

Copy link
Copy Markdown

Problem

Issue #1514: when Lithe starts from Finder or the Dock it gets launchd's minimal environment, so PATH and JAVA_HOME from the login shell are missing. Maven installed with mise (or asdf, SDKMAN) is not found, and Java selection can quietly fall back to a Homebrew JDK. Launching from a terminal works because the shell environment is inherited.

MacRuntimeDiscovery only looked at the process environment, fixed Homebrew/system paths and SDKMAN's Java candidates. Maven had no managed-install lookup at all.

Fix

All changes are in macos/Sources/Lithe/Platform/MacOS/Runtime/MacRuntimeDiscovery.swift.

  • enrichedEnvironment runs the user's login shell once per discovery ($SHELL -ilc, 1.5 s timeout) and merges its environment into the inherited one. Inherited non-empty values keep priority, and inherited PATH entries stay ahead of the shell's, so an explicit JAVA_HOME, MAVEN_HOME or M2_HOME still wins.
  • parseLoginShellEnvironment reads only the env -0 records printed after a marker, because startup files can write to stderr and MacProcessRunner merges it into the output. The capture is cut at 256 KB and 1024 variables, and anything over either limit is dropped as a whole, so a truncated PATH never replaces the inherited one. A timeout or non-zero exit falls back to the inherited environment.
  • managedInstallationRoots adds mise (MISE_DATA_DIR), asdf (ASDF_DATA_DIR) and SDKMAN (SDKMAN_DIR) install roots for both Java and Maven. Each root is read up to 256 entries. Maven executables are de-duplicated by their resolved path, so a current symlink and the version directory are probed once.
  • javaHomeOnPath no longer stops at /usr/bin/java. A GUI launch puts /usr/bin ahead of the shell's PATH, so it keeps scanning for a real JDK, skips launchers that are not inside a JDK home, and uses /usr/libexec/java_home's default only when the system launcher is the only Java on PATH.

The Java runtime discovery capability record (shared/platform-feature-matrix/features/java-runtime-discovery.json) lists MacRuntimeLocator.swift and adds the Finder/Dock versus terminal checks to its manual verification steps. verificationStatus stays as it was.

Testing

  • ./.agents/skills/write-stable-tests/scripts/test-stability-macos.sh -- --filter MacRuntimeDiscoveryTests: all 10 tests pass. New cases cover managed mise/asdf Java and Maven roots with a current symlink and inherited-priority merging, the /usr/bin/java and non-JDK shim cases on PATH, the marker and size limits of the shell capture, and the bounded directory read. Fixtures are temp directories removed in defer, and a failed cleanup is recorded with Issue.record.
  • verify-test-stability.sh, verify-platform-feature-matrix.sh, verify-platform-feature-matrix-change.sh upstream/preview, verify-agent-notes.sh, verify-service-boundaries.sh, verify-runtime-bundle-immutability.sh and generate-i18n.mjs --check pass.

I have not done the manual Finder/Dock launch check with real mise/asdf/SDKMAN installs listed in the capability record.

Fixes #1514

Finder and Dock launches inherit launchd's minimal environment, so Maven
and JDKs installed by mise, asdf or SDKMAN were missing and a Homebrew
JDK could be picked instead.

Runtime discovery now merges a bounded login-shell environment into the
inherited one (inherited values and PATH order still win) and scans the
mise, asdf and SDKMAN install roots for both Java and Maven. The shell
capture is read after a marker, capped by size and variable count, and
falls back to the inherited environment on timeout or failure. Each
managed root is read up to a fixed number of entries.

When /usr/bin/java or a non-JDK shim comes first on PATH, discovery
keeps scanning for a real JDK and only uses java_home's default when
the system launcher is the sole Java on PATH.
@lithe-code-review

lithe-code-review Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⛔ 有阻塞
范围: preview ← fix/1514-macos-managed-runtime-discovery · head: e5a6c49

Findings

  1. [P1] 将登录 shell 探测移出主线程上的运行时选择路径
    macos/Sources/Lithe/Platform/MacOS/Runtime/MacRuntimeDiscovery.swift:77-78,同类入口见 :26-27

    • 实际影响:原本只检查文件的选择方法现在每次都会同步启动登录 shell,并等待退出或 1.5 秒超时。运行配置界面在 SwiftUI body 中调用这些方法,因此即使后台发现已经完成、版本缓存已经命中,界面重绘仍会阻塞主线程;多个自动运行时标签会累积等待。
    • 触发条件:未显式配置项目 JDK、进程环境没有 JAVA_HOME,或项目没有 Maven Wrapper、需要选择系统 Maven;用户 shell 初始化较慢时尤其明显。
    • 复现步骤:使用初始化耗时约一秒的登录 shell,从 Finder 启动并打开自动选择 JDK/Maven 的运行配置页。静态调用链为 EffectiveRuntimeLabel.body → choice() → RuntimeSettingsFeatureModel → @MainActor ProjectRuntimeService.chooseJavaHome/chooseMavenExecutable → MacRuntimeLocator → enrichedEnvironment → MacProcessRunner.run。恢复运行配置的 loadRunConfigurationToolchainCandidates 也在进入后台版本探测前同步调用这些选择方法。
    • 来源:本次引入。base 的这两个入口没有启动 shell;本次新增同步调用绕过了既有后台探测边界。
  2. [P2] 将补全后的环境继续传递给 Maven 版本探测
    macos/Sources/Lithe/Platform/MacOS/Runtime/MacRuntimeDiscovery.swift:18-19

    • 实际影响:补全环境只用于找到 mvn,随后 .compactMap(probeMaven) 丢失这份环境。probeMaven → cachedCommandOutput 创建的 ProcessRequest 没有设置 environment,最终继承 GUI 进程的最小环境。依赖 shell 中 JAVA_HOME 或 PATH 的新发现 Maven 因而无法运行版本命令,并被从 mavenRuntimes 中静默移除。
    • 触发条件:从 Finder/Dock 启动,JDK 仅由版本管理器提供,登录 shell 设置了有效 JAVA_HOME,系统 /usr/bin/java 无可用的注册 JVM。
    • 复现步骤:让登录 shell 导出 managed JDK 的 JAVA_HOME 并将 Maven 加入 PATH,GUI 进程自身不含这些变量,再触发运行时发现。代码能够找到 Maven 启动器,但其 -version 子进程看不到刚恢复的环境。也可用一个要求对应 JAVA_HOME、否则非零退出的临时 Maven 启动器构造确定性回归测试。
    • 来源:本次引入的新发现链路未接完整。本次将仅存在于 shell 环境中的 Maven 接入旧探测器,却没有同步传递它运行所需的环境;新增测试只检查文件发现,没有覆盖这一子进程路径。

Scope Check

  • 关联需求:已读取提供的 Issue [Bug] macOS 下自动检测不到 mise/SDKMAN 等版本管理器安装的 Maven 和 JDK,必须手动指定路径 #1514;新增代码覆盖登录 shell 环境采集、mise/asdf/SDKMAN 安装目录、Maven 符号链接去重和系统 Java launcher 后续扫描。
  • 已完成范围:三个文件均已检查,无未列出的语义文件;功能矩阵继续保持 verificationStatus: pending,与尚未完成手工验证相符。
  • 未完成范围:上述主线程回归需要修复;managed Maven 从环境恢复到版本探测的完整链路存在缺口。当前激活 JDK 的最终选择也缺少服务层及真实 GUI 启动验证,不能仅据目录发现测试认定 Issue 已完整解决。
  • 复用边界:沿用现有原生进程适配器、探测缓存和 Core JDK 选择策略,未新增语言或构建领域模型的并行实现。
  • Findings 均指向本次新增行为及其直接调用链,属于本次职责范围;未将未修改的历史问题列为 Finding。

Suggestions

  1. 对应 Finding 1:在后台采集环境及 PATH 默认运行时,供同步选择入口读取已完成的快照,并定义刷新、失败重试与并发合并行为;首次采集也不能在主线程等待。补充覆盖真实选择入口的主线程可响应性测试,并同步相关工具链探测生命周期 Note。
  2. 对应 Finding 2:把本轮补全环境传入 Maven 探测请求,确保缓存身份能区分影响探测结果的环境。补充“GUI 最小环境、仅 managed JDK、无系统注册 JVM”的发现与版本探测测试。

Summary

目录发现和解析边界有所改善,但新增 shell 执行进入了界面同步选择链路,构成需要修复的交互回归。环境恢复目前也只接到候选路径枚举,尚未覆盖 Maven 子进程。P1 应在合并前修复;P2 为非阻塞修改建议。

Verification

  • 实际检查:指定 base...head 的全部三个文件及 base 实现;直接追踪 MacRuntimeLocator、ProjectRuntimeService、RuntimeSettingsFeatureModel、EffectiveRuntimeLabel、运行配置编辑界面、进程请求/启动适配器、探测缓存、Core Java 选择策略及相关测试和 Agent Notes。
  • 已提供结果:PR 正文报告 MacRuntimeDiscoveryTests 10 项通过,以及测试稳定性、功能矩阵、Agent Notes、服务边界、bundle 不变性和 i18n 生成检查通过;这些是作者提供的结果,本次未独立复验。提供的上下文中,head 尚无 Check Run 结果。
  • 本次未运行:任何仓库代码、测试、构建或应用;未访问网络、未修改文件、未发布评论。
  • 验证缺口:尚无真实 mise/asdf/SDKMAN 安装下 Finder/Dock 与终端启动的对照结果;新增测试未覆盖实际 shell 请求、主线程调用链及补全环境向 Maven 子进程的传递。

@ghfind-review ghfind-review Bot added the review: no-score ghfind author score missing (not zero) label Oct 29, 2026
…Maven probes

Runtime selection (systemMavenExecutable, javaHomeOnPath) is called on the
main actor from the run configuration UI, so it must not start a login
shell or java_home. Background discovery now stores the login shell
environment and /usr/bin/java's default JDK in MacShellEnvironmentCache;
selection only merges what that cache holds and otherwise uses the
inherited environment as before.

Discovery also passes the enriched environment to the Maven -version
probe, so a managed Maven that needs the shell's JAVA_HOME or PATH is not
dropped. The probe cache key includes the variables that change the probed
runtime.
@mvanhorn

Copy link
Copy Markdown
Author

两个问题都已在 a9b0c1a 中修复。

P1:systemMavenExecutable 和 javaHomeOnPath 不再同步启动登录 shell 或 java_home。后台发现时会把登录 shell 环境和 /usr/bin/java 的默认 JDK 存入新的 MacShellEnvironmentCache,主线程上的选择路径只读取这份缓存;发现尚未运行时直接使用继承的环境,行为与 base 一致。

P2:discover 现在把补全后的环境传给 probeMaven,cachedCommandOutput 会把它设置到 ProcessRequest.environment。MacRuntimeLocator.mavenRuntime(at:) 也使用缓存的补全环境。探测缓存的 key 加入了 JAVA_HOME、PATH、MAVEN_HOME 等会影响结果的变量。

新增测试:一个只有在 JAVA_HOME 正确时才输出版本的临时 Maven 启动器(不带该环境时探测返回 nil,带上后返回 3.9.9)、缓存 key 区分环境,以及 MacShellEnvironmentCache 的读写边界。

本地运行:test-stability-macos.sh -- --filter "MacRuntimeDiscoveryTests|MacRuntimeProbeCacheTests" 20 项全部通过;verify-test-stability.sh、verify-platform-feature-matrix.sh、verify-platform-feature-matrix-change.sh upstream/preview、verify-agent-notes.sh、verify-service-boundaries.sh、verify-runtime-bundle-immutability.sh 和 generate-i18n.mjs --check 均通过。ProjectRuntimeLoadingTests 的 "complete" 用例在本次修改前的 e5a6c49 上就已经失败,与这次改动无关。真实 Finder/Dock 启动下的 mise/asdf/SDKMAN 手工验证仍未做。

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

两个问题都已在 a9b0c1a 中修复。

P1:systemMavenExecutable 和 javaHomeOnPath 不再同步启动登录 shell 或 java_home。后台发现时会把登录 shell 环境和 /usr/bin/java 的默认 JDK 存入新的 MacShellEnvironmentCache,主线程上的选择路径只读取这份缓存;发现尚未运行时直接使用继承的环境,行为与 base 一致。

P2:discover 现在把补全后的环境传给 probeMaven,cachedCommandOutput 会把它设置到 ProcessRequest.environment。MacRuntimeLocator.mavenRuntime(at:) 也使用缓存的补全环境。探测缓存的 key 加入了 JAVA_HOME、PATH、MAVEN_HOME 等会影响结果的变量。

新增测试:一个只有在 JAVA_HOME 正确时才输出版本的临时 Maven 启动器(不带该环境时探测返回 nil,带上后返回 3.9.9)、缓存 key 区分环境,以及 MacShellEnvironmentCache 的读写边界。

本地运行:test-stability-macos.sh -- --filter "MacRuntimeDiscoveryTests|MacRuntimeProbeCacheTests" 20 项全部通过;verify-test-stability.sh、verify-platform-feature-matrix.sh、verify-platform-feature-matrix-change.sh upstream/preview、verify-agent-notes.sh、verify-service-boundaries.sh、verify-runtime-bundle-immutability.sh 和 generate-i18n.mjs --check 均通过。ProjectRuntimeLoadingTests 的 "complete" 用例在本次修改前的 e5a6c49 上就已经失败,与这次改动无关。真实 Finder/Dock 启动下的 mise/asdf/SDKMAN 手工验证仍未做。

这个还是觉得作为插件契约去进行约束,暂时不考虑进行硬编码的方案,但是非常感觉你提交的 PR

@xiaoyumuxi xiaoyumuxi closed this Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: no-score ghfind author score missing (not zero)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants