feat(core): 新增 update 模块,做与更新源无关的自更新内核
更新源还没拍板,所以先把与源无关的那半边做掉:语义化版本比较、发布清单模型、 渠道准入、下载校验、原子替换 + 回滚。源抽象成 UpdateSource trait(清单 + 包体两个 方法),core 里没有任何 CDN 常量;内置的 DirSource 吃本地目录或 file://,既是测试 实现也是将来 HTTP 源的对照物。 取舍: - 版本比较严格按 semver 2.0 §11:数值比(0.9 < 0.10)、预发布 < 同号正式版、 预发布标识逐段比(alpha < beta < rc 是 ASCII 序的自然结果,不另立阶段权重表)、 构建元数据不参与排序与相等。 - 渠道准入两道判据:清单自报渠道的阶梯比较 + 版本号里的预发布阶段。清单可能写错, 版本号不会,所以正式版渠道即使拿到一份自称 stable 的 rc 也不会提示更新。 - 校验两道判据且顺序有意:先比长度(抓截断,不必读全文件)再比摘要(抓内容错), 长度要求**恰好**相等;失败即删暂存文件。StagedPackage 只能由 stage() 产出, 于是「未校验的包」在类型上就进不了安装路径。 - 替换跨平台统一成「旧文件改名挪开 → 新文件搬进来 → 失败把备份搬回去」:Windows 上 覆盖运行中的 exe 不存在,改名是唯一可用动作,而改名也可能被不共享删除的句柄拒绝, 所以失败必须能回滚。Unix 也照这个顺序跑,避免回滚路径只有 Windows 被真正跑过。 - core 零 UI 依赖不变:只有 serde/serde_json + 复用的 sha1/sha2,无新依赖, Cargo.toml 未改;不接 presenter、不接 UI。 单测 62 条(crates/core/src/update/tests/):版本边界、清单校验、渠道准入、 摘要不符、包体截断(短了/多了)、无权限写目标目录、Windows 占用文件、回滚与备份清理。
This commit is contained in:
1 parent
156abfe38b
commit
2e718af711
21 files changed
+3309
No files matched your search
@@ -0,0 +1,66 @@
|
||||
//! 自更新内核:版本比较、发布清单、渠道准入、下载校验、原子替换。
|
||||
//!
|
||||
//! 用户已定的主线是「安装包 + 自动更新通道」,但**更新源还没拍板**。所以这一层只做与源无关
|
||||
//! 的部分,源本身是插拔的 [`UpdateSource`]:core 里没有任何 CDN 常量、没有任何厂商名,
|
||||
//! 接哪家由上层注入(本地目录源 [`DirSource`] 是内置的对照/测试实现)。
|
||||
//!
|
||||
//! 设计输入:`DevDocs/analyse/07-settings-packaging-update-p2.md` §4(HMCL 自更新)与 §6.2
|
||||
//! (多平台清单扩展)。我们抄骨架、补两处 HMCL 明确放弃的东西:**回滚**与**按平台分发**。
|
||||
//!
|
||||
//! # 一条完整的更新链
|
||||
//!
|
||||
//! ```text
|
||||
//! UpdateChecker::check(current, channel) 清单 + 版本 + 渠道 + 目标平台 → 结论
|
||||
//! │ Available(candidate)
|
||||
//! ├─ UpdateChecker::prepare(candidate, staging) 取包 → 长度/摘要双检
|
||||
//! │ │ StagedPackage(校验通过,失败已自动清理)
|
||||
//! │ └─ install::install_staged(...) 改名旧版 → 搬入新版(失败回滚)
|
||||
//! └─ UpToDate / MissingArtifact
|
||||
//! ```
|
||||
//!
|
||||
//! # 分层
|
||||
//!
|
||||
//! ```text
|
||||
//! version 语义化版本解析与排序(alpha/beta/rc、build metadata) channel 渠道阶梯与准入
|
||||
//! manifest 清单模型 + JSON 校验 target 目标平台与包形态
|
||||
//! source UpdateSource trait + 本地目录源 verify 暂存 + 长度/摘要校验
|
||||
//! checker 选择哪一份发布、急不急 install 原子替换 + 回滚
|
||||
//! error 分类与文案
|
||||
//! ```
|
||||
//!
|
||||
//! # 刻意不做的事
|
||||
//!
|
||||
//! * **不碰 UI**:本模块只产出可序列化的判定结果与错误分类,弹窗/进度/文案排版在壳层。
|
||||
//! 入口在 `crates/presenter`/`crates/app-iced` 接,`crates/core` 保持零 UI 依赖。
|
||||
//! * **不自己替换正在运行的 exe**:那需要「退出自身 → 另一个进程搬运」(HMCL 的
|
||||
//! `--apply-to`),属启动器外壳的编排;本模块只保证搬运本身原子且可回滚。
|
||||
//! * **不装安装器形态**(msi/dmg/deb/rpm):交给系统包管理器。
|
||||
//! * **不做差分/增量**:清单里没有 patch 字段,宁可每次全量,也别先上一套没验过的差分协议。
|
||||
|
||||
pub mod channel;
|
||||
pub mod checker;
|
||||
pub mod error;
|
||||
pub mod install;
|
||||
pub mod manifest;
|
||||
pub mod source;
|
||||
pub mod target;
|
||||
pub mod verify;
|
||||
pub mod version;
|
||||
|
||||
pub use crate::download::{HashAlgo, Integrity};
|
||||
pub use crate::platform::os_info::OsFamily;
|
||||
|
||||
pub use self::channel::Channel;
|
||||
pub use self::checker::{CheckOutcome, UpdateCandidate, UpdateChecker, Urgency};
|
||||
pub use self::error::{UpdateError, UpdateErrorKind};
|
||||
pub use self::install::{
|
||||
backup_path, install_staged, InstallFailure, InstallReceipt, ReplacePolicy,
|
||||
};
|
||||
pub use self::manifest::{Artifact, Release, ReleaseManifest};
|
||||
pub use self::source::{DirSource, UpdateSource};
|
||||
pub use self::target::{Arch, PackageFormat, Target};
|
||||
pub use self::verify::{stage, verify_file, StagedPackage};
|
||||
pub use self::version::{Stage, Version, VersionError};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
Reference in new issue
Block a user