ui: Fetch latest release info via GitHub API

This commit is contained in:
Matt Borgerson
2026-01-21 16:52:35 -07:00
committed by mborgerson
parent 26fcbe54f1
commit ec7f5e438a
3 changed files with 61 additions and 20 deletions
+41 -14
View File
@@ -25,20 +25,19 @@
#include <SDL_filesystem.h>
#include "util/miniz/miniz.h"
#include "xemu-version.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
const char *releases_url = "https://api.github.com/repos/xemu-project/xemu/releases/latest";
#if defined(_WIN32)
const char *version_url = "https://raw.githubusercontent.com/xemu-project/xemu/ppa-snapshot/XEMU_VERSION";
#if defined(__x86_64__)
const char *download_url = "https://github.com/xemu-project/xemu/releases/latest/download/xemu-win-x86_64-release.zip";
#define PACKAGE_ARCH "x86_64"
#elif defined(__aarch64__)
const char *download_url = "https://github.com/xemu-project/xemu/releases/latest/download/xemu-win-aarch64-release.zip";
#define PACKAGE_ARCH "arm64"
#else
#error Unknown update path
#error Unhandled package arch
#endif
#else
FIXME
#endif
#define DPRINTF(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__);
@@ -89,6 +88,7 @@ void AutoUpdateWindow::Draw()
}
if (updater.is_updating()) {
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().ItemSpacing.y));
ImGui::ProgressBar(updater.get_update_progress_percentage()/100.0f,
ImVec2(-1.0f, 0.0f));
}
@@ -129,7 +129,7 @@ Updater::Updater()
m_status = UPDATER_IDLE;
m_update_availability = UPDATE_AVAILABILITY_UNKNOWN;
m_update_percentage = 0;
m_latest_version = "Unknown";
m_release_version = "Unknown";
m_should_cancel = false;
}
@@ -152,7 +152,7 @@ void *Updater::checker_thread_worker_func(void *updater)
void Updater::check_for_update_internal()
{
g_autoptr(GByteArray) data = g_byte_array_new();
int res = http_get(version_url, data, NULL, NULL);
int res = http_get(releases_url, data, NULL, NULL);
if (m_should_cancel) {
m_should_cancel = false;
@@ -163,13 +163,40 @@ void Updater::check_for_update_internal()
goto finished;
}
m_latest_version = std::string((const char *)data->data, data->len);
try {
json release = json::parse(std::string((const char *)data->data, data->len));
m_release_url = release.value("html_url", "https://github.com/xemu-project/xemu/releases/latest");
m_release_version = release["tag_name"].get<std::string>();
if (!m_release_version.empty() && m_release_version[0] == 'v') {
m_release_version = m_release_version.substr(1);
}
if (m_latest_version != xemu_version) {
m_release_package_url.clear();
std::string expected_filename = "xemu-" + m_release_version + "-windows-" PACKAGE_ARCH ".zip";
for (const auto &asset : release["assets"]) {
std::string name = asset["name"].get<std::string>();
if (name == expected_filename) {
m_release_package_url = asset["browser_download_url"].get<std::string>();
break;
}
}
if (m_release_package_url.empty()) {
DPRINTF("Could not find asset matching %s\n", expected_filename.c_str());
m_status = UPDATER_ERROR;
goto finished;
}
if (m_release_version != xemu_version) {
m_update_availability = UPDATE_AVAILABLE;
} else {
m_update_availability = UPDATE_NOT_AVAILABLE;
}
} catch (const json::exception &e) {
DPRINTF("JSON parse error: %s\n", e.what());
m_status = UPDATER_ERROR;
goto finished;
}
m_status = UPDATER_IDLE;
finished:
@@ -215,7 +242,7 @@ void Updater::update_internal()
return static_cast<Updater *>(info->userptr)->progress_cb(info);
};
int res = http_get(download_url, data, &progress_info, NULL);
int res = http_get(m_release_package_url.c_str(), data, &progress_info, NULL);
if (m_should_cancel) {
m_should_cancel = false;
+5 -2
View File
@@ -49,7 +49,9 @@ private:
UpdateAvailability m_update_availability;
int m_update_percentage;
QemuThread m_thread;
std::string m_latest_version;
std::string m_release_version;
std::string m_release_url;
std::string m_release_package_url;
bool m_should_cancel;
UpdateStatus m_status;
UpdaterCallback m_on_complete;
@@ -66,7 +68,8 @@ public:
bool is_update_available() { return m_update_availability == UPDATE_AVAILABLE; }
bool is_checking_for_update() { return m_status == UPDATER_CHECKING_FOR_UPDATE; }
bool is_updating() { return m_status == UPDATER_UPDATING; }
std::string get_update_version() { return m_latest_version; }
const std::string& get_release_version() { return m_release_version; }
const std::string& get_release_url() { return m_release_url; }
void cancel() { m_should_cancel = true; }
void update();
void update_internal();
+12 -1
View File
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/http.h"
#include "xemu-version.h"
#include <curl/curl.h>
#include <fcntl.h>
@@ -29,6 +30,13 @@
static bool libcurl_init_called = false;
static bool libcurl_init_success = false;
static char *xemu_user_agent = NULL;
static void libcurl_cleanup(void)
{
curl_global_cleanup();
g_free(xemu_user_agent);
}
static bool ensure_libcurl_initialized(Error **errp)
{
@@ -37,7 +45,8 @@ static bool ensure_libcurl_initialized(Error **errp)
libcurl_init_called = true;
if (res == CURLE_OK) {
libcurl_init_success = true;
atexit(curl_global_cleanup);
xemu_user_agent = g_strdup_printf("xemu/%s", xemu_version);
atexit(libcurl_cleanup);
}
}
@@ -88,6 +97,7 @@ int http_get(const char *url, GByteArray *response_body,
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_get_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response_body);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Follow redirects
curl_easy_setopt(curl, CURLOPT_USERAGENT, xemu_user_agent);
#if ALLOW_INSECURE_HOSTS
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
@@ -136,6 +146,7 @@ int http_post_json(const char *url, const char *json_data, Error **errp)
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_USERAGENT, xemu_user_agent);
#if ALLOW_INSECURE_HOSTS
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);