feat(updater): in-app dragonxd updater + browse-all-releases
Add a full-node daemon updater (util/DaemonUpdater + daemon_download_dialog) reachable from Settings -> NODE & SECURITY: downloads/verifies (SHA-256 + enforced ed25519 signature) and atomically installs the latest dragonxd from the project Gitea, with a "Restart daemon now" step. Add a shared "Browse all releases..." picker (release_list_view) to both the miner and daemon updaters so users can pin older/pre-release builds. Pure no-I/O cores (daemon_updater_core / xmrig_updater_core) are unit-tested; sign-daemon-release.sh signs release archives offline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -40,37 +40,71 @@ bool isHex64(const std::string& s)
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Fill an XmrigRelease from one Gitea release JSON object (ok=true iff it has a tag).
|
||||
XmrigRelease parseOneXmrigRelease(const json& j)
|
||||
{
|
||||
XmrigRelease r;
|
||||
if (!j.is_object()) return r;
|
||||
if (j.contains("tag_name") && j["tag_name"].is_string())
|
||||
r.tag = j["tag_name"].get<std::string>();
|
||||
if (j.contains("name") && j["name"].is_string())
|
||||
r.name = j["name"].get<std::string>();
|
||||
if (j.contains("body") && j["body"].is_string())
|
||||
r.body = j["body"].get<std::string>();
|
||||
if (j.contains("prerelease") && j["prerelease"].is_boolean())
|
||||
r.prerelease = j["prerelease"].get<bool>();
|
||||
if (j.contains("published_at") && j["published_at"].is_string())
|
||||
r.publishedAt = j["published_at"].get<std::string>();
|
||||
if (j.contains("assets") && j["assets"].is_array()) {
|
||||
for (const auto& a : j["assets"]) {
|
||||
if (!a.is_object()) continue;
|
||||
XmrigReleaseAsset asset;
|
||||
if (a.contains("name") && a["name"].is_string())
|
||||
asset.name = a["name"].get<std::string>();
|
||||
if (a.contains("browser_download_url") && a["browser_download_url"].is_string())
|
||||
asset.downloadUrl = a["browser_download_url"].get<std::string>();
|
||||
if (a.contains("size") && a["size"].is_number_integer())
|
||||
asset.size = a["size"].get<long long>();
|
||||
if (!asset.name.empty() && !asset.downloadUrl.empty())
|
||||
r.assets.push_back(std::move(asset));
|
||||
}
|
||||
}
|
||||
if (!r.tag.empty()) r.ok = true;
|
||||
return r;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
XmrigRelease parseXmrigRelease(const std::string& jsonStr)
|
||||
{
|
||||
XmrigRelease r;
|
||||
try {
|
||||
const json j = json::parse(jsonStr);
|
||||
if (j.contains("tag_name") && j["tag_name"].is_string())
|
||||
r.tag = j["tag_name"].get<std::string>();
|
||||
if (j.contains("body") && j["body"].is_string())
|
||||
r.body = j["body"].get<std::string>();
|
||||
if (j.contains("assets") && j["assets"].is_array()) {
|
||||
for (const auto& a : j["assets"]) {
|
||||
if (!a.is_object()) continue;
|
||||
XmrigReleaseAsset asset;
|
||||
if (a.contains("name") && a["name"].is_string())
|
||||
asset.name = a["name"].get<std::string>();
|
||||
if (a.contains("browser_download_url") && a["browser_download_url"].is_string())
|
||||
asset.downloadUrl = a["browser_download_url"].get<std::string>();
|
||||
if (a.contains("size") && a["size"].is_number_integer())
|
||||
asset.size = a["size"].get<long long>();
|
||||
if (!asset.name.empty() && !asset.downloadUrl.empty())
|
||||
r.assets.push_back(std::move(asset));
|
||||
}
|
||||
}
|
||||
if (r.tag.empty()) { r.error = "release JSON has no tag_name"; return r; }
|
||||
r.ok = true;
|
||||
r = parseOneXmrigRelease(json::parse(jsonStr));
|
||||
if (!r.ok) r.error = "release JSON has no tag_name";
|
||||
} catch (const std::exception& e) {
|
||||
r.error = std::string("failed to parse release JSON: ") + e.what();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<XmrigRelease> parseXmrigReleaseList(const std::string& jsonStr)
|
||||
{
|
||||
std::vector<XmrigRelease> out;
|
||||
try {
|
||||
const json j = json::parse(jsonStr);
|
||||
if (!j.is_array()) return out;
|
||||
for (const auto& e : j) {
|
||||
if (e.contains("draft") && e["draft"].is_boolean() && e["draft"].get<bool>())
|
||||
continue; // skip drafts (not meant for end users)
|
||||
XmrigRelease r = parseOneXmrigRelease(e);
|
||||
if (r.ok) out.push_back(std::move(r));
|
||||
}
|
||||
} catch (const std::exception&) {
|
||||
// malformed list -> empty (caller treats as "could not load releases")
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string currentXmrigPlatformToken()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
|
||||
Reference in New Issue
Block a user