Files
LanMountainDesktop/.cursor/plans/launcher_单项目解耦_302f1ec6.plan.md
2026-05-28 15:14:37 +08:00

20 KiB
Raw Blame History

name, overview, todos, isProject
name overview todos isProject
Launcher 单项目解耦 在保持单一 LanMountainDesktop.Launcher 项目、单一 exe、零部署风险的前提下按职责域增量重构目录分层、RunAsync→Pipeline+Phase、UpdateEngine→策略类、App→纯 Avalonia+LauncherOrchestrator执行过程中由 Agent 自主 Git 提交,每域可编译可测。
id content status
phase-a-diagnostics Phase AStartup 诊断 + HostStartupMonitor 独立类 + AOT 启动检测竞态修复 + 测试 completed
id content status
phase-b-directory Phase B1职责域目录迁移Deployment/Update/Startup/Oobe/Plugins/Infrastructure零逻辑变更提交 completed
id content status
phase-b-pipeline Phase B2RunAsync→LaunchPipeline+ILaunchPhase引入 LauncherOrchestrator删除 LauncherFlowCoordinator提交 completed
id content status
phase-b-app-slim Phase B3App.axaml.cs 精简为纯 Avalonia 初始化 + 委托 LauncherOrchestrator提交 completed
id content status
phase-c-di Phase CLauncherServiceRegistration + 轻量 MS DI统一 CLI/GUI 装配,提交 completed
id content status
phase-d-update-split Phase DUpdateEngineService→门面+策略类Verifier/Activator/Rollback 等),提交 completed
id content status
phase-e-guardrails Phase ELauncherArchitectureTests + 文档 + AOT 回归,提交 completed
false

Launcher 单项目内部解耦改造计划(执行版)

0. 硬性约束

约束 说明
单项目 [LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj](LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj),不新建 Launcher.* 独立程序集
单 exe 仍只发布 LanMountainDesktop.Launcher.exeAOT 单文件)
零部署风险 不改变安装包目录结构、不引入新进程、不改变 Public IPC / Coordinator IPC 拓扑与契约
增量重构 一个职责域一域推进,每步 dotnet build + 相关 dotnet test 通过后再进下一步
单进程性能 模块间仅 in-process 接口调用,不为解耦新增 IPC
未来可拆 各域暴露 I* 接口,将来若需多进程可直接复用契约
Git 自主提交 Agent 在每个职责域完成且验证通过后 自动 commit,无需用户手动提交(见 §8

外部共享库 [LanMountainDesktop.PluginPackaging](LanMountainDesktop.PluginPackaging/) 保留Host + Launcher CLI 共用),不属于 Launcher 拆分。


1. 验收标准(必须全部满足)

1.1 零部署风险

  • Inno Setup / CI 产物仍只有:LanMountainDesktop.Launcher.exe + app-{version}/ + .launcher/
  • Host 调用 Launcher 的 CLI 参数、launch-sourceapply-update 路径不变
  • Public IPC routeslanmountain.launcher.startup-progressloading-state)与 Coordinator pipe 不变
  • VeloPack / 更新 apply 状态机(.current/.partial/.destroy)行为不变

1.2 增量可验证

  • 每个 Phase 结束:编译绿 + 该域新增/既有测试绿
  • 允许「纯移动文件」的 PR 单独提交,行为 diff 为零

1.3 测试友好

  • Startup/Update/Deployment/ 内类型 无 Avalonia 依赖,可独立单元测试
  • 每个 ILaunchPhase、每个 Update 策略类各有对应测试类
  • 保留并扩展现有 [LauncherStartupTimeoutPolicyTests](LanMountainDesktop.Tests/LauncherStartupTimeoutPolicyTests.cs)[LauncherMultiInstancePolicyTests](LanMountainDesktop.Tests/LauncherMultiInstancePolicyTests.cs)

1.4 启动性能

  • Pipeline 阶段为同步/异步方法调用链,不引入额外进程或网络
  • DI 容器仅在进程入口构建一次Stage/Phase 实例可复用 Singleton

1.5 代码结构目标

对象 当前(实测) 目标
LauncherFlowCoordinator 全 partial ~1880 行859+568+279+…) 删除;逻辑迁入 Pipeline + Phases
RunAsync() 等价逻辑 跨 partial ~800+ 行 while/阶段混杂 ≤80 行 编排入口,细节在各 Phase
UpdateEngineService ~1622 行 门面 ≤200 行 + 6 个策略类各 ≤300 行
App.axaml.cs ~258 行(已部分瘦身) ≤120 行:纯 Avalonia + 一行委托 LauncherOrchestrator
LauncherOrchestrator 不存在(逻辑在 Coordinator + CompositionRoot 546 行) ≤250 行GUI 入口编排
LauncherCompositionRoot ~546 行 ≤150 行:仅 DI 构建 + 入口分发

