Files
koring-launcher/docs/auto-update-plan.md
T
dream_pep 30451d505c ci(release): 重构发布流水线,新增中文发布说明脚本,更新文档
重构GitHub Actions发布流水线:
- 改为手动触发,支持beta测试和正式发布两种模式
- 自动基于UTC时刻生成BUILD ID作为版本后缀
- 拆分打包签名与发布步骤,新增中文发布说明生成逻辑
新增scripts/release-notes.ps1脚本,用于生成带提交记录的中文发布说明
更新docs/auto-update-plan.md文档,适配新的发布流程并新增对应章节
2026-08-28 02:33:47 +08:00

17 KiB
Raw Blame History

Koring Launcher 自动更新方案(v1 规划稿)

状态:M1 已完成(2026-08-28M2 待开始 目标平台:Windows 优先NSIS exe 安装包),macOS/Linux 后续复用同一套架构 决策记录:

  • 安装器模式:保持 assisted 安装器(oneClick: false + 可改安装目录)→ 每次更新整包下载
  • 更新托管:GitHub Releases
  • 当前交付:M1 基础设施已完成electron-updater 依赖 + publish 配置 + 1.2.0 打包验证)

1. 背景与目标

  • 项目:Electron 33 + React 19 + TS + electron-builder 25Windows NSIS 安装包,dist:dev/beta/run 三模式打包)
  • 现状:package.json 版本 1.1.2,无任何更新机制;用户需手动下载新版安装包
  • 目标:客户端内自动检查新版本 → 下载 → 静默重装 → 自动重启,先跑通 Windows

2. 技术选型

选 electron-updaterelectron-builder 官方配套,与现有打包链无缝衔接)。

方案 说明 结论
electron-updater 自动生成 latest.yml 清单、SHA512 校验、进度事件、失败重试、断点续传 选用
electron-simple-updater 只支持替换 asar,不支持 NSIS 重装
update-electron-app 只面向 GitHub,定制性差
自研差分/自建服务 灰度、强制更新、统计需要时再评估 后期可选

版本兼容electron-builder 25.1.8 ↔ electron-updater ^6.x(官方同仓库发布,6.x 与 24/25/26 打包器配套)。

3. 关键约束(已确认)

3.1 assisted 安装器 → 整包下载

当前 electron-builder.yml 为 assisted 安装器:

nsis:
  oneClick: false
  allowToChangeInstallationDirectory: true

electron-updater 的 NSIS 差分更新(blockmap 增量)只支持一键安装(per-user oneClick; assisted 安装器安装目录不固定,退化为每次下载完整 koring-launcher-<version>-setup.exe

已决策:接受整包下载,不改安装体验。含义:

  • 每次更新下载全量安装包(预计几十 MB 级别),CDN/带宽按此评估;
  • 后续若用户量大、包体过大,可再评估改 oneClick 启用差分,或自研增量(代价高,不优先)。

3.2 代码签名(重要)

Windows 下 electron-updater 会对下载的安装包做 Authenticode 校验:当前 exe 有签名时,要求新安装包发布者一致; 当前 exe 未签名时校验会被跳过(记警告)。

  • 未签名:开发/内测可跑通全流程,但国内杀软对未签名 exe 误报率高;
  • 正式对外发布前必须做代码签名(OV 证书起步,EV 更佳),并让发布流水线对 setup.exe 签名;
  • 签名后 electron-builder 默认会校验一致性,无需额外配置,但要保证发布流水线签名证书与产物一致

4. 整体架构

┌───────────────────────────── 客户端 ─────────────────────────────┐
│  React UI (src/)  ⇄  preload.ts (contextBridge)  ⇄  主进程       │
│                     │  electronAPI.onUpdateStatus()  │           │
│                     └────────── IPC ────────────────┘           │
│  主进程 electron/updater.ts(封装 electron-updater             │
│  electron/handlers/update.tsIPC handler                     │
└──────────────────────────────┬──────────────────────────────────┘
                               │ HTTPS
                               ▼
                  ┌───────────────────────────┐
                  │   GitHub Releases  (仓库)  │
                  │  · koring-launcher-1.2.1  │
                  │    -setup.exe             │
                  │  · latest.yml             │
                  └───────────────────────────┘
                   (国内网络差 → 见 §5.3 加速对策)

5. 服务端:GitHub Releases

5.1 发布产物

electron-builder 配置 publish: { provider: github } 后,electron-builder --publish always (或 --publish 搭配 CI token)会自动:

  1. 打包 Windows 产物;
  2. 创建/更新 GitHub Releasetag 取自版本号,如 v1.2.1);
  3. 上传 koring-launcher-1.2.1-setup.exe + latest.yml+ latest.yml.blockmap,assisted 模式下不用但会生成)。

