一个 .NET 双进程自更新库。应用程序可以下载更新、启动独立的更新进程、退出,让更新进程原子性地替换文件并重启。
- 双进程更新模式 — 主程序下载更新、启动更新进程、退出;更新进程替换文件并重启主程序
- Zstd 压缩 — 更新包使用 Zstd(通过 ZstdSharp)进行高效压缩
- 原子性文件替换 — 在 Windows 上使用
ReplaceFileWin32 API 进行可靠的文件替换 - 回滚支持 — 失败时自动备份和回滚
- 链式更新路径 — 支持锚点包和累积包的版本链
- SHA512 校验 — 文件级完整性检查
- 无内置 HTTP — 消费者通过
IUpdateCatalogFetcher提供自己的 HTTP 实现 - 无界面 — 纯库,通过事件报告进度,无 UI
dotnet add package Moder.UpdateusingModer.Update;usingModer.Update.Compression;usingModer.Update.FileOperations;usingModer.Update.Models;usingModer.Update.Package;// 设置组件varcompressor=newZstdCompressor();varpackageReader=newZstdPackageReader(compressor);varfileService=newFileReplacementService();varprocessSpawner=newProcessSpawner();varupdateManager=newUpdateManager(packageReader,fileService,processSpawner);// 检查更新(使用你的 HTTP 客户端实现 IUpdateCatalogFetcher)varchecker=newUpdateChecker(myFetcher,updateManager);varresult=awaitchecker.CheckForUpdatesAsync("1.0.0");if(result.Status==UpdateCheckStatus.UpdateAvailable){foreach(varentryinresult.UpdatePath!){// 下载并应用每个更新包usingvarpackageStream=awaitmyFetcher.DownloadPackageAsync(entry.PackagePath);varupdateResult=awaitupdateManager.ApplyUpdateAsync(packageStream,newUpdateOptions{CurrentVersion="1.0.0",TargetDir=AppContext.BaseDirectory,EnableRollback=true});}// 启动更新进程并退出updateManager.PrepareRestart("Moder.Update.Updater.exe",options);Environment.Exit(0);}publicclassMyHttpFetcher:IUpdateCatalogFetcher{privatereadonlyHttpClient_client;privatereadonlystring_baseUrl;publicMyHttpFetcher(HttpClientclient,stringbaseUrl){_client=client;_baseUrl=baseUrl;}publicasyncTask<string>FetchCatalogAsync(CancellationTokenct=default){returnawait_client.GetStringAsync($"{_baseUrl}/catalog.json",ct);}publicasyncTask<Stream>DownloadPackageAsync(stringpackagePath,CancellationTokenct=default){returnawait_client.GetStreamAsync($"{_baseUrl}/{packagePath}",ct);}}更新包使用以下二进制格式:
[4 字节: "MUP\0" 魔术头] + [Zstd 压缩的 tar 归档]
tar 归档包含:
manifest.json— 更新清单,包含版本信息、文件列表和校验和- 应用程序文件 — 完整替换文件(非差分)
更新目录是一个 JSON 文件,服务于固定 URL:
{
"latestVersion": "2.0.0",
"minRequiredVersion": "1.0.0",
"lastUpdated": "2025-01-01T00:00:00Z",
"entries": [
{
"packagePath": "packages/1.1.0.zst",
"targetVersion": "1.1.0",
"minSourceVersion": "1.0.0",
"maxSourceVersion": "1.0.99",
"packageChecksum": "sha512hash...",
"fileCount": 5,
"compressedSize": 1024000,
"uncompressedSize": 2048000
}
]
}Moder.Update.Updater 是一个独立的控制台应用程序:
- 等待主应用程序进程退出
- 使用原子操作替换应用程序文件
- 重启主应用程序
Moder.Update.Updater --target-pid 1234 --target-path /path/to/app --staging-dir /path/to/stagingdotnet build src/Moder.Update.sln
dotnet test src/Moder.Update.sln./scripts/pack.sh # Linux/macOS
scripts\pack.cmd # Windows提供了一个示例项目(Moder.Update.Demo)用于端到端测试更新流程。
- .NET 10.0 SDK 或更高版本
- Windows 操作系统(该库使用 Win32
ReplaceFileAPI)
# 1. 构建解决方案
dotnet build src/Moder.Update.sln
# 2. 创建测试包 (1.0.0 -> 1.1.0)
dotnet run --project src/Moder.Update.Demo -- \
--create-package 1.0.0 1.1.0 ./demo-app ./demo-packages
# 3. 将示例程序复制到测试目录
mkdir -p ./test-app
dotnet publish src/Moder.Update.Demo -c Release -o ./test-app
# 4. 运行 --check 查看更新状态
dotnet run --project ./test-app/Moder.Update.Demo.dll -- --check
# 5. 运行 --apply 应用更新并重启
dotnet run --project ./test-app/Moder.Update.Demo.dll -- --apply| 命令 | 描述 |
|---|---|
--version | 显示当前版本 |
--check | 使用本地目录检查更新 |
--apply | 下载并应用更新包,然后重启 |
--create-package <from> <to> <source> <output> | 创建测试更新包 |
# Linux/macOS
./scripts/create-demo-package.sh 1.0.0 1.1.0 ./my-app ./demo-packages
# Windows
scripts\create-demo-package.cmd 1.0.0 1.1.0 .\my-app .\demo-packages示例程序从相对于二进制文件的 ../../../demo-packages 目录查找更新包。该目录应包含:
catalog.json— 更新目录*.zst— 更新包
MIT