2. 目标架构

2.1 核心类型关系

flowchart TB
    Program --> EntryRouter
    App --> LauncherOrchestrator
    EntryRouter --> LauncherOrchestrator
    LauncherOrchestrator --> LaunchPipeline
    LaunchPipeline --> Phase1[CleanupPhase]
    LaunchPipeline --> Phase2[OobeGatePhase]
    LaunchPipeline --> Phase3[ApplyUpdatePhase]
    LaunchPipeline --> Phase4[LaunchHostPhase]
    LaunchPipeline --> Phase5[MonitorStartupPhase]
    Phase3 --> IUpdateEngine
    Phase4 --> IDeploymentLocator
    Phase5 --> IHostStartupMonitor
    LauncherCompositionRoot --> ServiceProvider
    ServiceProvider --> LaunchPipeline

命名约定:

  • **LauncherOrchestrator**GUI 生命周期内的唯一编排入口(取代 LauncherFlowCoordinator 对外角色)
  • **LaunchPipeline**:按序执行 ILaunchPhase 列表
  • **ILaunchPhase**:原 ILaunchPipelineStage;每个 Phase 对应原 RunAsync 中一个职责段

2.2 职责域目录(单项目内)

LanMountainDesktop.Launcher/
├── Program.cs                         # CLI / GUI 路由
├── App.axaml.cs                       # 纯 Avalonia≤120 行)
├── Shell/
│   ├── LauncherOrchestrator.cs        # GUI 编排入口
│   ├── LauncherCompositionRoot.cs     # DI + Entry 分发
│   ├── LaunchPipeline.cs
│   ├── Phases/                        # ILaunchPhase 实现
│   │   ├── CleanupDeploymentsPhase.cs
│   │   ├── OobeGatePhase.cs
│   │   ├── ApplyPendingUpdatePhase.cs
│   │   ├── LaunchHostPhase.cs
│   │   └── MonitorStartupPhase.cs
│   └── EntryHandlers/                 # apply-update / air-app-broker / attach
├── Deployment/
├── Update/
│   ├── IUpdateEngine.cs
│   ├── UpdateEngineFacade.cs          # 原 UpdateEngineService 门面
│   └── Strategies/
│       ├── PendingUpdateDetector.cs
│       ├── UpdatePackageVerifier.cs
│       ├── DeploymentActivator.cs
│       ├── UpdateSnapshotStore.cs
│       ├── RollbackStrategy.cs
│       └── IncomingArtifactsCleaner.cs
├── Startup/
├── Oobe/
├── Ipc/
├── AirApp/
├── Plugins/
├── Infrastructure/
├── Models/
└── Views/

2.3 模块依赖规则

  • Deployment/Update/Startup/禁止 using Avalonia
  • Views/禁止 引用具体 UpdateEngineFacade / DeploymentLocator(仅接口或 Orchestrator
  • 跨域:仅通过 I 接口*Orchestrator/Pipeline 负责装配

2.4 与 Host 边界(不变)

能力 Owner
OOBE / Splash / 多实例 / 启动检测 Launcher Startup/ + Shell/
更新 apply / rollback Launcher Update/
插件市场 / pending Host + PluginPackaging
更新 download Host → spawn Launcher apply

3. 三大核心拆分(用户指定)

3.1 拆分 LauncherFlowCoordinatorRunAsync → Pipeline + Phase

现状: 逻辑分散在 4 个 partial等效一个 1800+ 行 God ClassRunAsync 内含清理、OOBE、更新、启动、IPC 监听、超时 while-loop、多实例分支。

目标 API单项目 Shell/ 内):

internal interface ILaunchPhase
{
    string PhaseId { get; }
    /// <returns>null = 继续下一阶段;非 null = 管道终止并返回结果</returns>
    Task<LauncherResult?> ExecuteAsync(LaunchContext context, CancellationToken cancellationToken);
}

internal sealed class LaunchPipeline
{
    public LaunchPipeline(IEnumerable<ILaunchPhase> phases) { ... }
    public Task<LauncherResult> RunAsync(LaunchContext context, CancellationToken ct);
}

Phase 映射(与原 RunAsync 步骤一一对应):

Phase 原 RunAsync 段 产出
CleanupDeploymentsPhase CleanupOldDeployments 无 UI
ExistingHostProbePhase 多实例 / Public IPC 探测 可短路成功
ApplyPendingUpdatePhase _updateEngine.ApplyPendingUpdateAsync 失败仍继续
OobeGatePhase migration + OOBE steps UI via ILauncherUiPresenter
LaunchHostPhase LaunchHostWithIpcAsync Process + plan
MonitorStartupPhase while-loop + IPC + timeout 调用 IHostStartupMonitor

**LauncherOrchestrator 职责:**

  • 接收 SplashWindow、构建 LaunchContext(含 reporter、attempt registry、coordinator server
  • 调用 LaunchPipeline.RunAsync
  • 管理 Splash/Error 窗口生命周期(委托 ILauncherUiPresenter
  • 不含 更新/部署/IPC 细节

删除清单: LauncherFlowCoordinator.cs 及全部 partial 文件。


3.2 拆分 UpdateEngineService → 门面 + 策略类

现状: ~1622 行单文件,混合检测、验签、解压、激活、快照、回滚、清理。

目标结构:

Update/
├── IUpdateEngine.cs                    # 对外契约(未来多进程可原样抽出)
├── UpdateEngineFacade.cs               # 门面编排策略≤200 行
└── Strategies/
    ├── IUpdateStrategy.cs              # 可选:各策略统一接口
    ├── PendingUpdateDetector.cs        # CheckPendingUpdate
    ├── UpdatePackageVerifier.cs        # manifest + RSA 签名
    ├── UpdatePackageExtractor.cs       # 解压 / 增量复用
    ├── DeploymentActivator.cs          # .current / .partial / .destroy
    ├── UpdateSnapshotStore.cs          # snapshots 读写
    ├── RollbackStrategy.cs             # rollback CLI/GUI
    └── IncomingArtifactsCleaner.cs     # CleanupIncomingArtifacts

门面方法映射:

UpdateEngineService 公开方法 委托策略
CheckPendingUpdate() PendingUpdateDetector
ApplyPendingUpdateAsync() Detector → Verifier → Extractor → Activator → Snapshot
RollbackLatest() RollbackStrategy
CleanupIncomingArtifacts() IncomingArtifactsCleaner
DownloadAsync()(若有) 保持或拆 UpdateDownloader

测试: 每个 Strategy 独立 mock IDeploymentLocator / 文件系统,不启 Avalonia。


3.3 精简 App.axaml.cs → 纯 Avalonia + LauncherOrchestrator

现状: ~258 行,仍含 apply-update、air-app-broker、preview、coordinator attach 等分支。

目标结构:

// App.axaml.cs 目标形态(概念)
public override void OnFrameworkInitializationCompleted()
{
    if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
    {
        var context = LauncherRuntimeContext.Current;
        var mode = LauncherEntryModeResolver.Resolve(context);
        _ = LauncherOrchestrator.RunAsync(desktop, context, mode);
    }
    base.OnFrameworkInitializationCompleted();
}

从 App 迁出的逻辑 → Shell/EntryHandlers/

现 App 分支 新 Handler
launch + splash GuiLaunchEntryHandler → Orchestrator
apply-update ApplyUpdateEntryHandler
air-app-broker AirAppBrokerEntryHandler
debug preview PreviewEntryHandler

验收: App.axaml.cs ≤120 行;不含 new UpdateEngineService / new DeploymentLocator / while-loop。


4. 分阶段执行顺序与 Git 提交点

flowchart LR
    A[Phase A Startup] --> B1[Phase B1 目录迁移]
    B1 --> B2[Phase B2 Pipeline+Orchestrator]
    B2 --> B3[Phase B3 App 精简]
    B3 --> C[Phase C DI]
    B1 --> D[Phase D Update 策略拆分]
    C --> E[Phase E 守卫+文档+AOT回归]
    D --> E

Phase AStartup 子系统 + AOT 生产 bug优先

  • 抽出 Startup/HostStartupMonitor.cs(从 partial 独立)
  • 修复 IPC 连接退避、成功判定统一走 StartupSuccessTracker
  • Host 侧 DesktopVisible 上报对齐(仅日志/时序,不改 IPC 契约)
  • 测试 + **git commit**: fix(launcher): extract HostStartupMonitor and harden startup detection

Phase B1目录迁移零逻辑变更

  • 物理移动文件到 Deployment/Update/Startup/ 等,更新 namespace
  • dotnet build + test
  • **git commit**: refactor(launcher): reorganize into responsibility folders

Phase B2Pipeline + Phase + LauncherOrchestrator

  • 实现 ILaunchPhaseLaunchPipelineLauncherOrchestrator
  • 逐 Phase 从 Coordinator 迁移逻辑(可先并行运行对照测试)
  • 删除 LauncherFlowCoordinator*
  • **git commit**: refactor(launcher): replace LauncherFlowCoordinator with LaunchPipeline

Phase B3App.axaml.cs 精简

  • EntryHandlers 提取App 仅 Avalonia + Orchestrator 委托
  • **git commit**: refactor(launcher): slim App.axaml.cs to Avalonia shell only

Phase C轻量 DI

  • LauncherServiceRegistration.cs + Microsoft.Extensions.DependencyInjection
  • Program / CliHost / CompositionRoot 统一 ServiceProvider
  • **git commit**: refactor(launcher): add composition-root DI wiring

Phase DUpdateEngine 策略拆分(可与 B2 并行,依赖 B1

  • 策略类提取 + UpdateEngineFacade
  • 删除原巨型 UpdateEngineService.cs
  • 每策略测试
  • **git commit**: refactor(launcher): split UpdateEngine into strategy classes

Phase E守卫 + 文档 + AOT 回归

  • LauncherArchitectureTests(命名空间依赖规则)
  • 更新 [docs/LAUNCHER.md](docs/LAUNCHER.md)[.trae/specs/launcher-shell-hardening/spec.md](.trae/specs/launcher-shell-hardening/spec.md)
  • AOT publish 本地 smokelaunch / apply-update / 多实例 / 启动检测
  • **git commit**: docs(launcher): document module boundaries and add architecture tests

5. Phase / Service 测试矩阵

组件 测试文件 覆盖点
StartupSuccessTracker StartupSuccessTrackerTests Foreground/Tray/Background policy
HostStartupMonitor HostStartupMonitorTests 超时、IPC 延迟、ShellStatus 轮询
LaunchPipeline LaunchPipelineTests Phase 短路、失败传播
ILaunchPhase *PhaseTests 单阶段 mock
PendingUpdateDetector PendingUpdateDetectorTests 无 pending / corrupt
DeploymentActivator DeploymentActivatorTests 标记文件状态机
RollbackStrategy RollbackStrategyTests 快照回退
命名空间规则 LauncherArchitectureTests 无 Avalonia 泄漏

6. 明确不做

  • 不新建 csprojLauncher.Deployment 等)
  • 不新建 exe / Windows Service
  • 不改变 Public IPC / Coordinator IPC 协议
  • 不把插件市场安装迁回 Launcher
  • 不为模块间通信引入新 IPC仅保留现有 Host↔Launcher 契约)

7. 风险与缓解

风险 缓解
大规模移动 merge 冲突 B1 独立 commit零逻辑变更
Pipeline 迁移行为回归 先写 Phase 级测试再迁代码;保留 LMD_LAUNCHER_LEGACY_COORDINATOR=1 开关一个版本(可选)
AOT + DI 显式注册,禁止反射扫描;PublishAot CI 步骤验证
Update 拆分遗漏路径 CLI update * 与 GUI apply-update 同一 IUpdateEngine 门面

8. Git 工作流Agent 自主提交)

原则: 每个 Phase 验证通过后立即提交;不累积巨型 uncommitted diff。

Commit 前检查(每个 commit 必做):

dotnet build LanMountainDesktop.slnx -c Debug
dotnet test LanMountainDesktop.slnx -c Debug --filter "FullyQualifiedName~Launcher"

Commit message 风格(与仓库一致):

refactor(launcher): replace LauncherFlowCoordinator with LaunchPipeline

Pipeline + Phase pattern; LauncherOrchestrator becomes GUI entry.
No deployment or IPC contract changes.

禁止: git push --force、修改 git config、跳过 hooks除非 hook 失败需修复后新 commit

建议分支: refactor/launcher-internal-modularization(单 long-lived 分支,按 Phase 连续 commit或每 Phase 一个 PR 由用户决定 merge 时机)。


9. 整体完成定义Definition of Done

  • LauncherFlowCoordinator 源文件
  • App.axaml.cs ≤120 行,仅 Avalonia + Orchestrator 委托
  • UpdateEngineService 巨型文件已替换为 Facade + Strategies
  • 职责域目录就位,架构测试通过
  • 全量 Launcher 相关测试 + AOT publish smoke 通过
  • 安装包结构与 IPC 拓扑与重构前一致
  • 每个 Phase 有对应 Git commit工作区 clean