latest.yml 是更新清单(版本、文件路径、大小、sha512),electron-updater 靠它发现新版本并校验完整性:

version: 1.2.1
files:
  - url: koring-launcher-1.2.1-setup.exe
    sha512: <base64-sha512>
    size: 81234567
path: koring-launcher-1.2.1-setup.exe
sha512: <base64-sha512>
releaseDate: '2025-01-01T00:00:00.000Z'

5.2 配置示例(electron-builder.yml 增加段)

publish:
  provider: github
  owner: <GitHub 用户名/组织>
  repo: <仓库名>
  # releaseType: release   # 默认 releasebeta 通道可设 draft/prerelease

打包时 electron-builder 会把 app-update.yml(含 provider 信息)写进 resources/ electron-updater 在运行时读取它——没有 publish 配置就不会生成 app-update.yml,更新会直接报错M1 验收点)。

5.3 国内网络注意(务必先评估)

electron-updater 的 GitHub provider 走 api.github.com(发现版本)与 github.com/.../releases/download/...(下载), 国内部分网络环境访问慢或不稳定。对策(按需选):

  • 现状接受:很多应用直接走 GitHub,配合失败重试 + 手动"下载最新版"兜底按钮;
  • CDN 加速(推荐后续做):改为 generic provider,把 latest.yml + exe 同步到 OSS/COS + CDN(或 ghproxy 类代理),app-update.yml 指向 CDN URL
  • 自建/商业 CDN 代理 GitHub Releases:等用户量上来再评估。

方案设计上保持 provider 可切换:electron/updater.ts 只面向 electron-updater 统一 API 未来从 githubgeneric 只需改 electron-builder.yml + 重新打包,业务代码不动。

6. Windows 更新流程(整包下载版)

应用启动
 ├─ 加载完成且空闲后(延迟 10~15s)→ 静默 checkForUpdates()
 │    (避开启动加载与 Minecraft 下载抢带宽;首启/开发模式跳过)
 ├─ 无更新 → 结束,静默
 └─ 有更新 → 发 update:status{state:'available', version}
       │
       ▼
  前端提示"发现新版本 vX.Y.Z"(非强制,可忽略/稍后)
       │  用户点"下载更新"
       ▼
  autoUpdater.downloadUpdate()
   ├─ download-progress → update:status{state:'downloading', percent, speed, ...}
   ├─ 下载完成 → SHA512 校验(latest.yml)→ update:status{state:'downloaded'}
   │
   ▼
  前端提示"重启并安装"(可暂缓;退出时 autoInstallOnAppQuit 兜底)
       │  用户确认
       ▼
  autoUpdater.quitAndInstall()
   ├─ 应用退出 → NSIS 静默安装(不弹 UI,沿用原安装目录)
   └─ 安装完成自动重启 → 运行新版本

失败路径:
  · 网络失败 → 提示重试(electron-updater 自带断点续传/重试)
  · 校验失败 → 清除缓存重下;仍失败则提示手动下载最新版
  · 静默安装失败 → 提示手动下载;保留旧版本可用

更新状态机(前端 store 用)

idle → checking → available → downloading → downloaded → installing → relaunch
        └─not-available→ idle        └─ error → idle(可重试)

7. 代码结构规划

electron/
  updater.ts              # 新增:封装 electron-updater
                          #   · 初始化(读 app-update.yml、设日志)
                          #   · 守卫:!app.isPackaged 时跳过(开发模式)
                          #   · check() / download() / quitAndInstall() / 状态查询
                          #   · 订阅 checking/available/not-available/download-progress/downloaded/error
                          #   · 转发为 update:status 事件(带完整 payload
  handlers/update.ts      # 新增:IPC handler
                          #   · update:check            → 触发检查
                          #   · update:download         → 触发下载
                          #   · update:quitAndInstall   → 重启安装
                          #   · update:getState         → 查询当前状态/版本信息
  main.ts                 # 改:import { registerUpdateHandlers };注册;启动后延迟静默检查
  preload.ts              # 改:暴露
                          #   · checkForUpdates() / downloadUpdate() / quitAndInstall()
                          #   · onUpdateStatus(cb) → 订阅 update:status(沿用现有 on() 模式)

