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:
2026-06-27 21:27:13 -05:00
parent 2e8e214689
commit 4473e7e00a
19 changed files with 1848 additions and 108 deletions

View File

@@ -38,7 +38,10 @@ struct XmrigReleaseAsset {
struct XmrigRelease {
bool ok = false;
std::string tag; // e.g. "v1.0.0"
std::string name; // human title (e.g. "DRG-XMRig v6.25.3")
std::string body; // release notes markdown (holds the checksum blocks)
bool prerelease = false; // marked pre-release on the Gitea release
std::string publishedAt; // ISO-8601 publish timestamp (date shown in the UI)
std::vector<XmrigReleaseAsset> assets;
std::string error;
};
@@ -61,6 +64,10 @@ inline constexpr bool kXmrigRequireSignature = true; // enforced: refus
// Parse the Gitea GET /releases/latest JSON into an XmrigRelease (ok=false + error on failure).
XmrigRelease parseXmrigRelease(const std::string& json);
// Parse the Gitea GET /releases (array) JSON into the list of releases, newest first, skipping
// drafts. Empty on parse failure. Lets the user browse + pin a specific (or pre-release) version.
std::vector<XmrigRelease> parseXmrigReleaseList(const std::string& json);
// The asset-name token for the host platform: "linux-x64", "win-x64", "macos-x64",
// "macos-arm64", or "" if unknown/unsupported.
std::string currentXmrigPlatformToken();
@@ -102,6 +109,8 @@ public:
UpToDate,
UpdateAvailable,
Unavailable, // no miner build is published for this platform (terminal, not an error)
Listing, // fetching the full release list (Browse all releases)
ReleaseList, // release list fetched; awaiting the user's pick
Downloading,
Verifying,
Extracting,
@@ -124,6 +133,9 @@ public:
// Gitea releases API for the DRG-XMRig fork.
static constexpr const char* kApiUrl =
"https://git.dragonx.is/api/v1/repos/DragonX/drg-xmrig/releases/latest";
// Full release list (newest first, includes pre-releases) for the "Browse all releases" picker.
static constexpr const char* kReleasesUrl =
"https://git.dragonx.is/api/v1/repos/DragonX/drg-xmrig/releases?limit=50";
XmrigUpdater() = default;
~XmrigUpdater();
@@ -136,10 +148,21 @@ public:
void startCheck(const std::string& installedTag);
// Download → verify archive → extract (flatten) → verify binary → install into `targetDir` on a
// background thread. Re-fetches the release so it is self-contained. End state: Done / Failed.
// On Done, getProgress().latest_tag is the version that should be persisted as the installed tag.
// background thread. Re-fetches the latest release so it is self-contained. End state: Done /
// Failed. On Done, getProgress().latest_tag is the version that should be persisted as installed.
void startInstall(const std::string& targetDir);
// Fetch the full release list on a background thread (for "Browse all releases"). End state:
// ReleaseList (then getReleases() holds the list, newest first) / Failed.
void startListReleases();
// Snapshot of the release list fetched by startListReleases().
std::vector<XmrigRelease> getReleases() const;
// Install a SPECIFIC release (chosen from the browse list) into `targetDir` — same verify/extract
// path as startInstall, but pinned to `release` instead of latest. End state: Done / Failed.
void startInstallRelease(const std::string& targetDir, XmrigRelease release);
void cancel();
Progress getProgress() const;
bool isDone() const; // true once the worker reached a terminal state (Done/Failed/Unavailable)
@@ -151,13 +174,16 @@ public:
private:
void runCheck(std::string installedTag);
void runListReleases();
void runInstall(std::string targetDir);
void installResolved(const std::string& targetDir, const XmrigRelease& rel); // shared install body
void setProgress(State state, const std::string& text, double done = 0, double total = 0);
bool downloadToFile(const std::string& url, const std::string& destPath);
std::string httpGet(const std::string& url);
mutable std::mutex mutex_;
Progress progress_;
std::vector<XmrigRelease> releases_;
std::atomic<bool> cancel_requested_{false};
std::atomic<bool> worker_running_{false};
std::thread worker_;