feat: 优化客户端配置和跨平台支持
This commit is contained in:
@@ -3,6 +3,7 @@ project(Bootstrap LANGUAGES CXX)
|
||||
add_executable(Bootstrap
|
||||
main.cpp
|
||||
${CMAKE_SOURCE_DIR}/i18n/update-client.qrc
|
||||
${CMAKE_SOURCE_DIR}/config/server_config.qrc
|
||||
)
|
||||
target_compile_features(Bootstrap PRIVATE cxx_std_17)
|
||||
target_link_libraries(Bootstrap PRIVATE Qt5::Core Qt5::Widgets)
|
||||
|
||||
+59
-2
@@ -43,6 +43,8 @@ const QString kRegistrySourceHashKey = QStringLiteral("source_sha256");
|
||||
const QString kRegistrySourcePathKey = QStringLiteral("source_path");
|
||||
const QString kRegistryRuntimeRootKey = QStringLiteral("runtime_root");
|
||||
const QString kRegistryImportedAtKey = QStringLiteral("imported_at_utc");
|
||||
const QString kEmbeddedServerConfigPath = QStringLiteral(":/simcae/server_config.json");
|
||||
const QString kApiBaseUrlKey = QStringLiteral("api_base_url");
|
||||
|
||||
QString encodeArgument(const QString& value)
|
||||
{
|
||||
@@ -169,6 +171,22 @@ bool writeConfigValueToFile(const QString& configPath, const QString& key,
|
||||
return writeBytesToFile(configPath, payload, errorMessage);
|
||||
}
|
||||
|
||||
bool isRegistryManagedConfigKey(const QString& key)
|
||||
{
|
||||
return key != kApiBaseUrlKey;
|
||||
}
|
||||
|
||||
QJsonObject registryManagedConfigObject(const QJsonObject& source)
|
||||
{
|
||||
QJsonObject result;
|
||||
for (auto it = source.constBegin(); it != source.constEnd(); ++it)
|
||||
{
|
||||
if (isRegistryManagedConfigKey(it.key()))
|
||||
result.insert(it.key(), it.value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString windowsErrorMessage(DWORD errorCode)
|
||||
{
|
||||
@@ -421,6 +439,7 @@ ConfigHelper::ConfigHelper()
|
||||
QCryptographicHash::Sha256).toHex());
|
||||
migrateLegacyIniIfNeeded();
|
||||
syncRegistryFromConfigFileIfChanged();
|
||||
removeRegistryValue(kApiBaseUrlKey);
|
||||
qDebug() << "Loading app config path:" << m_configPath;
|
||||
qDebug() << "File exists?" << QFile::exists(m_configPath);
|
||||
qDebug() << "Registry installation id:" << m_registryInstallId;
|
||||
@@ -492,7 +511,8 @@ bool ConfigHelper::syncRegistryFromConfigFileIfChanged()
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray normalizedConfig = QJsonDocument(document.object()).toJson(QJsonDocument::Compact);
|
||||
const QJsonObject config = registryManagedConfigObject(document.object());
|
||||
const QByteArray normalizedConfig = QJsonDocument(config).toJson(QJsonDocument::Compact);
|
||||
const QString sourceHash = QString::fromLatin1(
|
||||
QCryptographicHash::hash(normalizedConfig, QCryptographicHash::Sha256).toHex());
|
||||
|
||||
@@ -506,7 +526,6 @@ bool ConfigHelper::syncRegistryFromConfigFileIfChanged()
|
||||
if (previousHash == sourceHash)
|
||||
return true;
|
||||
|
||||
const QJsonObject config = document.object();
|
||||
if (config.isEmpty())
|
||||
{
|
||||
settings.beginGroup(kRegistryMetaGroup);
|
||||
@@ -614,6 +633,35 @@ bool ConfigHelper::sanitizeConfigFileAfterImport(QSettings& settings)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigHelper::removeRegistryValue(const QString& key) const
|
||||
{
|
||||
QSettings settings(QSettings::NativeFormat, QSettings::UserScope,
|
||||
kRegistryOrganization, kRegistryApplication);
|
||||
enterRegistryGroup(settings);
|
||||
settings.beginGroup(kRegistryConfigGroup);
|
||||
settings.remove(key);
|
||||
settings.endGroup();
|
||||
settings.sync();
|
||||
return settings.status() == QSettings::NoError;
|
||||
}
|
||||
|
||||
QString ConfigHelper::readEmbeddedValue(const QString& key) const
|
||||
{
|
||||
if (key != kApiBaseUrlKey)
|
||||
return QString();
|
||||
|
||||
QFile file(kEmbeddedServerConfigPath);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return QString();
|
||||
|
||||
QJsonParseError error;
|
||||
const QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &error);
|
||||
if (error.error != QJsonParseError::NoError || !document.isObject())
|
||||
return QString();
|
||||
|
||||
return document.object().value(key).toVariant().toString().trimmed();
|
||||
}
|
||||
|
||||
bool ConfigHelper::readRegistryValue(const QString& key, QString* value) const
|
||||
{
|
||||
QSettings settings(QSettings::NativeFormat, QSettings::UserScope,
|
||||
@@ -653,6 +701,9 @@ bool ConfigHelper::writeRegistryValue(const QString& key, const QString& value)
|
||||
|
||||
QString ConfigHelper::readFileValue(const QString& key) const
|
||||
{
|
||||
if (!isRegistryManagedConfigKey(key))
|
||||
return QString();
|
||||
|
||||
QFile file(m_configPath);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return QString();
|
||||
@@ -668,6 +719,12 @@ QString ConfigHelper::readFileValue(const QString& key) const
|
||||
QString ConfigHelper::getValue(const QString& section, const QString& key) const
|
||||
{
|
||||
Q_UNUSED(section);
|
||||
const QString embeddedValue = readEmbeddedValue(key);
|
||||
if (!embeddedValue.isEmpty())
|
||||
return embeddedValue;
|
||||
if (!isRegistryManagedConfigKey(key))
|
||||
return QString();
|
||||
|
||||
QString value;
|
||||
if (readRegistryValue(key, &value))
|
||||
return value;
|
||||
|
||||
@@ -29,6 +29,8 @@ private:
|
||||
void enterRegistryGroup(QSettings& settings) const;
|
||||
bool syncRegistryFromConfigFileIfChanged();
|
||||
bool sanitizeConfigFileAfterImport(QSettings& settings);
|
||||
bool removeRegistryValue(const QString& key) const;
|
||||
QString readEmbeddedValue(const QString& key) const;
|
||||
bool readRegistryValue(const QString& key, QString* value) const;
|
||||
bool writeRegistryValue(const QString& key, const QString& value);
|
||||
QString readFileValue(const QString& key) const;
|
||||
|
||||
@@ -38,7 +38,7 @@ bool DeviceIdentityHelper::ensureIssued(const QString& base,const QString& token
|
||||
const QString trimmedBase=base.trimmed();
|
||||
if(appId.trimmed().isEmpty()){m_error="app_id is empty in config/app_config.json";return false;}
|
||||
if(channel.trimmed().isEmpty()){m_error="channel is empty in config/app_config.json";return false;}
|
||||
if(trimmedBase.isEmpty()||trimmedBase.contains("YOUR_SERVER_IP",Qt::CaseInsensitive)){m_error="api_base_url is not configured. Set it to the update server address, for example http://192.168.229.128:8000";return false;}
|
||||
if(trimmedBase.isEmpty()||trimmedBase.contains("YOUR_SERVER_IP",Qt::CaseInsensitive)){m_error="api_base_url is not configured. Set config/server_config.json before building the client, for example http://192.168.229.128:8000";return false;}
|
||||
if(token.trimmed().isEmpty()){m_error="client_token is empty in config/app_config.json";return false;}
|
||||
if(licenseKey.trimmed().isEmpty()){m_error="license_key is empty. Create a License in the admin page and paste the generated key into config/app_config.json";return false;}
|
||||
ConfigHelper& config=ConfigHelper::instance();
|
||||
|
||||
@@ -44,6 +44,7 @@ bash ./scripts/package-sdk.sh --source-dir ./out/linux/bin --output-dir ./dist/U
|
||||
==============
|
||||
|
||||
config/app_config.json 是部署配置源文件。Launcher / Updater / MainApp 启动时会把它同步到当前用户的 QSettings 配置区;Windows 下对应注册表,Linux 下对应用户配置文件。后续运行配置优先从 QSettings 读取。
|
||||
config/server_config.json 是编译期服务端地址配置源文件。它通过 config/server_config.qrc 编进 Launcher / Updater / MainApp,不写入 app_config.json,也不写入注册表。修改 api_base_url 后必须重新编译客户端程序才会生效。
|
||||
如果 config/app_config.json 内容被修改,下一次启动时会按解析后的 JSON 内容 SHA256 判断变化并重新导入注册表。
|
||||
非空 app_config.json 成功导入注册表后会自动清空为 {},文件保留不删除,方便下次直接粘贴管理后台生成的新配置。
|
||||
Windows 注册表位置:HKEY_CURRENT_USER\Software\Marsco\UpdateClientSDK\installations\<安装目录SHA256>\config。
|
||||
@@ -55,10 +56,11 @@ Linux 配置位置由 Qt QSettings 决定,通常在当前用户 home 目录的
|
||||
接入新软件时通常需要修改:
|
||||
|
||||
1. app_id、app_name、channel、current_version。
|
||||
2. api_base_url、client_token、license_key、launch_token。
|
||||
3. main_executable:团队业务主程序文件名。
|
||||
4. launcher_executable、updater_executable、bootstrap_executable。
|
||||
5. health_check_timeout_ms:升级后等待业务程序健康确认的毫秒数,最小 1000。
|
||||
2. client_token、license_key、launch_token。
|
||||
3. config/server_config.json 里的 api_base_url。
|
||||
4. main_executable:团队业务主程序文件名。
|
||||
5. launcher_executable、updater_executable、bootstrap_executable。
|
||||
6. health_check_timeout_ms:升级后等待业务程序健康确认的毫秒数,最小 1000。
|
||||
|
||||
Windows 完整格式参考 update-client/config/app_config.example.json;Linux 完整格式参考 update-client/config/app_config.linux.example.json。
|
||||
运行时生成的 client_identity.dat、local_state.json 等文件不得打入通用 SDK 模板。app_config.json 可以作为部署模板,但不要把某台机器运行后产生的临时状态混进去。
|
||||
@@ -67,7 +69,7 @@ Windows 发布打包:
|
||||
|
||||
1. 使用 Release 配置编译全部客户端程序。
|
||||
2. 先完成当前版本在线校验,确认 out/bin/update/manifest_cache 中存在对应的签名 Manifest。
|
||||
3. 准备一份实际 app_config.json,确认其中包含正确的 License Key、当前版本和业务程序名。
|
||||
3. 准备一份实际 app_config.json,确认其中包含正确的 License Key、当前版本和业务程序名;确认客户端程序已用正确的 config/server_config.json 编译。
|
||||
4. 在 PowerShell 执行:
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\package-client.ps1 -ConfigFile .\config\app_config.json
|
||||
5. 输出位于 dist/UpdateClient 和 dist/UpdateClient.zip。
|
||||
|
||||
+17
-7
@@ -8,7 +8,7 @@
|
||||
| --- | --- |
|
||||
| 重新生成给别人用的 SDK 包 | 二、维护者:生成 SDK 包 |
|
||||
| 把 SDK 放到 SimCAE 或其他业务软件目录 | 三、你:把 SDK 放进业务软件目录 |
|
||||
| 从后台生成 `app_config.json` | 四、你:生成并填写 app_config.json |
|
||||
| 从后台生成 `app_config.json` 和 qrc 服务端配置 | 四、你:生成并填写客户端配置 |
|
||||
| 给业务主程序接入启动保护代码 | 五、你:业务主程序接入要求 |
|
||||
| 验证升级、回滚、健康检查 | 六、你:联调测试 |
|
||||
| 生成最终交付给用户的客户端包 | 七、维护者:生成最终客户端包 |
|
||||
@@ -23,6 +23,7 @@ SDK 核心程序:
|
||||
- `Updater.exe` / `Updater`:下载、校验、备份、安装、健康确认、提交或回滚。
|
||||
- `Bootstrap.exe` / `Bootstrap`:处理运行中可能被占用的 EXE/DLL 或 Linux 可执行文件替换。
|
||||
- `config/app_config.json`:部署配置源文件。启动时会同步到当前用户的 QSettings 配置区;Windows 下对应注册表,Linux 下对应用户配置文件。
|
||||
- `config/server_config.json`:编译期服务端地址配置源文件,通过 `config/server_config.qrc` 编进 Launcher / Updater / MainApp,不写入 `app_config.json` 或注册表。
|
||||
- `config/manifest_public_key.pem`:Manifest 签名公钥,用来验证服务端发布包没有被篡改。
|
||||
|
||||
## 二、维护者:生成 SDK 包
|
||||
@@ -178,7 +179,7 @@ D:\SimCAE_SDK\scripts\install-sdk.ps1 `
|
||||
|
||||
注意:SimCAE 自己已经带有 Qt 运行库。SDK 的 Launcher/Updater 应复用 SimCAE 的 `Qt5*.dll`、`platforms/`、`imageformats/` 等目录。不要把另一套 Qt DLL 覆盖到 `SimCAE\bin`,否则可能出现“无法定位程序输入点”一类错误。
|
||||
|
||||
## 四、你:生成并填写 app_config.json
|
||||
## 四、你:生成并填写客户端配置
|
||||
|
||||
最推荐的方式是在服务端管理后台生成客户端配置:
|
||||
|
||||
@@ -188,11 +189,13 @@ D:\SimCAE_SDK\scripts\install-sdk.ps1 `
|
||||
4. 创建 License。
|
||||
5. 在“客户端配置生成”区域选择应用、渠道、License 和主程序名。
|
||||
6. 点击生成配置。
|
||||
7. 复制生成的 JSON,覆盖 `D:\SimCAE\bin\config\app_config.json`。
|
||||
7. 复制“客户端 app_config.json”,覆盖 `D:\SimCAE\bin\config\app_config.json`。
|
||||
8. 复制“qrc 服务端配置 server_config.json”,覆盖 `update-client\config\server_config.json`,然后重新编译 Launcher / Updater / Bootstrap。这个文件会被 `config/server_config.qrc` 编进程序,不会放进用户机器的 `app_config.json` 或注册表。
|
||||
|
||||
配置同步规则:
|
||||
|
||||
- `app_config.json` 是部署配置源文件,适合交付、复制、人工修改。
|
||||
- `api_base_url` 不再属于 `app_config.json` 字段。它只存在于 `config/server_config.json`,并通过 qrc 编进程序。
|
||||
- Launcher / Updater / MainApp 启动时会计算 `app_config.json` 解析后的 JSON 内容 SHA256;如果 JSON 内容和上次导入时不同,就把文件里的配置重新写入当前 Windows 用户的注册表。
|
||||
- 后续运行时优先从注册表读取配置,不再每次直接读 JSON。
|
||||
- 运行过程中产生的动态值,例如首次输入的 `license_key`、服务端返回的 `device_id`、升级后的 `current_version`,会写入注册表。
|
||||
@@ -213,7 +216,6 @@ D:\SimCAE_SDK\scripts\install-sdk.ps1 `
|
||||
- `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` 时填 `..`。
|
||||
@@ -232,7 +234,6 @@ D:\SimCAE_SDK\scripts\install-sdk.ps1 `
|
||||
"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",
|
||||
@@ -248,6 +249,14 @@ D:\SimCAE_SDK\scripts\install-sdk.ps1 `
|
||||
}
|
||||
```
|
||||
|
||||
对应的 `config/server_config.json` 示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_base_url": "http://192.168.229.128:8000"
|
||||
}
|
||||
```
|
||||
|
||||
## 五、你:业务主程序需要配合什么
|
||||
|
||||
当前安全模式下,业务主程序需要配合两件事:
|
||||
@@ -309,6 +318,7 @@ TicketHelper::consumeAndVerify(
|
||||
3. 创建 License。
|
||||
4. 发布一个初始版本,例如 `1.0.0`。
|
||||
5. 生成客户端配置,并写入 `bin\config\app_config.json`。客户端下次启动时会自动同步到注册表。
|
||||
6. 生成 qrc 服务端配置,并写入源码目录 `config\server_config.json` 后重新编译 SDK 程序。
|
||||
|
||||
为什么必须先发布初始版本:Launcher 启动业务主程序前会做 Manifest 完整性校验。这个 Manifest 是服务端发布版本时生成并签名的清单,用来证明当前本地文件属于一个可信版本。如果没有发布过 `current_version` 对应版本,客户端会提示签名 Manifest 缓存缺失。
|
||||
|
||||
@@ -319,7 +329,7 @@ TicketHelper::consumeAndVerify(
|
||||
基础联调步骤:
|
||||
|
||||
1. 确认服务端正在运行。
|
||||
2. 确认 `bin\config\app_config.json` 中 `api_base_url`、`client_token`、`license_key`、`current_version` 正确。
|
||||
2. 确认 `bin\config\app_config.json` 中 `client_token`、`license_key`、`current_version` 正确,并确认 `Launcher.exe` 已用正确的 `config/server_config.json` 重新编译。
|
||||
3. 双击 `bin\Launcher.exe`。
|
||||
4. 首次启动时如果 `license_key` 为空,按弹窗输入后台创建的 License;SDK 会把它保存到注册表。
|
||||
5. 成功进入业务主程序后,回到后台查看设备、升级日志、下载日志。
|
||||
@@ -398,7 +408,7 @@ Linux 下如果程序安装在 `/opt`、`/usr/local` 等普通用户不可写目
|
||||
|
||||
1. 直接启动业务主程序提示 ticket 错误:应从 `Launcher.exe` 启动。
|
||||
2. 首次启动保存配置/状态文件失败:如果目录不可写,SDK 会弹出管理员权限确认框;用户取消或当前账号没有管理员权限时仍会失败。
|
||||
3. 首次启动设备登记失败:检查 `api_base_url`、`client_token`、`license_key`、服务端 License 状态。
|
||||
3. 首次启动设备登记失败:检查编译进 qrc 的 `config/server_config.json`、`client_token`、`license_key`、服务端 License 状态。
|
||||
4. 提示 License 错误或过期:在后台确认 License 是否存在、是否被禁用或删除、是否超过最大设备数。
|
||||
5. 策略或 Manifest 验签失败:检查 `config/manifest_public_key.pem` 是否和服务端私钥匹配。
|
||||
6. 提示 signed manifest cache missing:先在后台发布一次 `current_version` 对应版本,并让客户端拿到该版本 Manifest。
|
||||
|
||||
@@ -12,10 +12,11 @@ set(CMAKE_AUTORCC ON)
|
||||
# 所有源码文件
|
||||
set(SRC_LIST
|
||||
main.cpp
|
||||
UpdateLogic.h
|
||||
UpdateLogic.cpp
|
||||
${CMAKE_SOURCE_DIR}/i18n/update-client.qrc
|
||||
)
|
||||
UpdateLogic.h
|
||||
UpdateLogic.cpp
|
||||
${CMAKE_SOURCE_DIR}/i18n/update-client.qrc
|
||||
${CMAKE_SOURCE_DIR}/config/server_config.qrc
|
||||
)
|
||||
# 生成可执行程序
|
||||
add_executable(Launcher ${SRC_LIST})
|
||||
if(WIN32)
|
||||
|
||||
@@ -2,10 +2,11 @@ project(MainApp)
|
||||
add_executable(MainApp
|
||||
main.cpp
|
||||
MainWindow.h
|
||||
MainWindow.cpp
|
||||
../Common/ConfigHelper.h
|
||||
${CMAKE_SOURCE_DIR}/i18n/update-client.qrc
|
||||
)
|
||||
MainWindow.cpp
|
||||
../Common/ConfigHelper.h
|
||||
${CMAKE_SOURCE_DIR}/i18n/update-client.qrc
|
||||
${CMAKE_SOURCE_DIR}/config/server_config.qrc
|
||||
)
|
||||
target_include_directories(MainApp
|
||||
PUBLIC ${CMAKE_SOURCE_DIR}/Common
|
||||
PRIVATE ${OPENSSL_INC}
|
||||
|
||||
@@ -11,10 +11,11 @@ set(SRC
|
||||
main.cpp
|
||||
UpdaterLogic.h
|
||||
UpdaterLogic.cpp
|
||||
UpdateTransaction.h
|
||||
UpdateTransaction.cpp
|
||||
${CMAKE_SOURCE_DIR}/i18n/update-client.qrc
|
||||
)
|
||||
UpdateTransaction.h
|
||||
UpdateTransaction.cpp
|
||||
${CMAKE_SOURCE_DIR}/i18n/update-client.qrc
|
||||
${CMAKE_SOURCE_DIR}/config/server_config.qrc
|
||||
)
|
||||
add_executable(Updater ${SRC})
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
"current_version": "1.0.0",
|
||||
"client_protocol": "3",
|
||||
"launch_token": "SimCAE_Launch_Token_2026_ChangeMe_32Bytes",
|
||||
"license_key": "",
|
||||
"api_base_url": "http://YOUR_SERVER_IP:8000",
|
||||
"license_key": "",
|
||||
"client_token": "SimCAEClientToken2026",
|
||||
"request_timeout_ms": "5000",
|
||||
"temp_folder": "update_temp",
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
"client_protocol": "3",
|
||||
"launch_token": "SimCAE_Launch_Token_2026_ChangeMe_32Bytes",
|
||||
"license_key": "",
|
||||
"api_base_url": "http://YOUR_SERVER_IP:8000",
|
||||
"client_token": "SimCAEClientToken2026",
|
||||
"request_timeout_ms": "5000",
|
||||
"temp_folder": "update_temp",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"api_base_url": "http://YOUR_SERVER_IP:8000"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/simcae">
|
||||
<file>server_config.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -48,11 +48,11 @@ function Join-RelativePath([string]$Base, [string]$Child) {
|
||||
return "$baseNorm/$childNorm"
|
||||
}
|
||||
|
||||
$requiredFields = @(
|
||||
"app_id", "channel", "api_base_url", "current_version",
|
||||
"client_token", "launch_token", "license_key",
|
||||
"main_executable", "launcher_executable", "updater_executable", "bootstrap_executable"
|
||||
)
|
||||
$requiredFields = @(
|
||||
"app_id", "channel", "current_version",
|
||||
"client_token", "launch_token", "license_key",
|
||||
"main_executable", "launcher_executable", "updater_executable", "bootstrap_executable"
|
||||
)
|
||||
foreach ($field in $requiredFields) {
|
||||
if (-not $settings.$field) {
|
||||
throw "Config file is missing required field: $field"
|
||||
|
||||
@@ -95,7 +95,7 @@ CONFIG_FILE="$(realpath "$CONFIG_FILE")"
|
||||
OUTPUT_DIR="$(realpath -m "$OUTPUT_DIR")"
|
||||
ARCHIVE_FILE="$(realpath -m "$ARCHIVE_FILE")"
|
||||
|
||||
for field in app_id channel api_base_url current_version client_token launch_token license_key main_executable launcher_executable updater_executable bootstrap_executable; do
|
||||
for field in app_id channel current_version client_token launch_token license_key main_executable launcher_executable updater_executable bootstrap_executable; do
|
||||
if [[ -z "$(json_value "$field")" ]]; then
|
||||
echo "Config file is missing required field: $field" >&2
|
||||
exit 1
|
||||
|
||||
@@ -100,6 +100,8 @@ Get-ChildItem $source -Force | Where-Object {
|
||||
|
||||
Copy-Item $exampleConfigPath (Join-Path $configDir "app_config.json") -Force
|
||||
Copy-Item $publicKey (Join-Path $configDir "manifest_public_key.pem") -Force
|
||||
Copy-Item (Join-Path $RepoRoot "config/server_config.json") (Join-Path $configDir "server_config.json") -Force
|
||||
Copy-Item (Join-Path $RepoRoot "config/server_config.qrc") (Join-Path $configDir "server_config.qrc") -Force
|
||||
|
||||
$commonSourceDir = Join-Path $RepoRoot "Common"
|
||||
$commonSourceFiles = @(
|
||||
|
||||
@@ -94,6 +94,8 @@ shopt -u dotglob nullglob
|
||||
|
||||
cp "$EXAMPLE_CONFIG" "$OUTPUT_DIR/config/app_config.json"
|
||||
cp "$PUBLIC_KEY" "$OUTPUT_DIR/config/manifest_public_key.pem"
|
||||
cp "$REPO_ROOT/config/server_config.json" "$OUTPUT_DIR/config/server_config.json"
|
||||
cp "$REPO_ROOT/config/server_config.qrc" "$OUTPUT_DIR/config/server_config.qrc"
|
||||
|
||||
for common_file in ConfigHelper.h ConfigHelper.cpp TicketHelper.h TicketHelper.cpp; do
|
||||
common_path="$REPO_ROOT/Common/$common_file"
|
||||
|
||||
Reference in New Issue
Block a user