Qt: Support multiple updater channels

This commit is contained in:
Connor McLaughlin 2020-12-22 02:47:48 +10:00
parent 14c227a813
commit 7249825c16
5 changed files with 135 additions and 17 deletions

View file

@ -28,7 +28,7 @@ Log_SetChannel(AutoUpdaterDialog);
#ifdef WIN32
#if defined(__has_include) && __has_include("scmversion/tag.h")
#include "scmversion/tag.h"
#ifdef SCM_RELEASE_TAG
#ifdef SCM_RELEASE_TAGS
#define AUTO_UPDATER_SUPPORTED
#endif
#endif
@ -36,11 +36,12 @@ Log_SetChannel(AutoUpdaterDialog);
#ifdef AUTO_UPDATER_SUPPORTED
static constexpr char LATEST_TAG_URL[] = "https://api.github.com/repos/stenzek/duckstation/tags";
static constexpr char LATEST_RELEASE_URL[] =
"https://api.github.com/repos/stenzek/duckstation/releases/tags/" SCM_RELEASE_TAG;
static constexpr char CHANGES_URL[] = "https://api.github.com/repos/stenzek/duckstation/compare/%s..." SCM_RELEASE_TAG;
static constexpr char UPDATE_ASSET_FILENAME[] = SCM_RELEASE_ASSET;
static constexpr char* LATEST_TAG_URL = "https://api.github.com/repos/stenzek/duckstation/tags";
static constexpr char* LATEST_RELEASE_URL = "https://api.github.com/repos/stenzek/duckstation/releases/tags/%s";
static constexpr char* CHANGES_URL = "https://api.github.com/repos/stenzek/duckstation/compare/%s...%s";
static constexpr char* UPDATE_ASSET_FILENAME = SCM_RELEASE_ASSET;
static constexpr char* UPDATE_TAGS[] = SCM_RELEASE_TAGS;
static constexpr char* THIS_RELEASE_TAG = SCM_RELEASE_TAG;
#endif
@ -69,6 +70,33 @@ bool AutoUpdaterDialog::isSupported()
#endif
}
QStringList AutoUpdaterDialog::getTagList()
{
#ifdef AUTO_UPDATER_SUPPORTED
return QStringList(std::begin(UPDATE_TAGS), std::end(UPDATE_TAGS));
#else
return QStringList();
#endif
}
std::string AutoUpdaterDialog::getDefaultTag()
{
#ifdef AUTO_UPDATER_SUPPORTED
return THIS_RELEASE_TAG;
#else
return {};
#endif
}
std::string AutoUpdaterDialog::getCurrentUpdateTag() const
{
#ifdef AUTO_UPDATER_SUPPORTED
return m_host_interface->GetStringSettingValue("AutoUpdater", "UpdateTag", THIS_RELEASE_TAG);
#else
return {};
#endif
}
void AutoUpdaterDialog::reportError(const char* msg, ...)
{
std::va_list ap;
@ -86,7 +114,7 @@ void AutoUpdaterDialog::queueUpdateCheck(bool display_message)
#ifdef AUTO_UPDATER_SUPPORTED
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getLatestTagComplete);
QUrl url(QUrl::fromEncoded(QByteArray(LATEST_TAG_URL, sizeof(LATEST_TAG_URL) - 1)));
QUrl url(QUrl::fromEncoded(QByteArray(LATEST_TAG_URL)));
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
m_network_access_mgr->get(request);
@ -100,7 +128,10 @@ void AutoUpdaterDialog::queueGetLatestRelease()
#ifdef AUTO_UPDATER_SUPPORTED
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getLatestReleaseComplete);
QUrl url(QUrl::fromEncoded(QByteArray(LATEST_RELEASE_URL, sizeof(LATEST_RELEASE_URL) - 1)));
SmallString url_string;
url_string.Format(LATEST_RELEASE_URL, getCurrentUpdateTag().c_str());
QUrl url(QUrl::fromEncoded(QByteArray(url_string)));
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
m_network_access_mgr->get(request);
@ -110,6 +141,9 @@ void AutoUpdaterDialog::queueGetLatestRelease()
void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply)
{
#ifdef AUTO_UPDATER_SUPPORTED
const std::string selected_tag(getCurrentUpdateTag());
const QString selected_tag_qstr = QString::fromStdString(selected_tag);
// this might fail due to a lack of internet connection - in which case, don't spam the user with messages every time.
m_network_access_mgr->disconnect(this);
reply->deleteLater();
@ -127,7 +161,7 @@ void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply)
if (!val.isObject())
continue;
if (val["name"].toString() != QStringLiteral(SCM_RELEASE_TAG))
if (val["name"].toString() != selected_tag_qstr)
continue;
m_latest_sha = val["commit"].toObject()["sha"].toString();
@ -150,7 +184,7 @@ void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply)
}
if (m_display_messages)
reportError("latest release not found in JSON");
reportError("%s release not found in JSON", selected_tag.c_str());
}
else
{
@ -228,7 +262,8 @@ void AutoUpdaterDialog::queueGetChanges()
#ifdef AUTO_UPDATER_SUPPORTED
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getChangesComplete);
const std::string url_string(StringUtil::StdStringFromFormat(CHANGES_URL, g_scm_hash_str));
const std::string url_string(
StringUtil::StdStringFromFormat(CHANGES_URL, g_scm_hash_str, getCurrentUpdateTag().c_str()));
QUrl url(QUrl::fromEncoded(QByteArray(url_string.c_str(), static_cast<int>(url_string.size()))));
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
@ -275,7 +310,7 @@ void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply)
if (message.contains(QStringLiteral("[SAVEVERSION+]")))
update_will_break_save_states = true;
if (message.contains(QStringLiteral("[SETTINGSVERSION+]")))
update_increases_settings_version = true;
}

