Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clinerules/01-generalupdate-init.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SetSource(url, key) -> SetOption(AppType.Client) -> LaunchAsync()

## 4 大场景: None/UpgradeOnly/MainOnly/Both

## manifest.json 字段
## generalupdate.manifest.json 字段
MainAppName, UpdateAppName, ProductId, InstallPath, UpdatePath, ClientVersion, UpgradeClientVersion

## 关键: UpgradeApp须随首个版本发布, 双进程AppSecretKey一致, 版本4段式
14 changes: 7 additions & 7 deletions .clinerules/05-generalupdate-troubleshoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ tags: ["dotnet", "generalupdate"]

## 诊断流程 (5步)

1. **版本号** — `manifest.json` 的 `ClientVersion` / `UpgradeClientVersion` 是否与服务端返回的版本一致? 4段式版本(string)严格匹配。
1. **版本号** — `generalupdate.manifest.json` 的 `ClientVersion` / `UpgradeClientVersion` 是否与服务端返回的版本一致? 4段式版本(string)严格匹配。
2. **manifest 字段** — `MainAppName`、`UpdateAppName`、`ProductId`、`InstallPath`、`UpdatePath` 是否填写正确且与服务端一致?
3. **UpgradeApp 存在** — 主应用目录下是否存在 `Upgrade` 子目录及 `Upgrade.exe` (或对应平台可执行文件)? 随首个版本一同发布的。
4. **策略可访问** — `SetSource(url, key)` 配置的 API/OSS 端点能否正常响应? OSS 策略需要 `SetOSSInfo` 配置正确。
Expand All @@ -29,7 +29,7 @@ tags: ["dotnet", "generalupdate"]
|------|------|-----------|
| **升级没启动** | `LaunchAsync()` 未调用 / 调用顺序错误 / 主进程未检测到新版本 | 确认 `Bootstrap.LaunchAsync()` 在 `Main()` 中调用, 且 `SetOption(AppType.Client)` 正确 |
| **Method not found** | 引用的 GeneralUpdate 版本与服务端不匹配 / 混用不同大版本 | 统一 NuGet 版本, 清理 bin/obj 后重新生成 |
| **路径超长 (>260)** | Windows 路径限制, `InstallPath` / `UpdatePath` 过深 | 缩短安装路径; 或启用 `SetOption` 中的长路径支持 (`LongPathSupport = true`) |
| **路径超长 (>260)** | Windows 路径限制, `InstallPath` / `UpdatePath` 过深 | 缩短安装路径; 或在项目配置中启用长路径支持 (`app.manifest` / `runtimeconfig`) |
| **IPC 暴露** | EncryptedFile IPC 写入明文 / NamedPipe 未鉴权 | 启用加密传输; NamedPipe 设置 `AccessControl` 限制连接用户; 不使用 `AutoFallback` 回退到明文 |
| **跨租户泄露** | 多租户部署共用 `ProductId` / `AppSecretKey` | 每个租户分配独立 `ProductId` 和 `AppSecretKey`; 服务端鉴权时校验租户标识 |

Expand All @@ -38,7 +38,7 @@ tags: ["dotnet", "generalupdate"]
| 问题 | 原因 | 排查/解决 |
|------|------|-----------|
| **静默更新不生效** | `UpdateMode.Silent` 配置缺失 / 权限不足无法后台启动 | 确认 `SetOption(UpdateMode.Silent)` 且应用有后台运行权限; 检查 `LaunchCommand` 参数是否正确传递 |
| **无限循环更新** | `ClientVersion` 与服务端最新版本不一致导致不断检测到"新版本" | 验证更新后是否更新了本地 `manifest.json` 的版本号; 确认服务端已标记该版本为"已发布"状态 |
| **无限循环更新** | `ClientVersion` 与服务端最新版本不一致导致不断检测到"新版本" | 验证更新后是否更新了本地 `generalupdate.manifest.json` 的版本号; 确认服务端已标记该版本为"已发布"状态 |
| **OSS 无更新** | Bucket 配置错误 / 文件命名不匹配 / 签名过期 | `curl` 测试 OSS URL 是否直接可下载; 确认 `SetOSSInfo` 的 endpoint/bucket/region 正确 |
| **文件占用** | 更新时目标文件正在被使用 (主进程/杀软/搜索索引) | 关闭主进程后更新; 使用 `Bowl` 守护进程管理进程生命周期; 排除杀软扫描目录 |

