Skip to content

Commit ec7f5e4

Browse files
committed
ui: Fetch latest release info via GitHub API
1 parent 26fcbe5 commit ec7f5e4

3 files changed

Lines changed: 61 additions & 20 deletions

File tree

ui/xui/update.cc

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,20 @@
2525
#include <SDL_filesystem.h>
2626
#include "util/miniz/miniz.h"
2727
#include "xemu-version.h"
28+
#include <nlohmann/json.hpp>
29+
30+
using json = nlohmann::json;
31+
32+
const char *releases_url = "https://api.github.com/repos/xemu-project/xemu/releases/latest";
2833

29-
#if defined(_WIN32)
30-
const char *version_url = "https://raw.githubusercontent.com/xemu-project/xemu/ppa-snapshot/XEMU_VERSION";
3134
#if defined(__x86_64__)
32-
const char *download_url = "https://github.com/xemu-project/xemu/releases/latest/download/xemu-win-x86_64-release.zip";
35+
#define PACKAGE_ARCH "x86_64"
3336
#elif defined(__aarch64__)
34-
const char *download_url = "https://github.com/xemu-project/xemu/releases/latest/download/xemu-win-aarch64-release.zip";
35-
#else
36-
#error Unknown update path
37-
#endif
37+
#define PACKAGE_ARCH "arm64"
3838
#else
39-
FIXME
39+
#error Unhandled package arch
4040
#endif
4141

42-
4342
#define DPRINTF(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__);
4443

4544
AutoUpdateWindow update_window;
@@ -89,6 +88,7 @@ void AutoUpdateWindow::Draw()
8988
}
9089

9190
if (updater.is_updating()) {
91+
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().ItemSpacing.y));
9292
ImGui::ProgressBar(updater.get_update_progress_percentage()/100.0f,
9393
ImVec2(-1.0f, 0.0f));
9494
}
@@ -129,7 +129,7 @@ Updater::Updater()
129129
m_status = UPDATER_IDLE;
130130
m_update_availability = UPDATE_AVAILABILITY_UNKNOWN;
131131
m_update_percentage = 0;
132-
m_latest_version = "Unknown";
132+
m_release_version = "Unknown";
133133
m_should_cancel = false;
134134
}
135135