src/
  api/update.ts           # 新增:IPC 封装 + TS 类型(UpdateStatus
  stores/update.ts        # 新增:zustand store,维护状态机
  components/settings/... # 更新 UI:设置页"关于/更新"区块
                          #   · 当前版本号 + 检查更新按钮
                          #   · 下载进度条(百分比/速度/剩余大小)
                          #   · "重启并安装"确认
  components/...          # 可选:新版可用 toast / 弹窗(非强制打扰)

依赖注意electron-updater 必须放 dependencies(不能是 devDependencies), 否则 asar 打包后运行时找不到模块——最常见事故,M1 验收必须覆盖。

8. 版本与多通道

  • 沿用现有 scripts/version.js 升版(pnpm version:set 1.2.0),版本必须严格 semver 递增;
  • electron-updater 只认 package.json 的 version,升版后需重新打包发布;
  • dev / beta / run 三模式的通道隔离放加固阶段(M5)
    • GitHub provider 多通道依赖 tag 约定(如 v1.2.1-beta.1),行为略绕,先只用默认 latest 通道跑通;
    • 届时按产品需要决定:beta 走 prerelease/draft releaserun 走正式 release,或切 generic + 不同 URL。

9. 里程碑与验收

阶段 内容 验收标准
M1 基础设施 dependencies 加 electron-updater;② electron-builder.yml 加 publish(github);③ 升版打包 pnpm dist:run 产物目录出现 latest.yml;解包 asar 的 resources/app-update.yml 存在;electron-updater 在 asar 内可 require
M2 主进程 updater.ts + handlers/update.ts + preload 暴露 手动触发 update:check 能返回状态;事件能推送到渲染进程(dev 下用 forceDevUpdateConfig 或打包版验证)
M3 前端 UI update store + 设置页更新区块 + 启动静默检查 完整交互:检查/提示/进度/重启安装/失败重试
M4 联调冒烟 发布 v1.2.0 → 再发布 v1.2.1,真机从 1.2.0 升到 1.2.1 Windows 真机全流程通过,含取消下载、断网重试、校验失败场景
M5 加固 代码签名SignPath 远程签名已接入 CI,见 §12);国内加速(CDN/generic)评估;通道隔离;强制更新开关;临时文件清理 可对外发布

10. 风险与对策

风险 影响 对策
electron-updater 误放 devDependencies 打包后更新直接报错 M1 验收强制检查 asar 内依赖
未配置 publish → 无 app-update.yml 运行时报 "app-update.yml not found" M1 验收点;文档示例已给
未签名 exe 被杀软拦截 下载/安装被拦,更新失败 正式发布前签名(M5);内测接受现状
assisted 静默安装弹 UI / 请求管理员 更新中断 M4 用现有 build/installer-custom.nsh 专门联调;必要时加 runAfterFinish/静默参数处理
api.github.com 国内访问差 检查/下载慢或失败 失败重试 + 手动下载兜底;后续 CDN 加速(§5.3)
检查频率过高 浪费带宽、触发限流 启动延迟 10~15s + 手动按钮;间隔建议 ≥1h
磁盘满 / %LOCALAPPDATA% 权限 下载/解压失败 失败回退提示,清理 updaterCacheDirName 缓存
版本号不递增 永远查不到更新 CI/脚本校验 version:set 只允许升版
更新失败后应用状态异常 用户卡在旧版本 保持旧版可用;UI 提供手动下载入口;错误上报(可复用现有 crash 体系)

11. 后续待决策(不阻塞 M1~M4)

  • 是否需要强制更新 / 最低版本策略
  • 国内加速方案何时落地(generic + OSS/COS/CDN
  • macOS / Linux 更新的排期(架构已预留,届时分别补 zip/dmg 与 AppImage 产物与签名)

12. SignPath 代码签名接入(2026-08-28 落地)

方案electron-builder 自定义签名(win.sign: scripts/signpath-sign.js), 构建时对内部 exeKoring Launcher.exe、elevate.exe、卸载器)与最终 setup.exe 逐个提交 SignPath 远程签名,签名发生在 blockmap / latest.yml 生成之前, 清单 sha512 与 blockmap 自动对应签名后产物electron-updater 校验无缝。

