fix(updater): keep a downloaded update across checks and restarts - #2949
Merged
Merged
Conversation
A downloaded update could not be installed. Every update check started a new audit operation and cleared the prepared transaction and manifest, so the package that had just been downloaded was forgotten, and the install step had nothing left to hand to the helper. The frontend cleared the state in the same way: every check response overwrote the update state, and several clicks on "Check for updates" ran one serialized check each and showed one message each. Backend: - Reuse a prepared update before discovery runs: a remembered signed manifest is verified again with the bundled key, the cached package is checked against the size and SHA-256 it names, and the staged package is re-validated before it is offered for installation (PREPARED_UPDATE_RESTORED). A remembered update whose release epoch no longer advances the installed one is spent and dropped quietly (PREPARED_UPDATE_CONSUMED); anything that no longer verifies is discarded (PREPARED_UPDATE_DISCARDED) and downloaded again. - Remember the prepared manifest and transaction in prepared-update.json, and keep the staging directory that was produced from exactly one package, so a restart neither downloads nor unpacks the same payload again. - Make triggerDownload idempotent: a repeated request for a prepared update reports success and the completed progress instead of downloading again, and a check can no longer discard the discovered update or a running download. - Report "ready to install" as a third check result so the client can offer the install action instead of "no update available". Frontend: - Share one in-flight check between overlapping triggers, ignore a second click while the check runs, and show the check button as loading. - Never let a check response erase an update that is downloading, installing or already downloaded: only a newer discovered release may replace it. Tests cover the persisted prepared update, staging reuse, the cache hit without a download, the redownload after a changed package, the offline restore after a restart, and the frontend state protection and request de-duplication.
A prepared update that came from the beta channel failed verification in the next session: the remembered manifest was checked against the stable channel, so the manifest was declared untrusted and the downloaded beta update was discarded instead of being offered for installation. Verify the remembered manifest against the channel it names, exactly like the discovery verifies a manifest against the channel it queried. The manifest is covered by the signature, so its channel cannot be forged.
…lable" A check that could not reach the update source was reported exactly like a check that found no newer release, so a user with a broken network or a blocked download host was told "no new version available" and had no way to tell the difference. Report the two cases separately: - A release index or manifest that the source does not publish (HTTP 404) keeps meaning "no update available", because a channel that has not published its index yet is not an error. - Any other failure (connect timeout, server error, verification failure) is reported as a failed check. The check result carries a new CHECK_FAILED state, the JCEF handler maps it to the updateFailed status, and the client already renders that status as an error message. The new client message says the check failed and suggests checking the network instead of claiming that no new version exists.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
下载完成后更新装不上:每次
appCheckUpdate()都会开启新的审计操作并把preparedTransaction/preparedManifest清空,刚下载并暂存的包当场被遗忘,点"重启安装"时后端已经没有可交给 helper 的更新;前端同样在每次检查响应里无条件覆盖状态。具体缺陷(本次一次性修掉):
beginAuditOperation,前端action.ts)triggerDownload不幂等Updated被当成"没有新版本"改动
后端
DISCOVERY/PREPARED_UPDATE_RESTORED),完全跳过 discovery,全程不需要网络。PREPARED_UPDATE_CONSUMED);验签失败或包被改动则丢弃并重新下载(PREPARED_UPDATE_DISCARDED)。prepared-update.json持久化(PreparedUpdateStore,原子写、损坏即视为无);暂存目录用.source-sha256标记来源包,重启后不重复解包。triggerDownload幂等:已 prepared 时直接返回成功并补发一次完成进度;检查失败不再丢弃已发现的更新;缓存命中记DOWNLOADING/CACHE_HIT,缓存不匹配才删除重下。READY_TO_INSTALL(UpdatedStatus.Updated),客户端据此显示"重启安装"而不是"没有可用新版本"。前端
文档:
script/package/README-updates.md增加下载/暂存/恢复说明,并修正 LaunchAgent 标签为 per-product(com.chat2db.updater.<product>)。验证
chat2db-community-updater:112 tests,0 失败(新增PreparedUpdateStoreTest7 个、FullPackageStagerTest5 个复用用例)。chat2db-community-jcef:137 tests,0 失败(新增 6 个:缓存命中不发起下载、重复下载不重下、检查不丢 prepared、重启后离线恢复、篡改缓存被丢弃并重下、已安装记录被静默清理)。yarn test:hot-update、yarn test:update-check-schedule通过,改动文件 eslint 0 warning。未包含 / 待办
本机真机测试期间发现并补修的两个问题(同一 PR)
channel=BETA,于是验签通过、通道校验失败 → 已下载的 beta 更新被丢弃。现在按 manifest 自己的通道校验(通道字段在签名覆盖范围内,不可伪造),并补了回归测试(去掉修复该测试即红)。appCheckUpdate的 catch 无论超时、连不上还是 404 都返回 notAvailable。现在分两类:CHECK_FAILED态 → handler 映射updateFailed→ 前端提示"检查更新失败,请检查网络后重试"(5 种语言已补,i18n 校验通过)测试与 CI
chat2db-community-updater:112 tests,chat2db-community-jcef:141 tests,0 失败test:hot-update、test:update-check-schedule、test:i18n通过,改动文件 eslint 0 warning真机验证进度(本机 macOS,隔离的 dev 实例)
CHECK_FAILED分支PREPARED_UPDATE_RESTORED)、点击安装后 helper 切换 + 自动重启到 beta.4(首次尝试因测试包内 helper jar 取错而 30s ACK 超时并失败,属测试环境问题,已修正后重试)