View file

@ -1,5 +1,7 @@
#pragma once
#include "ui_autoupdaterdialog.h"
#include <string>
#include <QtCore/QStringList>
#include <QtWidgets/QDialog>
class QNetworkAccessManager;
@ -16,6 +18,8 @@ public:
~AutoUpdaterDialog();
static bool isSupported();
static QStringList getTagList();
static std::string getDefaultTag();
Q_SIGNALS:
void updateCheckCompleted();
@ -38,6 +42,7 @@ private Q_SLOTS:
private:
void reportError(const char* msg, ...);
bool updateNeeded() const;
std::string getCurrentUpdateTag() const;
#ifdef WIN32
bool processUpdate(const QByteArray& update_data);

View file

@ -1,7 +1,9 @@
#include "generalsettingswidget.h"
#include "autoupdaterdialog.h"
#include "frontend-common/controller_interface.h"
#include "mainwindow.h"
#include "qtutils.h"
#include "scmversion/scmversion.h"
#include "settingsdialog.h"
#include "settingwidgetbinder.h"
@ -113,17 +115,28 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
#endif
if (AutoUpdaterDialog::isSupported())
{
QCheckBox* enableDiscordPresence = new QCheckBox(tr("Enable Automatic Update Check"), m_ui.groupBox_4);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, enableDiscordPresence, "AutoUpdater",
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.autoUpdateEnabled, "AutoUpdater",
"CheckAtStartup", true);
m_ui.formLayout_4->addWidget(enableDiscordPresence, current_row, current_col);
dialog->registerWidgetHelp(enableDiscordPresence, tr("Enable Automatic Update Check"), tr("Checked"),
dialog->registerWidgetHelp(m_ui.autoUpdateEnabled, tr("Enable Automatic Update Check"), tr("Checked"),
tr("Automatically checks for updates to the program on startup. Updates can be deferred "
"until later or skipped entirely."));
m_ui.autoUpdateTag->addItems(AutoUpdaterDialog::getTagList());
SettingWidgetBinder::BindWidgetToStringSetting(m_host_interface, m_ui.autoUpdateTag, "AutoUpdater", "UpdateTag",
AutoUpdaterDialog::getDefaultTag());
m_ui.autoUpdateCurrentVersion->setText(tr("%1 (%2)").arg(g_scm_tag_str).arg(g_scm_date_str));
connect(m_ui.checkForUpdates, &QPushButton::clicked,
[this]() { m_host_interface->getMainWindow()->checkForUpdates(true); });
current_col++;
current_row += (current_col / 2);
current_col %= 2;
}
else
{
m_ui.verticalLayout->removeWidget(m_ui.automaticUpdaterGroup);
m_ui.automaticUpdaterGroup->hide();
}
}
GeneralSettingsWidget::~GeneralSettingsWidget() = default;

View file

@ -157,6 +157,70 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="automaticUpdaterGroup">
<property name="title">
<string>Automatic Updater</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Update Channel:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="autoUpdateTag"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Current Version:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="autoUpdateCurrentVersion">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="autoUpdateEnabled">
<property name="text">
<string>Enable Automatic Update Check</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="checkForUpdates">
<property name="text">
<string>Check for Updates...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View file

@ -39,6 +39,8 @@ public Q_SLOTS:
/// Updates debug menu visibility (hides if disabled).
void updateDebugMenuVisibility();
void checkForUpdates(bool display_message);
private Q_SLOTS:
void reportError(const QString& message);
void reportMessage(const QString& message);
@ -93,7 +95,6 @@ private Q_SLOTS:
void onGameListSetCoverImageRequested(const GameListEntry* entry);
void onCPUDebuggerClosed();
void checkForUpdates(bool display_message);
void onUpdateCheckComplete();
protected: