# SIMCAE 打包上传 本文站在 SIMCAE 开发者和发布人员的角度,说明拿到 Hub 更新客户端 SDK 后,SIMCAE 怎么打安装器、怎么生成 Qt IFW 交付包、怎么上传到 SimCAE Hub。 服务端部署流程见 simcae-hub 项目根目录《服务端部署.md》。普通发布人员只需要拿到已经打好的更新客户端 SDK;如果需要重新生成 SDK,见源代码仓库 `SIMCAE/update-client/updater打包成SDK.md`。 本文下面的命令默认在 SIMCAE 项目根目录执行。下面用 SIMCAE 当前放在 simcae-hub 项目里的情况举例: ```powershell cd .\SIMCAE ``` 进入后再使用相对路径,例如 `.\installer`、`.\update-client`、`.\out\build\...`。这样不要求开发者的 SIMCAE 一定放在某个固定磁盘目录。 ## 一、先理解交付物 客户端交付会涉及三类文件: | 交付物 | 给谁用 | 作用 | | --- | --- | --- | | Hub 更新客户端 SDK | SIMCAE 开发者 | 提供 `Launcher.exe`、`Updater.exe`、`Bootstrap.exe` 和必要运行库 | | Qt IFW 交付包 ZIP | 上传到 SimCAE Hub | 包含 IFW package、repository 和可选客户安装器,服务端会校验、注入配置并发布 | | 客户安装器 | 最终客户 | 客户从门户下载后双击安装,安装后得到 `maintenancetool.exe` | 正式主线是:开发者只上传一个 Qt IFW 交付包 ZIP,客户只从门户下载客户安装器。客户不需要手动改服务器地址、token 或 `app_config.json`。 ## 二、准备 Hub 更新客户端 SDK 开发者应拿到 `UpdateClientSDK-With-QtDll.zip`。 这个 ZIP 由 `update-client` 仓库的 SDK 打包脚本生成,SDK 维护者按源代码仓库 `SIMCAE/update-client/updater打包成SDK.md` 操作即可。 建议手动解压到 SIMCAE 仓库内的固定相对目录:`.\update-client\dist\UpdateClientSDK-With-QtDll`。 这里的 `With-QtDll` 表示包里带的是运行所需的 Qt DLL,不是完整 Qt SDK。 如果公司内部统一把 SDK 放在别的位置,也可以,只要后面 `$UpdateClientSdk` 指向解压后的 SDK 目录即可。 解压后至少应有: - `bin\Launcher.exe` - `bin\Updater.exe` - `bin\Bootstrap.exe` SDK 包不应该包含最终客户配置,例如 `app_config.json`、`server_config.json`、`server_config.qrc`、`manifest_public_key.pem`。这些最终配置由服务端在上传发布包时生成或注入。 ## 三、准备 SIMCAE 已编译产物 默认 SIMCAE 已经在开发机上完成 Release 编译。客户端打包文档不要求每次重新全量编译 SIMCAE,因为 SIMCAE 工程很大,打安装包时通常只需要复用已有 Release 产物。 需要确认: | 内容 | 说明 | | --- | --- | | SIMCAE Release 构建目录 | 已经存在 `SimCAE.exe`、库文件、资源文件 | | Qt Installer Framework | 已经安装 `binarycreator.exe` 和 `repogen.exe` | | DAP 运行时 | 如果启用 DAP 组件,`DAPrailCalxml` 等运行时已放在打包规则要求的位置 | | Hub 更新客户端 SDK | 已解压,并能找到 `Launcher.exe`、`Updater.exe`、`Bootstrap.exe` | 如果业务主程序启用了“必须从 Launcher 启动”的门禁,SIMCAE 编译时使用的 launch token 必须和服务端 `.env` 里的 `SIMCAE_LAUNCH_TOKEN` 一致。 ## 四、设置本次打包版本 SIMCAE 安装器文件名、IFW 组件 `package.xml` 版本、repository 里的 `Updates.xml` 版本都来自 CMake 变量 `SimCAE_Version`。这个版本默认读取 SIMCAE 仓库最近的纯数字 Git tag,例如 `1.1.3`。 先把几个容易混淆的“版本标签”分清楚: | 名称 | 写在哪里 | 谁会读取 | 作用 | | --- | --- | --- | --- | | Git tag | SIMCAE 仓库提交,例如 `git tag 1.1.3` | CMake 版本脚本 | 生成 `SimCAE_Version`,再写入安装器文件名和组件元数据 | | 组件版本 | `packages/<组件ID>/meta/package.xml` 的 `` | `repogen.exe` | 生成 repository 时写入 `Updates.xml` | | repository 组件版本 | `Updates.xml` 里的 `...` 和 `...` | `maintenancetool.exe` | 客户端判断某个组件是否需要更新 | | ZIP 文件名 | 例如 `SimCAE-Delivery-1.1.3-windows_x86_64-msvc.zip` | 人和管理后台记录 | 方便识别上传文件,不是 MaintenanceTool 的更新依据 | 所以 `git tag 1.1.3` 打的是 SIMCAE 源码提交标签,不是给 ZIP 文件打标签。它会被 CMake 读取后间接变成组件 `package.xml` 里的版本。真正决定 MaintenanceTool 是否更新的是服务器 repository 的 `Updates.xml`,而 `Updates.xml` 又来自组件自己的 `package.xml`。 正式发布时推荐在 SIMCAE 仓库给本次发布提交打纯数字 tag,再打包: ```powershell git tag 1.1.3 ``` 如果只是本机演示,不想改 SIMCAE 仓库 tag,可以临时指定本次打包版本。下面命令会创建一个本地临时脚本,让 CMake 本次配置时读到 `1.1.3`: ```powershell $Version = "1.1.3" $GitVersionShim = "..\.tmp\git-version-$Version.cmd" New-Item -ItemType Directory -Force (Split-Path $GitVersionShim) | Out-Null @" @echo off if /I "%1"=="describe" ( echo $Version exit /b 0 ) git %* "@ | Set-Content -LiteralPath $GitVersionShim -Encoding ASCII ``` 后续所有命令都复用这个 `$Version`。不要只改 ZIP 文件名,否则会出现文件名是 `1.1.3`,但组件 `package.xml` 和 `Updates.xml` 里版本还是 `0.10.1` 的错包;这种包上传后,MaintenanceTool 仍然会按 `0.10.1` 判断。 ## 五、刷新现有 CMake 打包配置 下面命令只刷新已有构建目录的 CMake 配置,用来告诉打包目标 Qt IFW 和 Hub SDK 在哪里,不是全量重新编译 SIMCAE。 先确认当前 PowerShell 已经在 SIMCAE 项目根目录。下面给出一个常见 Qt IFW 安装路径示例;如果你的 Qt IFW 装在别的位置,只改 `$QtIfwRoot` 这一行。 ```powershell $Build = ".\out\build\SimCAE-release-vs2022-qt515-ifw" $QtIfwRoot = "C:\Qt\Tools\QtInstallerFramework\4.11" $UpdateClientSdk = ".\update-client\dist\UpdateClientSDK-With-QtDll" ``` 检查关键文件: ```powershell Test-Path "$QtIfwRoot\bin\binarycreator.exe" Test-Path "$QtIfwRoot\bin\repogen.exe" Test-Path "$UpdateClientSdk\bin\Launcher.exe" Test-Path "$UpdateClientSdk\bin\Updater.exe" Test-Path "$UpdateClientSdk\bin\Bootstrap.exe" ``` 如果本次要让 `SimCAE.exe` 只能从 `Launcher.exe` 启动,先准备 SIMCAE 编译期使用的本地打包配置。这里的 `launch_token` 必须和服务端 `.env` 里的 `SIMCAE_LAUNCH_TOKEN` 完全一致。 ```powershell $LauncherProductConfig = "..\.tmp\simcae-launcher-product-config.json" @' { "app_id": "simcae", "product_code": "simcae", "app_name": "SimCAE", "launch_token": "SimCAE_Launch_Token_2026_ChangeMe_32Bytes", "license_key": "SIMCAE_LOCAL_PACKAGING_LICENSE_2026" } '@ | Set-Content -LiteralPath $LauncherProductConfig -Encoding UTF8 ``` 刷新配置: ```powershell cmake -S . -B $Build ` "-DSimCAE_QtIfwRoot=$QtIfwRoot" ` "-DSimCAE_PackageHubUpdateClient=ON" ` "-DSimCAE_HubUpdateClientSdkDir=$UpdateClientSdk" ` "-DSimCAE_UseLauncher=ON" ` "-DSimCAE_LauncherProductConfigFile=$LauncherProductConfig" ` "-DGIT_EXECUTABLE=$GitVersionShim" ``` 如果只想把 Hub 更新客户端打进安装包,但暂时不限制用户直接双击 `SimCAE.exe`,则把上面命令中的 `SimCAE_UseLauncher` 改为 `OFF`,并去掉 `SimCAE_LauncherProductConfigFile` 这一项。 ## 六、生成客户安装器和 IFW package 执行打包目标: ```powershell cmake --build $Build --config Release --target package_installer ``` 这个目标会读取 `SIMCAE\installer` 下的配置和组件规则,整理 IFW package staging,并生成安装器。组件有哪些、每个组件包含哪些文件、组件是否必选、依赖哪些组件,应该由 SIMCAE 开发者在打包配置和 `package.xml.in` 里提前定义好;SimCAE Hub 不会自动猜测业务应该拆成哪些组件。 当前示例里已有两个 IFW 组件: | 组件目录 | 组件含义 | 元数据来源 | | --- | --- | --- | | `packages/com.simcae.app` | 核心程序、Launcher、Updater、Bootstrap、核心库和通用资源 | `installer/packages/meta/package.xml.in` | | `packages/com.simcae.dap` | DAP 求解器插件及运行资源 | `installer/packages/com.simcae.dap/meta/package.xml.in` | Qt IFW 的组件 ID 来自 `packages/<组件ID>` 目录名,例如 `com.simcae.dap`。组件显示名称、版本、是否强制安装、依赖关系等来自该组件的 `meta/package.xml`,例如 ``、``、``、``。 常见输出: | 输出 | 说明 | | --- | --- | | `$Build\package` | Qt IFW package staging 目录 | | `$Build\SimCAE-<版本>-Windows-installer.exe` | 本地生成的客户安装器 | 确认核心组件里已经带上 Hub 更新客户端: ```powershell Test-Path "$Build\package\packages\com.simcae.app\data\view\bin\Launcher.exe" Test-Path "$Build\package\packages\com.simcae.app\data\view\bin\Updater.exe" Test-Path "$Build\package\packages\com.simcae.app\data\view\bin\Bootstrap.exe" Test-Path "$Build\package\packages\com.simcae.app\data\view\bin\SimCAE.exe" ``` 预期都返回 `True`。 再确认 package 里的组件版本就是本次 `$Version`: ```powershell $AppPackageXml = "$Build\package\packages\com.simcae.app\meta\package.xml" $DapPackageXml = "$Build\package\packages\com.simcae.dap\meta\package.xml" $AppVersion = ([xml](Get-Content -LiteralPath $AppPackageXml -Encoding UTF8 -Raw)).Package.Version $DapVersion = ([xml](Get-Content -LiteralPath $DapPackageXml -Encoding UTF8 -Raw)).Package.Version if ($AppVersion -ne $Version -or $DapVersion -ne $Version) { throw "组件版本不一致:app=$AppVersion dap=$DapVersion expected=$Version" } Test-Path "$Build\SimCAE-$Version-Windows-installer.exe" ``` 最后一行预期返回 `True`。如果这里不是 `True`,不要继续生成 repository。 ## 七、生成 IFW repository MaintenanceTool 读取的是 Qt IFW repository,不是客户安装器。 生成前先确认 `$Build\package` 已经存在,并且里面至少有 `config` 和 `packages`: ```powershell Test-Path "$Build\package\config\config.xml" Test-Path "$Build\package\packages" ``` 预期都返回 `True`。然后生成完整 repository: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\installer\scripts\build-ifw-repository.ps1" ` -PackageDir "$Build\package" ` -OutputDir "$Build\ifw-repository" ` -ZipFile "$Build\SimCAE-IFW-Repository-$Version-windows_x86_64-msvc.zip" ``` 这条命令不是“给压缩包打版本标签”。它只是调用 Qt IFW 的 `repogen.exe`,从 `$Build\package\packages` 读取已经准备好的组件目录和 `meta/package.xml`,生成 `Updates.xml` 和组件 `.7z` 包,最后把 repository 目录压成 ZIP。ZIP 文件名里的 `$Version` 只是为了让发布人员识别文件。 生成后检查: ```powershell Test-Path "$Build\ifw-repository\Updates.xml" Select-String -LiteralPath "$Build\ifw-repository\Updates.xml" -Pattern "com.simcae.app|com.simcae.dap|" ``` 生成的 repository 根目录必须包含 `Updates.xml`。这个 ZIP 一般不直接给客户,它是给 SimCAE Hub 后端托管,供 `maintenancetool.exe` 后续检查组件更新。 确认 repository 里的组件版本也是本次 `$Version`: ```powershell $UpdatesXml = "$Build\ifw-repository\Updates.xml" $UpdatesContent = Get-Content -LiteralPath $UpdatesXml -Encoding UTF8 -Raw if ($UpdatesContent -notmatch "$([regex]::Escape($Version))") { throw "repository Updates.xml 中没有本次版本 $Version" } ``` ## 八、组装 Qt IFW 交付包 ZIP 推荐上传给 SimCAE Hub 的是交付包 ZIP,它把 package、repository 和可选安装器放在一起。 目录结构建议: - `package/config/` - `package/packages/com.simcae.app/` - `package/packages/com.simcae.dap/` - `repository/Updates.xml` - `repository/com.simcae.app/` - `repository/com.simcae.dap/` - `installer/SimCAE-<版本>-Windows-installer.exe` 示例命令: ```powershell $PlatformKey = "windows_x86_64-msvc" $Bundle = "$Build\SimCAE-Delivery-$Version-$PlatformKey" $Installer = "$Build\SimCAE-$Version-Windows-installer.exe" if (-not (Test-Path $Installer)) { throw "缺少本次版本安装器:$Installer" } if (-not (Test-Path "$Build\ifw-repository\Updates.xml")) { throw "缺少 repository/Updates.xml,请先生成 IFW repository" } Remove-Item -LiteralPath $Bundle -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force "$Bundle" | Out-Null Copy-Item "$Build\package" "$Bundle\package" -Recurse -Force Copy-Item "$Build\ifw-repository" "$Bundle\repository" -Recurse -Force New-Item -ItemType Directory -Force "$Bundle\installer" | Out-Null Copy-Item $Installer "$Bundle\installer\" -Force Compress-Archive -Path "$Bundle\*" -DestinationPath "$Build\SimCAE-Delivery-$Version-$PlatformKey.zip" -Force ``` `release.json` 不需要开发者手写。产品编码、产品名称、版本号、通道、平台、架构和 ABI 来自管理后台表单;运行目录和主程序名由服务端从核心组件中自动识别。 ## 九、上传到管理后台 在浏览器打开管理后台,例如 `http://192.168.1.158:1798/login`。 按左侧菜单顺序准备基础数据。第一次发布某个产品时要完整走一遍;后续同产品、同通道、同平台发布新版本时,只需要确认这些数据仍然存在: 1. 产品目录:确认产品编码,例如 `simcae`。 2. 版本线:确认通道或版本线,例如 `stable`。 3. 组件管理:确认核心组件和可选组件,例如 `com.simcae.app`、`com.simcae.dap`;如果用于 MaintenanceTool 更新,后台组件编码要和 IFW package 的 `packages/<组件ID>` 目录名一致。 4. 平台管理:确认 `windows`、`x86_64`、`msvc`。 5. 产品版本:创建本次版本,例如 `1.1.3`。 6. 软件发布:创建本次发布,关联产品版本和版本线。 7. 发布包:新增或编辑发布包。 上传完整 Qt IFW 交付包时,在“发布包”页面直接点击右上角新增发布包,不需要先点击某个软件卡片。这个入口只用于新建整包更新包和 Qt IFW 交付包。 发布包页面选择: | 字段 | 建议 | | --- | --- | | 包类型 | Qt IFW 交付包 | | 文件 | `SimCAE-Delivery-<版本>-windows_x86_64-msvc.zip` | | 平台 | `windows / x86_64 / msvc` | | 状态 | 上传校验通过后变为可用 | 操作顺序: 1. 打开“发布包”页面。 2. 直接点击右上角“新建发布包”。 3. 包类型选择“Qt IFW 交付包”。此时普通新建入口只应看到“整包更新包”和“Qt IFW 交付包”。 4. 文件名填写本次交付包文件名,例如 `SimCAE-Delivery-1.1.3-windows_x86_64-msvc.zip`。 5. 选择产品、版本线、产品版本、发布和平台。 6. 保存发布包记录。 7. 点击该记录的“上传”。 8. 选择本地生成的交付包 ZIP。 9. 等待上传完成,状态应变为“可用”。 上传成功后,服务端会: - 校验 ZIP 安全路径和 IFW 结构。 - 校验 `package/` 和 `repository/Updates.xml`。 - 读取组件清单和组件版本。 - 自动注入 `config/app_config.json`。 - 自动写入必要的公钥配置。 - 发布 IFW repository。 - 生成或登记客户门户首次下载的安装器。 上传后建议立刻确认: ```powershell $Base = "http://192.168.1.158:18000/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc" (Invoke-WebRequest "$Base/Updates.xml" -UseBasicParsing).Content ``` 预期能看到本次版本号、组件 ID 和组件名称。如果这里还是旧版本,先确认发布包状态是否为“可用”,再确认上传时选择的产品、通道和平台是否一致。 ## 十、客户下载和安装 客户登录客户门户后,在下载中心下载客户安装器。客户下载到的是 `.exe` 安装器,不是 IFW repository ZIP,也不是开发者上传的交付包 ZIP。 客户安装后,安装目录中应包含: - `maintenancetool.exe` - `components.xml` - `network.xml` - `view\bin\SimCAE.exe` - `view\bin\Launcher.exe` - `view\bin\Updater.exe` - `view\bin\Bootstrap.exe` - `view\bin\config\app_config.json` 客户日常启动软件应使用 `Launcher.exe` 或安装器创建的快捷方式。组件更新、添加、移除由 `maintenancetool.exe` 负责。 首次安装建议按这个顺序检查: 1. 打开客户门户。 2. 登录有授权的客户账号。 3. 进入下载中心。 4. 找到对应产品和版本。 5. 点击下载,得到 `SimCAE-<版本>-Windows-installer.exe`。 6. 双击安装器,按页面提示完成安装。 7. 安装后进入安装目录,确认 `maintenancetool.exe`、`components.xml` 和 `view\bin\Launcher.exe` 都存在。 8. 双击 `Launcher.exe`,预期能启动 SimCAE。 9. 双击 `maintenancetool.exe`,预期能看到“添加或移除组件”“更新组件”“移除所有组件”。 ## 十一、组件更新 这里的“组件”指 `maintenancetool.exe` 里能看到的 Qt IFW 组件,例如 `com.simcae.app`、`com.simcae.dap`。组件更新就是“只发布某些组件的新版本,或新增一个组件”,让客户后续通过 MaintenanceTool 更新;它不是客户首次安装用的安装器,也不是 Launcher / Updater 用的整包更新 ZIP。 适合使用组件更新的情况: | 场景 | 应该怎么做 | | --- | --- | | 只更新 DAP 插件 | 做一个只包含 `com.simcae.dap` 的组件更新包 | | 新增示例、模板、插件等可选功能 | 做一个包含新组件的组件更新包 | | 一次更新几个互相依赖的组件 | 做一个多组件更新包,把这些组件一起放进去 | | 更新核心程序、Launcher、Updater、Bootstrap 或 `app_config.json` | 更推荐重新发完整 Qt IFW 交付包 | | 第一次发布某个产品、通道、平台 | 先发完整 Qt IFW 交付包,后面才能发组件更新 | 组件更新包不是单独飘在系统外面的文件。它上传时必须挂到某个产品、某个产品版本、某次发布、某个平台下面。服务端会把它合并到这个产品对应通道和平台的 current repository 中。 ### 11.1 组件版本怎么定 组件版本以组件自己的 `package.xml` 为准。比如 DAP 组件的版本写在这里: - `$Build\package\packages\com.simcae.dap\meta\package.xml` - XML 节点是 `1.1.4` 如果说“组件标签”,这里真正参与更新判断的是组件 ID 和组件版本:组件 ID 来自目录名 `packages/com.simcae.dap`,组件版本来自 `meta/package.xml` 里的 ``。开发者在维护 IFW package 时就应该把组件拆分、显示名、版本、必选状态和依赖关系写清楚。`repogen.exe` 生成 repository 时,会把这些信息写进 `Updates.xml`。MaintenanceTool 也是根据 `Updates.xml` 里的组件版本判断是否可更新。 当前 SIMCAE 全量打包默认会让所有组件跟随同一个 `$Version`,这个 `$Version` 来自 SIMCAE 仓库的纯数字 Git tag,或者前面文档里的 `$GitVersionShim` 临时版本脚本。例如 `$Version = "1.1.3"` 时,`com.simcae.app` 和 `com.simcae.dap` 默认都会变成 `1.1.3`。 如果只更新 DAP,不更新核心组件,规则是: 1. 服务器当前 `com.simcae.app` 是 `1.1.3`,`com.simcae.dap` 是 `1.1.3`。 2. 本次只把 `com.simcae.dap` 的 `package.xml` 改成 `1.1.4`。 3. 不改 `com.simcae.app` 的 `package.xml`,它仍然保持 `1.1.3`。 4. 生成只包含 `com.simcae.dap` 的组件更新包。 5. 上传后,服务器 current repository 里应变成 `com.simcae.app=1.1.3`、`com.simcae.dap=1.1.4`。 正式流程里,建议 SIMCAE 打包侧给每个组件提供独立版本参数。当前如果只是本地演示,可以在 `$Build\package\packages\<组件ID>\meta\package.xml` 里调整目标组件的 ``,然后再生成组件更新包。不要改不更新的组件版本,也不要只改 ZIP 文件名。ZIP 名字里写了 `1.1.4`,但 `package.xml` 仍是 `1.1.3` 时,生成出来的 `Updates.xml` 也会是 `1.1.3`。 ### 11.2 先确认服务器已有当前仓库 以 `simcae / stable / windows_x86_64-msvc` 为例: ```powershell $Base = "http://192.168.1.158:18000/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc" (Invoke-WebRequest "$Base/Updates.xml" -UseBasicParsing).Content ``` 预期能看到当前仓库的组件,例如: - `com.simcae.app` - `1.1.3` - `com.simcae.dap` - `1.1.3` 如果这里访问失败,先不要上传组件更新包,说明服务器还没有这个产品、通道、平台的 current repository。 ### 11.3 准备本地组件产物 开发者先按 SIMCAE 自己的规则把组件文件准备到 IFW package staging 里。组件边界应该在开发和打包配置阶段就已经分好;后面的 repository 生成命令只是读取这些组件,不会自动分析文件并替开发者拆组件。当前打包目标会把组件整理到: - `$Build\package\packages\com.simcae.app` - `$Build\package\packages\com.simcae.dap` 如果只更新 DAP 插件,先确认 DAP 组件目录存在: ```powershell Test-Path "$Build\package\packages\com.simcae.dap\meta\package.xml" Test-Path "$Build\package\packages\com.simcae.dap\data" ``` 预期都返回 `True`。同时要确认 `package.xml` 里的版本已经升高: ```powershell Select-String -LiteralPath "$Build\package\packages\com.simcae.dap\meta\package.xml" -Pattern "" ``` 例如服务器当前 DAP 是 `1.1.3`,本次 DAP 组件更新包应改成 `1.1.4` 或更高。 ### 11.4 生成组件更新 ZIP 使用 `-Include` 只把要更新的组件打进 repository。`-Include "com.simcae.dap"` 里的值是组件 ID,也就是 `packages/com.simcae.dap` 这个目录名;它不是 ZIP 标签,也不是版本号。下面以只更新 DAP 为例: ```powershell $ComponentVersion = "1.1.4" $PlatformKey = "windows_x86_64-msvc" $ComponentUpdateRepository = "$Build\ifw-component-update-com.simcae.dap-$ComponentVersion" $ComponentUpdateZip = "$Build\SimCAE-IFW-ComponentUpdate-com.simcae.dap-$ComponentVersion-$PlatformKey.zip" Remove-Item -LiteralPath $ComponentUpdateRepository -Recurse -Force -ErrorAction SilentlyContinue powershell -NoProfile -ExecutionPolicy Bypass -File ".\installer\scripts\build-ifw-repository.ps1" ` -PackageDir "$Build\package" ` -OutputDir $ComponentUpdateRepository ` -ZipFile $ComponentUpdateZip ` -Include "com.simcae.dap" ``` 这条命令不会修改 `com.simcae.dap` 的 ``。它只是根据 `-Include` 选择已有组件,把该组件当前 `package.xml` 中写好的版本、显示名和依赖交给 `repogen.exe`,再生成本次组件更新 repository ZIP。 如果一次更新多个组件: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\installer\scripts\build-ifw-repository.ps1" ` -PackageDir "$Build\package" ` -OutputDir "$Build\ifw-component-update-multi-$ComponentVersion" ` -ZipFile "$Build\SimCAE-IFW-ComponentUpdate-multi-$ComponentVersion-$PlatformKey.zip" ` -Include "com.simcae.app","com.simcae.dap" ``` 生成后检查 ZIP 对应的展开目录: ```powershell Test-Path "$ComponentUpdateRepository\Updates.xml" Get-ChildItem -LiteralPath $ComponentUpdateRepository Select-String -LiteralPath "$ComponentUpdateRepository\Updates.xml" -Pattern "com.simcae.dap||" ``` 单 DAP 更新包的典型结构应类似: - `Updates.xml` - `com.simcae.dap/1.1.4meta.7z` - `com.simcae.dap/1.1.4view.7z` - `com.simcae.dap/1.1.4view.7z.sha1` 如果 ZIP 解开后外面多套了一层目录,也可以上传;服务端会识别常见外层目录。但推荐让 ZIP 根部直接就是 `Updates.xml` 和组件目录,最不容易出错。 ### 11.5 上传组件更新 管理后台操作: 1. 打开“产品版本”,创建本次发布批次版本,例如 `1.1.4`。 2. 打开“软件发布”,创建本次发布,通道仍选择 `stable`。 3. 打开“发布包”,先点击要更新的软件卡片,进入该软件的发布包视图。 4. 点击“新建组件更新包”。进入某个软件后,包类型固定为“组件更新”,产品固定为当前软件。 5. 发布选择刚创建的 `1.1.4` 发布。 6. 平台选择和 current repository 完全一致的 `windows / x86_64 / msvc`。 7. 文件名填写 `SimCAE-IFW-ComponentUpdate-com.simcae.dap-1.1.4-windows_x86_64-msvc.zip`。 8. 保存后点击“上传”。 9. 选择上一步生成的 `$ComponentUpdateZip`。 10. 上传成功后,发布包状态应变为“可用”。 上传成功后,服务端会把更新组件合并进 current repository,未变化组件保持不变。 ### 11.6 上传后确认合并结果 重新读取服务器仓库: ```powershell $Base = "http://192.168.1.158:18000/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc" (Invoke-WebRequest "$Base/Updates.xml" -UseBasicParsing).Content ``` 预期: 1. 更新过的组件版本变成新版本,例如 `com.simcae.dap` 是 `1.1.4`。 2. 未更新的组件仍然存在,例如 `com.simcae.app` 还在。 3. 新增组件能出现在 `Updates.xml` 中。 4. 旧版本仓库仍保存在服务端 `releases/<版本>` 目录中,current 指向最新合并结果。 ### 11.7 常见失败提示 | 提示含义 | 原因 | 处理 | | --- | --- | --- | | 当前产品、通道和平台下还没有可合并的 IFW 当前仓库 | 还没上传过完整交付包 | 先上传完整 Qt IFW 交付包 | | 组件版本不能倒退或重复 | 上传组件版本小于或等于服务器 current 版本 | 升高组件 `package.xml` 里的版本后重新生成 | | 组件依赖不存在 | 新组件依赖的组件不在 current 仓库,也不在本次包里 | 先发布依赖组件,或把依赖组件一起打进本次更新包 | | 缺少组件目录 | `Updates.xml` 声明了组件,但 ZIP 里没有对应目录 | 重新用 `build-ifw-repository.ps1` 生成 | | 包含未在 `Updates.xml` 声明的组件目录 | ZIP 里多了未声明目录 | 删除多余目录后重新压包 | | 未包含 `Updates.xml` | 上传的不是 repository ZIP,或服务端没有配置 `SIMCAE_IFW_REPOGEN_PATH` 来从 packages 自动生成 | 上传 repository 形态 ZIP | 更新包失败时,服务端不会破坏原 current repository。 ## 十二、换源 服务器地址变化时有两种处理方式: | 方式 | 适用场景 | | --- | --- | | 临时换源命令 | 单台客户机器临时切到新仓库 | | RepositoryUpdate 批量换源 | 已安装客户软件批量迁移仓库地址 | 换源只影响 `maintenancetool.exe` 访问 IFW repository。Launcher / Updater 的 API 地址来自服务端注入的 `app_config.json`,需要通过新发布包或重新安装包更新。 ### 12.1 临时换源 临时换源适合开发、测试、临时排查。它不会永久改安装包里的默认源。 在 SIMCAE 项目根目录执行,假设客户软件安装在 `.tmp\maintenance-installed\SimCAE`: ```powershell $Install = ".\.tmp\maintenance-installed\SimCAE" $Repo = "http://192.168.1.158:18000/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc/" powershell -NoProfile -ExecutionPolicy Bypass -File ".\installer\scripts\switch-maintenance-repository.ps1" ` -MaintenanceToolPath "$Install\maintenancetool.exe" ` -RepositoryUrl $Repo ` -Mode Temp ` -Command check-updates ` -ClearCache ``` 注意 `$Repo` 必须是仓库根地址,不能写到 `Updates.xml`: - 正确:`http://192.168.1.158:18000/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc/` - 错误:`http://192.168.1.158:18000/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc/Updates.xml` ### 12.2 批量换源 批量换源适合服务器域名或 IP 变更。做法是在下一次 repository 的 `Updates.xml` 里加入 `RepositoryUpdate`,让 MaintenanceTool 更新组件时顺便替换本机源地址。 示例:把旧源 `http://192.168.1.158:18000/...` 替换为新源 `https://download.simcae.example.com/...`: ```powershell $UpdatesXml = "$Build\ifw-repository\Updates.xml" $OldRepo = "http://192.168.1.158:18000/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc/" $NewRepo = "https://download.simcae.example.com/api/v1/client/ifw/repositories/simcae/stable/windows_x86_64-msvc/" powershell -NoProfile -ExecutionPolicy Bypass -File ".\installer\scripts\write-repository-update.ps1" ` -UpdatesXml $UpdatesXml ` -Action replace ` -OldUrl $OldRepo ` -NewUrl $NewRepo ` -DisplayName "SimCAE stable component repository" ` -ClearExisting ``` 写入后重新压 repository 或重新组装 Qt IFW 交付包,再上传到 SimCAE Hub。客户下一次通过 MaintenanceTool 检查或更新组件后,会把仓库地址换成新地址。 ## 十三、常见问题 | 现象 | 原因和处理 | | --- | --- | | 上传提示缺少 `Updates.xml` | 选择的不是 repository 或交付包结构不对 | | MaintenanceTool 看不到更新 | 服务器仓库版本没有高于本机 `components.xml` 里的版本 | | 客户下载不到安装器 | 发布包不是 Qt IFW 交付包,或客户安装器生成/登记失败 | | Launcher 启动主程序失败 | `SIMCAE_LAUNCH_TOKEN` 和业务主程序编译时 token 不一致 | | 直接双击 `SimCAE.exe` 被拦截 | 这是启用 Launcher 启动门禁后的预期行为 | | 可选组件删除后 Updater 报缺文件 | Manifest 中可选组件文件没有标为可选,或组件边界划分不对 |