Expand All @@ -50,9 +50,9 @@ tags: ["dotnet", "generalupdate"]
| **差分更新失败** | BSDIFF 补丁应用时数据不完整 / 旧版本基准文件不匹配 | 校验原始文件哈希; 确认差分策略仅在同源版本间使用 |
| **升级后配置丢失** | `UpdatePath` 未包含配置文件 / 更新后未合并 `appsettings.json` | 在更新流程中添加配置文件合并步骤; 或使用外部配置中心 |
| **信号量泄露** | IPC 信号量未正确释放, 重启后端口/资源被占用 | 重启系统; 检查 `Bowl` 是否正确清理资源; 监控信号量计数 |
| **ZIP 遍历写入** | 恶意更新的 ZIP 包包含 `../` 路径覆盖系统文件 | 使用 `ZipSecurity` 校验项, 拒绝包含 `../` 或绝对路径的条目 |
| **ZIP 遍历写入** | 恶意更新的 ZIP 包包含 `../` 路径覆盖系统文件 | 解压时校验 ZIP 条目路径, 拒绝包含 `../` 或绝对路径的条目 |
| **AOT 编译失败** | 动态加载 / 反射代码未适配 NativeAOT | 添加 `[DynamicDependency]` 特性; 避免 `Assembly.Load` 等动态加载 |
| **SignalR 重连慢** | WebSocket 长连接断开后 `RetryDelay` 配置过长 | 调整 `SignalRRetryPolicy` 的 `RetryDelay` 和 `MaxRetryCount` |
| **SignalR 重连慢** | WebSocket 长连接断开后 `RetryDelay` 配置过长 | 降低重试延迟并限制最大重试次数, 或自定义重连策略 |
| **SSL 证书错误** | 自签名证书未受信 / 服务器 SNI 不匹配 | 配置 `SslValidationPolicy`; 或添加自定义证书验证委托 |

## L级 (Low) — 非关键
Expand All @@ -69,7 +69,7 @@ tags: ["dotnet", "generalupdate"]

```bash
# 测试 API 是否可访问
curl -v "http://your-server/api/update/check?appName=MyApp&clientVersion=1.0.0.1"
curl -v -X POST "http://your-server/Upgrade/Verification" -H "Content-Type: application/json" -d '{"appKey":"xxx","appType":1,"clientVersion":"1.0.0.1","productId":"xxx","platform":"windows","tenantId":"default"}'

# 验证 manifest.json 内容
cat /path/to/app/manifest.json | jq .
Expand Down Expand Up @@ -98,6 +98,6 @@ powershell "Get-CimInstance Win32_Semaphore | Select-Object Name, Count"
## 安全注意事项

- IPC 传输 — 不使用明文 `AutoFallback`, 始终启用加密
- ZIP 包校验 — 启用 `ZipSecurity` 拒绝路径穿越条目
- ZIP 包校验 — 解压时校验 ZIP 条目路径, 拒绝 `../` 和绝对路径条目
- 签名验证 — 程序集签名: 在 `SetSource` 中提供公钥, 拒绝未签名更新
- AppSecretKey 管理 — 硬编码在客户端是最后手段; 优先从启动参数或环境变量注入
6 changes: 3 additions & 3 deletions .cursor/rules/generalupdate-init.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ SetSource(url, key) -> SetOption(AppType.Client) -> LaunchAsync()
## 4 大更新场景
- None, UpgradeOnly, MainOnly, Both

## manifest.json
## generalupdate.manifest.json
MainAppName, UpdateAppName, ProductId, InstallPath, UpdatePath, ClientVersion, UpgradeClientVersion

## 关键警告
- UpgradeApp.exe 必须随首个版本发布(不能自己下载自己)
- Client 和 Upgrade 必须相同 AppSecretKey
- 版本号必须是 4 段式
- manifest ClientVersion 不能为空
- generalupdate.manifest.json ClientVersion 不能为空

## 3 种配置方式
- Minimal: SetSource + manifest.json
- Minimal: SetSource + generalupdate.manifest.json
- Standard: SetConfig(UpdateRequest) + Option + events
- appsettings: LoadFromConfiguration(config)

Expand Down
2 changes: 1 addition & 1 deletion .github/instructions/backend-api.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ applyTo: "**/*.cs"
## Verification Endpoint
POST /Upgrade/Verification
Request: appKey, appType, clientVersion, productId, platform, tenantId
Response: body[{ id, version, url, hash, size, name, appType, isCrossVersion }]
Response: { code, message, body: [{ id, version, url, hash, size, name, appType, isCrossVersion }] }

## Report Endpoint
POST /Upgrade/Report
Expand Down
4 changes: 2 additions & 2 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- IPC: Encrypted file(AES, default), NamedPipe, SharedMemory, AutoFallback

## Bootstrap
- Minimal: SetSource(url, key) -> SetOption(Client) -> LaunchAsync()
- Minimal: SetSource(url, key) -> SetOption(AppType.Client) -> LaunchAsync()
- Standard: SetConfig(UpdateRequest) -> SetOption() -> AddListener*() -> LaunchAsync()
- From config: LoadFromConfiguration(config.GetSection("GeneralUpdate"))

Expand Down Expand Up @@ -46,7 +46,7 @@ Hooks, Strategy, UpdateReporter, DownloadSource, DownloadOrchestrator, DownloadP

## Diagnostics
1. NuGet versions match
2. manifest.json valid
2. generalupdate.manifest.json valid
3. UpgradeApp.exe exists
4. Server API reachable
5. Logs in Logs/generalupdate-trace *.log