Files
update-client/Docs/客户端部署说明.md
T

397 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UpdateClientSDK 客户端部署说明
本文按“维护者打包 SDK -> 你接入业务软件 -> 联调测试 -> 生成最终客户端包”的顺序说明。你拿到这份文档后,按章节一步一步做即可。
## 一、这个 SDK 是什么
UpdateClientSDK 是“独立更新器 SDK / 升级运行时 SDK”。它不是传统的 `include + lib` 形态,而是把自动升级能力做成一组独立程序,让业务软件通过这些程序完成检查更新、下载、安装、回滚和启动保护。
SDK 核心程序:
- `Launcher.exe`:用户入口。检查版本、验证授权和策略,决定直接启动业务主程序或进入升级流程。
- `Updater.exe`:下载、校验、备份、安装、健康确认、提交或回滚。
- `Bootstrap.exe`:处理运行中可能被占用的 EXE/DLL 替换。
- `config/app_config.json`:部署配置源文件。启动时会同步到当前 Windows 用户的注册表,运行时优先读注册表。
- `config/manifest_public_key.pem`:Manifest 签名公钥,用来验证服务端发布包没有被篡改。
## 二、维护者:生成 SDK 包
这一节是 SDK 维护者操作。接入方通常只需要拿到 `UpdateClientSDK.zip`
打包前确认:
1. 已在 Windows 上用 Release 配置编译完成,输出目录里有 `Launcher.exe``Updater.exe``Bootstrap.exe`
2. `config/manifest_public_key.pem` 和服务端使用的私钥是一对。
3. SDK Word 接入说明已经放在 `update-client` 根目录或 `update-client/Docs` 目录下,文件名包含 `SDK`,例如 `SimCAE自动升级SDK接入说明_v0.1.docx`。打包脚本会把它复制到 SDK 根目录,方便接入方一打开压缩包就能看到。
4. 如果业务软件本身已经带 Qt DLL,通常不要把 SDK 的 Qt 运行库打进去,避免 Qt 版本混用。
在 Windows PowerShell 中执行:
```powershell
cd C:\Users\admin\Desktop\update-client
.\scripts\package-sdk.ps1 `
-SourceDir .\out\bin `
-OutputDir .\dist\UpdateClientSDK `
-ZipFile .\dist\UpdateClientSDK.zip `
-SdkVersion 0.1.0
```
生成结果:
```text
dist/
UpdateClientSDK/
SimCAE自动升级SDK接入说明_v0.1.docx
sdk_manifest.json
bin/
Launcher.exe
Updater.exe
Bootstrap.exe
...
config/
app_config.json
manifest_public_key.pem
scripts/
install-sdk.ps1
package-client.ps1
package-sdk.ps1
UpdateClientSDK.zip
```
`dist/UpdateClientSDK.zip` 发给接入方即可。
可选参数:
- `-IncludeDemoMainApp`:把仓库里的 Demo 主程序 `MainApp.exe` 也打进 SDK,方便演示。
- `-IncludeQtRuntime`:把 Qt 运行库也打进 SDK。只有业务软件本身不带 Qt 时才建议使用。
Linux SDK 打包方式:
```bash
cd /home/laluo/project/update-client
cmake --preset linux-x64-release
cmake --build --preset linux-x64-release
bash ./scripts/package-sdk.sh \
--source-dir ./out/linux/bin \
--output-dir ./dist/UpdateClientSDK-linux \
--archive ./dist/UpdateClientSDK-linux.tar.gz \
--sdk-version 0.1.0
```
Linux SDK 包里核心程序名不带 `.exe`
```text
dist/
UpdateClientSDK-linux/
SimCAE自动升级SDK接入说明_v0.1.docx
sdk_manifest.json
bin/
Launcher
Updater
Bootstrap
Common/
ConfigHelper.h
ConfigHelper.cpp
TicketHelper.h
TicketHelper.cpp
config/
app_config.json
manifest_public_key.pem
scripts/
package-sdk.sh
package-client.sh
UpdateClientSDK-linux.tar.gz
```
## 三、你:把 SDK 放进业务软件目录
假设业务软件目录是:
```text
D:\SimCAE\
bin\
SimCAE.exe
Qt5Core.dll
...
Licenses\
installerResources\
```
推荐把 SDK 放到 `bin` 目录,和 `SimCAE.exe` 同级;后台发布新版本时仍选择整个 `D:\SimCAE\` 作为发布根目录。
先解压 SDK
```powershell
Expand-Archive D:\交付\UpdateClientSDK.zip -DestinationPath D:\SimCAE_SDK -Force
```
再安装到业务软件的 `bin` 目录:
```powershell
cd D:\SimCAE\bin
D:\SimCAE_SDK\scripts\install-sdk.ps1 `
-SdkRoot D:\SimCAE_SDK `
-ReleaseDir .
```
安装后目录应类似:
```text
D:\SimCAE\
bin\
Launcher.exe
Updater.exe
Bootstrap.exe
SimCAE.exe
config\
app_config.json
manifest_public_key.pem
Qt5Core.dll
...
Licenses\
installerResources\
```
如果你需要重新覆盖 `config/app_config.json`,执行安装脚本时加 `-OverwriteConfig`
```powershell
D:\SimCAE_SDK\scripts\install-sdk.ps1 `
-SdkRoot D:\SimCAE_SDK `
-ReleaseDir D:\SimCAE\bin `
-OverwriteConfig
```
注意:SimCAE 自己已经带有 Qt 运行库。SDK 的 Launcher/Updater 应复用 SimCAE 的 `Qt5*.dll``platforms/``imageformats/` 等目录。不要把另一套 Qt DLL 覆盖到 `SimCAE\bin`,否则可能出现“无法定位程序输入点”一类错误。
## 四、你:生成并填写 app_config.json
最推荐的方式是在服务端管理后台生成客户端配置:
1. 浏览器打开服务端管理后台,例如 `http://服务器IP:8000/`
2. 登录后台。
3. 创建或选择应用,例如 `app_id=simcae`
4. 创建 License。
5. 在“客户端配置生成”区域选择应用、渠道、License 和主程序名。
6. 点击生成配置。
7. 复制生成的 JSON,覆盖 `D:\SimCAE\bin\config\app_config.json`
配置同步规则:
- `app_config.json` 是部署配置源文件,适合交付、复制、人工修改。
- Launcher / Updater / MainApp 启动时会计算 `app_config.json` 解析后的 JSON 内容 SHA256;如果 JSON 内容和上次导入时不同,就把文件里的配置重新写入当前 Windows 用户的注册表。
- 后续运行时优先从注册表读取配置,不再每次直接读 JSON。
- 运行过程中产生的动态值,例如首次输入的 `license_key`、服务端返回的 `device_id`、升级后的 `current_version`,会写入注册表。
- 为减少明文暴露,SDK 成功把非空 `app_config.json` 导入注册表后,会把 `app_config.json` 内容自动清空为 `{}`,但不会删除这个文件。以后你从管理后台复制新的客户端配置时,直接覆盖这个文件即可。
- SDK 会同步更新注册表里的 `source_sha256`,所以 `{}` 不会在下一次启动时反向覆盖注册表配置。
- 如果你手动修改了 `app_config.json`,下一次启动会重新导入并覆盖注册表里的同名字段。
- 如果检测到 `app_config.json` 确实发生变化,SDK 会同时删除 `config/client_identity.dat``config/version_policy.dat``config/local_state.json`,避免继续使用旧 License、旧设备身份、旧版本策略或旧防回滚状态;如果安装目录不可写,会弹出管理员权限确认框。
- 正常启动成功后,不要删除 `client_identity.dat``version_policy.dat``local_state.json`。它们分别用于本地设备身份、离线策略和防回滚/防时间倒退,删除后会影响离线启动或导致重新授权。
- 注册表按安装目录隔离;同一台电脑上多个安装目录不会互相覆盖配置。
- 注册表位置为 `HKEY_CURRENT_USER\Software\Marsco\UpdateClientSDK\installations\<安装目录SHA256>\config`
关键字段说明:
- `app_id`:服务端应用 ID,要和管理后台里的应用一致。
- `app_name`:应用显示名称。
- `channel`:发布渠道,例如 `stable``beta``dev`
- `current_version`:客户端当前初始版本,必须和后台已发布的版本一致。
- `client_protocol`:客户端协议号,当前建议为 `3`
- `launch_token`:本机启动票据 HMAC 密钥。服务端生成配置时会填默认值;正式部署建议按项目统一修改。
- `license_key`:管理后台创建 License 后生成的授权码。为空时首次启动 `Launcher.exe` 会弹窗让用户输入并保存到注册表;如果授权错误或过期,也会提示重新输入。
- `api_base_url`:服务端 API 地址,例如 `http://192.168.229.128:8000`
- `client_token`:服务端 `.env` 中的 `CLIENT_API_TOKEN`,必须和服务端一致。
- `device_id`:设备 ID。一般可以留空,首次启动时 SDK 会向服务端登记并写入注册表。
- `install_root`:被更新的安装根目录相对 `Launcher.exe` 所在目录的位置。SDK 放在 `bin` 时填 `..`
- `main_executable`:业务主程序相对 `Launcher.exe` 所在目录的路径。SDK 放在 `bin` 且主程序也在 `bin` 时填 `SimCAE.exe`
- `launcher_executable``updater_executable``bootstrap_executable`:通常不用改。
- `platform``arch`:当前为 `windows``x64`
典型配置:
```json
{
"app_id": "simcae",
"app_name": "SimCAE",
"channel": "stable",
"current_version": "1.0.0",
"client_protocol": "3",
"launch_token": "SimCAE_Launch_Token_2026_ChangeMe_32Bytes",
"license_key": "MARSCO-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"api_base_url": "http://192.168.229.128:8000",
"client_token": "SimCAEClientToken2026",
"request_timeout_ms": "5000",
"temp_folder": "update_temp",
"device_id": "",
"install_root": "..",
"main_executable": "SimCAE.exe",
"launcher_executable": "Launcher.exe",
"updater_executable": "Updater.exe",
"bootstrap_executable": "Bootstrap.exe",
"health_check_timeout_ms": "15000",
"platform": "windows",
"arch": "x64"
}
```
## 五、你:业务主程序需要配合什么
当前安全模式下,业务主程序需要配合两件事:
1. 接收 `--ticket-file=<path>` 参数,验证并消费一次性启动票据。
2. 如果收到 `--health-file=<path>` 参数,启动成功后向该路径写入 `ok\n`,让 Updater 确认新版本可用。
接入位置:
```text
main / WinMain 开头,创建主窗口之前
```
当前仓库里的 `update-client/MainApp/main.cpp` 是接入示例,已经实现:
- 启动票据校验。
- 本地 License/设备身份校验。
- 本地策略校验。
- Manifest 完整性校验。
- 健康标记写入。
真正接入业务软件时,把这些启动检查逻辑移植到业务主程序。用户入口应改成 `Launcher.exe`,不要让用户直接双击 `SimCAE.exe`
重要:业务主程序校验 ticket 时,必须使用 SDK 当前运行配置里的动态值,不要直接从 `app_config.json` 读取 `device_id`
原因是 `app_config.json` 是部署源文件,网页生成时 `device_id` 通常为空;真正的设备 ID 是 `Launcher.exe` 首次向服务端登记后写入当前用户注册表的。`Launcher.exe` 生成 ticket 时使用的是注册表里的真实 `device_id`。如果业务主程序从 `app_config.json` 读取空的 `device_id` 来校验,就会出现:
```text
ticket signature, identity, time or nonce invalid
```
或者细化后的:
```text
ticket device_id mismatch
```
业务主程序应像 `MainApp/main.cpp` 示例一样使用:
```cpp
ConfigHelper& config = ConfigHelper::instance();
TicketHelper::consumeAndVerify(
ticketFilePath,
config.getValue("App", "app_id"),
config.getValue("Update", "device_id"),
config.getValue("App", "current_version"),
config.getValue("App", "launch_token"),
&ticketError);
```
也就是说,接入业务主程序时不能只复制一小段 ticket 代码后自己解析 JSON;要么复用 SDK 的 `ConfigHelper` / `TicketHelper`,要么保证业务主程序读取到的 `app_id``device_id``current_version``launch_token``Launcher.exe` 完全来自同一套运行配置。
## 六、你:准备服务端数据
首次联调前,服务端至少要准备这些内容:
1. 创建应用,例如 `simcae`
2. 创建渠道,例如 `stable`
3. 创建 License。
4. 发布一个初始版本,例如 `1.0.0`
5. 生成客户端配置,并写入 `bin\config\app_config.json`。客户端下次启动时会自动同步到注册表。
为什么必须先发布初始版本:Launcher 启动业务主程序前会做 Manifest 完整性校验。这个 Manifest 是服务端发布版本时生成并签名的清单,用来证明当前本地文件属于一个可信版本。如果没有发布过 `current_version` 对应版本,客户端会提示签名 Manifest 缓存缺失。
后台发布版本时,选择整个安装根目录,例如 `D:\SimCAE\`,不要只选择 `D:\SimCAE\bin`。这样服务端会把 `bin/SimCAE.exe``Licenses/``installerResources/` 等完整结构写进 Manifest。
## 七、你:运行和联调
基础联调步骤:
1. 确认服务端正在运行。
2. 确认 `bin\config\app_config.json``api_base_url``client_token``license_key``current_version` 正确。
3. 双击 `bin\Launcher.exe`
4. 首次启动时如果 `license_key` 为空,按弹窗输入后台创建的 License;SDK 会把它保存到注册表。
5. 成功进入业务主程序后,回到后台查看设备、升级日志、下载日志。
6. 在后台发布更高版本,例如从 `1.0.0` 发布到 `1.0.1`
7. 再次启动 `Launcher.exe`,验证升级、健康确认和回滚逻辑。
联调目录建议:
```text
D:\SimCAE_Release\
C:\Users\<你的用户名>\Desktop\SimCAE_Release\
```
如果安装在 `C:\Program Files\...` 这类默认不可写目录,普通配置值会写入当前用户注册表,不需要修改 `app_config.json`。但 `client_identity.dat``local_state.json``version_policy.dat` 等本地状态文件仍位于安装目录下;当这些文件需要写入且目录不可写时,SDK 会弹出 Windows 管理员权限确认框,用户点击“是”后会继续保存。
注意:这次提权主要覆盖小型配置/状态文件的写入和删除。更新缓存、离线包暂存、升级替换 EXE/DLL 等大文件操作仍建议放在可写目录;如果最终产品必须完整安装到 `C:\Program Files\SimCAE\bin` 并在普通用户下自动升级,后续建议把运行时状态迁移到 `ProgramData` / `AppData`,或让 Updater/Bootstrap 在替换安装目录文件时走管理员权限。
## 八、维护者:生成某个产品的最终客户端包
SDK 是给接入方开发使用的。最终给用户安装或分发时,可以从已经联调过的 Release 目录生成最终客户端包。
在 Windows PowerShell 中执行:
```powershell
cd C:\Users\admin\Desktop\update-client
.\scripts\package-client.ps1 `
-SourceDir .\out\bin `
-ConfigFile .\config\app_config.json `
-OutputDir .\dist\UpdateClient `
-ZipFile .\dist\UpdateClient.zip
```
`package-client.ps1` 会检查:
- 配置文件必填字段是否完整。
- 主程序、Launcher、Updater、Bootstrap 是否存在。
- 是否混入 Debug DLL、PDB、ILK。
- 当前版本是否已有签名 Manifest 缓存。
- 是否存在重复主程序。
生成结果:
```text
dist/
UpdateClient/
UpdateClient.zip
```
Linux 最终客户端包生成方式:
```bash
cd /home/laluo/project/update-client
bash ./scripts/package-client.sh \
--source-dir /path/to/SimCAE \
--config-file /path/to/SimCAE/bin/config/app_config.json \
--output-dir ./dist/UpdateClient-linux \
--archive ./dist/UpdateClient-linux.tar.gz
```
Linux 打包脚本会检查:
- `app_config.json` 必填字段是否完整。
- 主程序、Launcher、Updater、Bootstrap 是否存在。
- 是否混入 Debug 产物。
- 当前版本是否已有签名 Manifest 缓存。
- 是否存在重复主程序。
Linux 下如果程序安装在 `/opt``/usr/local` 等普通用户不可写目录,升级器无法像 Windows UAC 那样自动提权修改安装目录。正式部署前建议二选一:
1. 把软件安装到当前用户有写权限的目录,例如用户 home 下的应用目录。
2. 由安装器创建专用目录和权限,让运行用户对软件目录有写入权限。
## 九、常见错误
1. 直接启动业务主程序提示 ticket 错误:应从 `Launcher.exe` 启动。
2. 首次启动保存配置/状态文件失败:如果目录不可写,SDK 会弹出管理员权限确认框;用户取消或当前账号没有管理员权限时仍会失败。
3. 首次启动设备登记失败:检查 `api_base_url``client_token``license_key`、服务端 License 状态。
4. 提示 License 错误或过期:在后台确认 License 是否存在、是否被禁用或删除、是否超过最大设备数。
5. 策略或 Manifest 验签失败:检查 `config/manifest_public_key.pem` 是否和服务端私钥匹配。
6. 提示 signed manifest cache missing:先在后台发布一次 `current_version` 对应版本,并让客户端拿到该版本 Manifest。
7. 升级后回滚:检查业务程序是否在 `health_check_timeout_ms` 内写入健康标记。
8. 发布失败提示主程序不在根目录:服务端 `.env``RELEASE_MAIN_EXECUTABLE` 要和平台匹配。Windows 通常是 `bin/SimCAE.exe`Linux 通常是 `bin/SimCAE`
9. 启动时提示 `无法定位程序输入点 ... Qt5*.dll`:通常是 Qt DLL 被不同版本覆盖或混用。恢复业务软件原始 Qt DLL,并重新打包 SDK;SimCAE 场景下不要使用 `-IncludeQtRuntime`