[Bug] 启动热更检查被短路后,后台更新可能把 splash 卡在 100%
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 问题描述 / What happened
从 0.1.72 起,Desktop splash 的启动热更检查被短路。客户端仍会在启动约 10 秒后过后台检查并下载补丁,但 splash 不再根据启动检查结果进入「更新完成,等待自动重启」,默认也不会空闲自动 apply。
实际行为:
- 这是竞态,不是必现卡死。0.1.72 同一构建上,CN 既可以卡住,也可以进入主界面。
- 若 splash 环境检查在约 10 秒后台热更开始前就已经 `passed`,会进入**当前旧版本**主界面;后台仍会把补丁下到 ready,但默认不自动 apply,需要用户点更新横幅才会 `executeRelaunch`。本机一次成功进入是启动后 822ms 就有 `LocalDbGate`,随后 10 秒才后台检查并本地 zip 直接 100%。
- 若 10 秒后台检查开始时 splash 还没 `passed`,进度事件会把状态打成 `updating`,界面停在更新 100%。因为启动检查已被短路成 `hasUpdate: false`,不会进入 `update_done`,也不会自动重启。本机卡住的两次直到退出都没有 `LocalDbGate`,也没有 `executeRelaunch`。
- 这不是 CN 特有逻辑。同机 Global 能升到 0.1.73,是因为它先进入了主界面,再走横幅 `update-relaunch`。两边都是旧包 OTA 到 beta,没有整包下载页。
期望行为:
- 启动阶段要么继续走完整热更闭环:检查 → 下载 → `update_done` → 重启应用。
- 要么明确跳过 splash 热更:后台下载不得把 splash 卡在更新 100%,应放行进入主界面,再由更新横幅 / 用户确认 apply。
- 不能出现「进度已经 100%,但既不进应用也不重启」的死状态。
## 环境 / Environment
- Cindy 版本或 commit / version or commit: 复现于 `0.1.72`(由 `0.1.69` OTA 上来);目标补丁 `0.1.73-beta`。引入提交 [061f5648b](https://github.com/makecindy/cindy/commit/061f5648b6256968a438fd49779a3f9de1123193)(`fix(desktop): skip splash startup update check`),合入提交 [8db3add69](https://github.com/makecindy/cindy/commit/8db3add6918ddd9096336482aa6e781ab43cde4b)(`merge: skip desktop splash startup update check`)。首次进入 `v0.1.72`。当前 `origin/main` 与 `v0.1.73-beta` 仍保留该短路。
- 平台与版本 / platform & OS version: macOS arm64
- 安装方式 / install method: `CindyCN.app` / `CindyGlobal.app` 正式包 + OTA 热更新,无整包下载页
## 复现步骤 / Steps to reproduce
1. 安装已包含启动热更短路的桌面包(`0.1.72` 或之后、尚未修复的构建)。
2. 打开 beta 渠道,使 CDN manifest 上存在更新。
3. 冷启动,并让 splash 的环境检查在 10 秒后台热更开始前尚未 `passed`。
4. 观察 splash 是否进入更新并停在 100%,主界面是否始终不出现。
5. 再冷启动一次:若环境检查很快 `passed`,可进入旧版本主界面,补丁已 ready,但不会自动 apply;需点击更新横幅才会 `executeRelaunch`。
补充:本地已有 ready zip 时,即使断网,后台检查仍可能直接广播 100%。只有本地没有可用补丁且 manifest 失败时,才比较容易放行进应用。不要在 100% 时强杀进程后再立刻启动,可能再次撞上同一状态。
## 日志与截图 / Logs & screenshots
本机 CindyCN 主进程日志(已脱敏)。`0.1.69` 仍会调用启动检查并成功 apply 到 `0.1.72`;进入 `0.1.72` 后启动检查消失。
```text
12:10:40 v=0.1.69 update-check-startup called
12:10:40 Startup: current=0.1.69, latest=0.1.72
12:11:21 auto relaunch conditions met (startup-apply-boundary), applying update v0.1.72
12:11:21 executeRelaunch() called
12:12:04 v=0.1.72 Initialized — first check in 10s
(本段起不再出现 update-check-startup called)
12:12:10 LocalDbGate ready rendererUptimeMs=6570
12:12:19 relaunch requested for update channel change
12:12:21 v=0.1.72 进入主界面
12:12:29 LocalDbGate ready rendererUptimeMs=8012
12:12:31 First background check current=0.1.72, latest=0.1.73, ready=
12:12:48 before-quit(下载 0.1.73 过程中退出)
12:12:56 v=0.1.72 卡住:直到 12:14:00 退出都没有 LocalDbGate
12:13:06 First background check current=0.1.72, latest=0.1.73, ready=
12:13:26 broadcastUpdateProgress p=100
无 executeRelaunch / 无 update-check-startup
12:16:44 v=0.1.72 再次卡住:直到 12:23:15 退出都没有 LocalDbGate
12:16:54 First background check current=0.1.72, latest=0.1.73, ready=
12:16:54 broadcastUpdateProgress p=100(本地 zip 直接完成)
无 executeRelaunch
13:11:26 v=0.1.72 这次进入主界面
13:11:27 LocalDbGate ready rendererUptimeMs=822
13:11:37 First background check,本地 zip 直接 100%
13:12:20 executeRelaunch() called app=/Applications/CindyCN.app zip=cindy-0.1.73-arm64-hotfix.zip
```
对照:Global 升到 `0.1.73` 走的是主界面横幅 `update-relaunch`,不是 splash 启动 apply。`LocalDbGate` 只说明主界面已经挂上,不是开库把 splash 卡死。
## 源码定位
当前 `origin/main` 仍是这个状态。
引入该回归的提交:
- 实际改动:https://github.com/makecindy/cindy/commit/061f5648b6256968a438fd49779a3f9de1123193
`fix(desktop): skip splash startup update check`
- 合入提交:https://github.com/makecindy/cindy/commit/8db3add6918ddd9096336482aa6e781ab43cde4b
`merge: skip desktop splash startup update check`
没有对应 GitHub PR,是 2026-08-31 直合,首次进入 `v0.1.72`。
1. 上述提交把 splash 启动检查改成立即返回:
```ts
// apps/desktop/src/preload/preload.ts:2510-2517
// Splash startup update check is temporarily disabled.
checkAppUpdate: () => Promise.resolve({ hasUpdate: false, action: 'none' }),
```
它不再 `invoke('update-check-startup')`。`0.1.69` 日志里还能看到这条 IPC,`0.1.72` 之后没有。
2. `UpdateService` 仍会在 10 秒后过后台检查。发现更新就 `broadcastUpdateProgress`:
```ts
// apps/desktop/src/main/updateService.ts:155
const FIRST_CHECK_DELAY_MS = 10_000;
// apps/desktop/src/main/updateService.ts:2316-2319
// Initialized — first check in 10s, polling every 30min
```
3. Renderer 仍按「启动热更检查还在」来消费这些进度。`status !== 'passed'` 时把进度写成 `'updating'`;只有启动检查返回 `hasUpdate && action === 'relaunch'` 才会 `update_done`:
```ts
// apps/desktop/src/renderer/contexts/EnvCheckContext.tsx:167-170
if (prev === 'passed') return prev;
// 随后 progress 事件 return 'updating'
// 同文件约 309-312
if (updateResult?.hasUpdate && updateResult?.action === 'relaunch') {
setStatus('update_done');
}
```
4. `EnvCheckGuard` 在非 `passed` 时不挂主界面,所以没有 `LocalDbGate`,也没有更新横幅:
```ts
// apps/desktop/src/renderer/contexts/EnvCheckContext.tsx:371-373
export function EnvCheckGuard({ children }: { children: ReactNode }) {
const { status } = useEnvCheck();
if (status !== 'passed') return null;
```
5. `useSplash.ts` 只在 `splash_update_done` 时调用 `autoRelaunchToUpdate()`。默认 `autoRelaunchOnIdle: false`,卡住时后台也不会空闲自动 apply。
6. 相关但不是同一件事:`5876662ab` 曾让冷启动 apply 不再看空闲开关;PR #1887 想恢复冷启动自动应用已就绪更新,已关闭且未合并。8/31 这次改动是从 splash 侧把启动检查整段关掉,后台下载却还留着。
未完全证实、修复时需要一并核对:部分卡住会话里 `check-environment` 可能也长时间没回到 `passed`。若环境检查已经结束,按当前 stub 理论上会 `setStatus('passed')`。日志能确定的是:这些会话直到退出都没有主界面,同时 splash 被后台热更进度钉在 100%。
## 建议方向
- 不要只短路 preload,却留下 splash 的 `updating` / `update_done` 状态机。
- 若产品仍要 splash 启动热更:恢复 `checkAppUpdate()` → `update-check-startup`,让 ready 补丁回到 `update_done` + 启动 apply。
- 若产品确认 splash 不再做启动热更:后台 `app-update-progress` 在进入主界面前不得把 status 打成 `'updating'`;下载完成应放行主界面,再走更新横幅。
- 回归时至少覆盖:环境检查慢于 10 秒后台热更、本地 zip 已 ready、默认关闭空闲自动重启、CN/Global 两个包装。
Contributor guide
Research direction
Start with apps/desktop/src/preload/preload.ts around checkAppUpdate, then trace the 10-second background check in apps/desktop/src/main/updateService.ts and progress handling in EnvCheckContext.tsx and useSplash.ts. Reproduce with a slow environment check and a ready local zip. Done means the splash cannot remain at 100% without either reaching the main interface or completing the update relaunch flow, including CN and Global packaging.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, typescript
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100