SignPath 后台准备(已确认值)

  • Organization ID31ecd033-d59e-492b-a70b-b00a54bbc7c2(已写入 workflow env
  • API Token:用户详情页生成(CI 用途),放入 GitHub Secrets SIGNPATH_API_TOKEN
  • slug:项目 Koring_Launcher;签名策略 Koring_Launcher_Dev_builder(均写入 workflow env); 产物配置 DEFAULT 在项目仅一个配置时可省略 SIGNPATH_ARTIFACT_CONFIG_SLUG

⚠️ 待办约束

  • 测试证书:当前策略 Purpose 为 Test signing(测试证书),用户机器默认不信任 (SmartScreen / 杀软警告;signtool verify 会报"不受信任"——预期现象), 正式对外发布需生产证书(OV/EV)+ 对应生产签名策略(届时只换 SIGNPATH_SIGNING_POLICY_SLUG)。
  • 审批流程:已在 SignPath 后台关闭人工审批(自动批准),CI 可全自动;策略 Purpose 已改为 Release signing。

新增/改动文件

  • scripts/signpath-sign.js — 签名模块(无 token 自动跳过,本地构建不受影响)
  • .github/workflows/release.yml — 手动触发:选择 beta/run → 构建 → 签名 → 发布 GitHub Release(见 §13
  • scripts/release-notes.ps1 — 中文 Release 发布说明生成器
  • electron-builder.ymlwin.sign 挂载

CI Secrets:仅 SIGNPATH_API_TOKEN(必填)+ SIGNPATH_ARTIFACT_CONFIG_SLUG(可选); 组织 ID 与 slugs 已在 workflow 中写死。

2026-08-28 实测结果(与官方 PowerShell 模块对齐后的真实签名):

  • 协议:POST {base}/v1/{orgId}/SigningRequestsmultipart/form-data,字段 ProjectSlug / SigningPolicySlug / ArtifactConfigurationSlug? / Description / 文件部件 Artifact)→ 响应 Location 头 = 请求 URL → 轮询 status/isFinalStatus → 完成后取 signedArtifactLink 下载; 认证 Authorization: Bearer <token>
  • 98.3MB setup.exe 提交成功(无大小限制问题);自动批准生效(InProgress → Completed 无人工介入)
  • 小文件端到端签名成功,signtool verify 显示签名链 Issued to: Lingke Network
  • ⚠️ 本机到 SignPath 下载 100MB+ 产物极慢(20min+),CIGitHub Actions)网络环境不受影响; 模块已加下载重试(3 次)+ 临时文件替换

验证点(首次 CI 运行):确认 SignPath 请求全部 Completed、latest.yml 的 sha512 与上传的 signed setup.exe 一致、签名链显示 "Lingke Network"。

13. 发布流水线(手动触发 / BUILD ID / 中文 Release

触发方式Actions 页面 → Run workflow,仅手动触发(不再使用 tag 触发)。

  • modebeta(测试,发布为 GitHub prerelease/ run(正式,发布为普通 release
  • version:基础版本号(如 1.2.0

版本号与 BUILD ID

  • BUILD ID = Action 运行的 UTC 时刻,格式 YYMMDDHHMM(如 2608280224
  • 最终版本 = {version}-{BUILD ID}(如 1.2.0-2608280224),tag = v{version}-{BUILD ID} electron-builder 产物 = koring-launcher-{version}-{BUILD ID}-setup.exelatest.yml 同步更新

发布内容gh release create,中文正文由 scripts/release-notes.ps1 生成):

  • # Koring Launcher Releases {version} + 版本信息(当前版本 / 编译状态 BETA/RUN)
  • ## 更新了什么内容:自上个 v* tag 以来的提交记录,每条默认折叠 (<details><summary>·Commit 1cf906d</summary>…</details>
  • 上传产物:setup.exe + latest.ymlelectron-updater 更新清单)

⚠️ 版本语义注意(electron-updater

  • {version}-{BUILD ID} 属 semver prerelease:同格式版本之间可正常升级(BUILD ID 更大者胜)
  • 若未来发布不带 BUILD ID 的稳定版本(如 1.2.0),稳定版用户不会自动升级到带 BUILD ID 的构建
  • 同一分钟内重复触发会产生相同 BUILD ID → tag 冲突,gh release create 会失败,稍候重试即可