@@ -152,7 +152,7 @@ void *Updater::checker_thread_worker_func(void *updater)
152152
void Updater::check_for_update_internal()
153153
{
154154
g_autoptr(GByteArray) data = g_byte_array_new();
155-
int res = http_get(version_url, data, NULL, NULL);
155+
int res = http_get(releases_url, data, NULL, NULL);
156156

157157
if (m_should_cancel) {
158158
m_should_cancel = false;
@@ -163,12 +163,39 @@ void Updater::check_for_update_internal()
163163
goto finished;
164164
}
165165

166-
m_latest_version = std::string((const char *)data->data, data->len);
166+
try {
167+
json release = json::parse(std::string((const char *)data->data, data->len));
168+
m_release_url = release.value("html_url", "https://github.com/xemu-project/xemu/releases/latest");
169+
m_release_version = release["tag_name"].get<std::string>();
170+
if (!m_release_version.empty() && m_release_version[0] == 'v') {
171+
m_release_version = m_release_version.substr(1);
172+
}
167173

168-
if (m_latest_version != xemu_version) {
169-
m_update_availability = UPDATE_AVAILABLE;
170-
} else {
171-
m_update_availability = UPDATE_NOT_AVAILABLE;
174+
m_release_package_url.clear();
175+
std::string expected_filename = "xemu-" + m_release_version + "-windows-" PACKAGE_ARCH ".zip";
176+
for (const auto &asset : release["assets"]) {
177+
std::string name = asset["name"].get<std::string>();
178+
if (name == expected_filename) {
179+
m_release_package_url = asset["browser_download_url"].get<std::string>();
180+
break;
181+
}
182+
}
183+
184+
if (m_release_package_url.empty()) {
185+
DPRINTF("Could not find asset matching %s\n", expected_filename.c_str());
186+
m_status = UPDATER_ERROR;
187+
goto finished;
188+
}
189+
190+
if (m_release_version != xemu_version) {
191+
m_update_availability = UPDATE_AVAILABLE;
192+
} else {
193+
m_update_availability = UPDATE_NOT_AVAILABLE;
194+
}
195+
} catch (const json::exception &e) {
196+
DPRINTF("JSON parse error: %s\n", e.what());
197+
m_status = UPDATER_ERROR;
198+
goto finished;
172199
}
173200

174201
m_status = UPDATER_IDLE;
@@ -215,7 +242,7 @@ void Updater::update_internal()
215242
return static_cast<Updater *>(info->userptr)->progress_cb(info);
216243
};
217244

218-
int res = http_get(download_url, data, &progress_info, NULL);
245+
int res = http_get(m_release_package_url.c_str(), data, &progress_info, NULL);
219246

220247
if (m_should_cancel) {
221248
m_should_cancel = false;

ui/xui/update.hh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ private:
4949
UpdateAvailability m_update_availability;
5050
int m_update_percentage;
5151
QemuThread m_thread;
52-
std::string m_latest_version;
52+
std::string m_release_version;
53+
std::string m_release_url;
54+
std::string m_release_package_url;
5355
bool m_should_cancel;
5456
UpdateStatus m_status;
5557
UpdaterCallback m_on_complete;
@@ -66,7 +68,8 @@ public:
6668
bool is_update_available() { return m_update_availability == UPDATE_AVAILABLE; }
6769
bool is_checking_for_update() { return m_status == UPDATER_CHECKING_FOR_UPDATE; }
6870
bool is_updating() { return m_status == UPDATER_UPDATING; }
69-
std::string get_update_version() { return m_latest_version; }
71+
const std::string& get_release_version() { return m_release_version; }
72+
const std::string& get_release_url() { return m_release_url; }
7073
void cancel() { m_should_cancel = true; }
7174
void update();
7275
void update_internal();

util/http.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qemu/osdep.h"
2121
#include "qapi/error.h"
2222
#include "qemu/http.h"
23+
#include "xemu-version.h"
2324

2425
#include <curl/curl.h>
2526
#include <fcntl.h>
@@ -29,6 +30,13 @@
2930

3031
static bool libcurl_init_called = false;
3132
static bool libcurl_init_success = false;
33+
static char *xemu_user_agent = NULL;
34+
35+
static void libcurl_cleanup(void)
36+
{
37+
curl_global_cleanup();
38+
g_free(xemu_user_agent);
39+
}
3240

3341
static bool ensure_libcurl_initialized(Error **errp)
3442
{
@@ -37,7 +45,8 @@ static bool ensure_libcurl_initialized(Error **errp)
3745
libcurl_init_called = true;
3846
if (res == CURLE_OK) {
3947
libcurl_init_success = true;
40-
atexit(curl_global_cleanup);
48+
xemu_user_agent = g_strdup_printf("xemu/%s", xemu_version);
49+
atexit(libcurl_cleanup);
4150
}
4251
}
4352

@@ -88,6 +97,7 @@ int http_get(const char *url, GByteArray *response_body,
8897
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_get_cb);
8998
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response_body);
9099
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Follow redirects
100+
curl_easy_setopt(curl, CURLOPT_USERAGENT, xemu_user_agent);
91101
#if ALLOW_INSECURE_HOSTS
92102
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
93103
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)
136146
curl_easy_setopt(curl, CURLOPT_POST, 1L);
137147
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
138148
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
149+
curl_easy_setopt(curl, CURLOPT_USERAGENT, xemu_user_agent);
139150
#if ALLOW_INSECURE_HOSTS
140151
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
141152
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

0 commit comments

Comments
 (0)