feat(updater) select valid asset

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers 2023-06-03 18:41:54 -07:00
parent 98174b7a37
commit bee88b1c7f
No known key found for this signature in database
GPG Key ID: E10E321EB160949B
7 changed files with 243 additions and 36 deletions

View File

@ -584,6 +584,7 @@ set(PRISMUPDATER_SOURCES
updater/prismupdater/UpdaterDialogs.h updater/prismupdater/UpdaterDialogs.h
updater/prismupdater/UpdaterDialogs.cpp updater/prismupdater/UpdaterDialogs.cpp
updater/prismupdater/GitHubRelease.h updater/prismupdater/GitHubRelease.h
updater/prismupdater/GitHubRelease.cpp
Json.h Json.h
Json.cpp Json.cpp

View File

@ -0,0 +1,36 @@
#include "GitHubRelease.h"
QDebug operator<<(QDebug debug, const GitHubReleaseAsset& asset)
{
QDebugStateSaver saver(debug);
debug.nospace() << "GitHubReleaseAsset( "
"id: " << asset.id << ", "
"name " << asset.name << ", "
"label: " << asset.label << ", "
"content_type: " << asset.content_type << ", "
"size: " << asset.size << ", "
"created_at: " << asset.created_at << ", "
"updated_at: " << asset.updated_at << ", "
"browser_download_url: " << asset.browser_download_url << " "
")";
return debug;
}
QDebug operator<<(QDebug debug, const GitHubRelease& rls)
{
QDebugStateSaver saver(debug);
debug.nospace() << "GitHubRelease( "
"id: " << rls.id << ", "
"name " << rls.name << ", "
"tag_name: " << rls.tag_name << ", "
"created_at: " << rls.created_at << ", "
"published_at: " << rls.published_at << ", "
"prerelease: " << rls.prerelease << ", "
"draft: " << rls.draft << ", "
"version" << rls.version << ", "
"body: " << rls.body << ", "
"assets: " << rls.assets << " "
")";
return debug;
}

View File

@ -3,6 +3,8 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QDebug>
#include "Version.h" #include "Version.h"
struct GitHubReleaseAsset { struct GitHubReleaseAsset {
@ -32,3 +34,7 @@ struct GitHubRelease {
bool isValid() const { return id > 0; } bool isValid() const { return id > 0; }
}; };
QDebug operator<<(QDebug debug, const GitHubReleaseAsset& rls);
QDebug operator<<(QDebug debug, const GitHubRelease& rls);

View File

@ -33,8 +33,8 @@
#include <QDebug> #include <QDebug>
#include <QMessageBox> #include <QMessageBox>
#include <QNetworkRequest>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QNetworkRequest>
#include <QProcess> #include <QProcess>
#include <memory> #include <memory>
@ -128,17 +128,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher"));
parser.addOptions({ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory).", "directory" }, parser.addOptions({ { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") },
{ { "I", "install-version" }, "Install a spesfic version.", "version name" }, { { "I", "install-version" }, "Install a spesfic version.", tr("version name") },
{ { "U", "update-url" }, "Update from the spesified repo.", "github repo url" }, { { "U", "update-url" }, tr("Update from the spesified repo."), tr("github repo url") },
{ { "e", "executable" }, "Path to the prismluancher executable.", "path" },
{ { "c", "check-only" }, { { "c", "check-only" },
"Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error)." }, tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") },
{ { "F", "force" }, "Force an update, even if one is not needed." }, { { "F", "force" }, tr("Force an update, even if one is not needed.") },
{ { "l", "list" }, "List avalible releases." }, { { "l", "list" }, tr("List avalible releases.") },
{ "debug", "Log debug to console." }, { "debug", tr("Log debug to console.") },
{ { "L", "latest" }, "Update to the latest avalible version." }, { { "L", "latest" }, tr("Update to the latest avalible version.") },
{ { "D", "allow-downgrade" }, "Allow the updater to downgrade to previous verisons." } }); { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } });
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
@ -146,13 +145,25 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
logToConsole = parser.isSet("debug"); logToConsole = parser.isSet("debug");
auto prism_executable = parser.value("executable"); auto prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME;
if (prism_executable.isEmpty()) {
prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME;
#if defined(Q_OS_WIN32) #if defined(Q_OS_WIN32)
prism_executable += ".exe"; prism_executable += ".exe";
#endif #endif
if (BuildConfig.BUILD_PLATFORM.toLower() == "macos")
showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS"));
if (!QFileInfo(prism_executable).isFile())
showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable."));
if (prism_executable.startsWith("/tmp/.mount_")) {
m_appimage = true;
m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
if (m_appimagePath.isEmpty())
showFatalErrorMessage(tr("Unsupprted Instaltion"),
tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)"));
} }
m_prismExecutable = prism_executable; m_prismExecutable = prism_executable;
auto prism_update_url = parser.value("update-url"); auto prism_update_url = parser.value("update-url");
@ -210,7 +221,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
} }
#endif #endif
} }
{ // setup logging { // setup logging
static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log";
auto moveFile = [](const QString& oldName, const QString& newName) { auto moveFile = [](const QString& oldName, const QString& newName) {
@ -226,13 +237,13 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0))); logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
showFatalErrorMessage("The launcher data folder is not writable!", showFatalErrorMessage(tr("The launcher data folder is not writable!"),
QString("The launcher couldn't create a log file - the data folder is not writable.\n" tr("The launcher couldn't create a log file - the data folder is not writable.\n"
"\n" "\n"
"Make sure you have write permissions to the data folder.\n" "Make sure you have write permissions to the data folder.\n"
"(%1)\n" "(%1)\n"
"\n" "\n"
"The launcher cannot continue until you fix this problem.") "The launcher cannot continue until you fix this problem.")
.arg(dataPath)); .arg(dataPath));
return; return;
} }
@ -296,8 +307,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
qDebug() << "<> Log initialized."; qDebug() << "<> Log initialized.";
} }
{ // log debug program info { // log debug program info
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater"
<< ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
@ -316,7 +325,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
qDebug() << "<> Paths set."; qDebug() << "<> Paths set.";
} }
{ // network { // network
m_network = makeShared<QNetworkAccessManager>(new QNetworkAccessManager()); m_network = makeShared<QNetworkAccessManager>(new QNetworkAccessManager());
qDebug() << "Detecting proxy settings..."; qDebug() << "Detecting proxy settings...";
QNetworkProxy proxy = QNetworkProxy::applicationProxy(); QNetworkProxy proxy = QNetworkProxy::applicationProxy();
@ -341,7 +350,6 @@ PrismUpdaterApp::~PrismUpdaterApp()
FreeConsole(); FreeConsole();
} }
#endif #endif
} }
void PrismUpdaterApp::fail(const QString& reason) void PrismUpdaterApp::fail(const QString& reason)
@ -477,17 +485,79 @@ GitHubRelease PrismUpdaterApp::selectRelease()
SelectReleaseDialog dlg(Version(m_prismVerison), releases); SelectReleaseDialog dlg(Version(m_prismVerison), releases);
auto result = dlg.exec(); auto result = dlg.exec();
if (result == QDialog::Rejected) {
return {};
}
GitHubRelease release = dlg.selectedRelease(); GitHubRelease release = dlg.selectedRelease();
return release;
}
QList<GitHubReleaseAsset> PrismUpdaterApp::validReleaseArtifacts(const GitHubRelease& release)
{
QList<GitHubReleaseAsset> valid;
qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM << "portable:" << m_portable;
if (BuildConfig.BUILD_PLATFORM.isEmpty())
qWarning() << "Build platform is not set!";
for (auto asset : release.assets) {
if (!m_appimage && asset.name.toLower().endsWith("appimage"))
continue;
else if (m_appimage && !asset.name.toLower().endsWith("appimage"))
continue;
bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower());
bool for_portable = asset.name.toLower().contains("portable");
if (((m_portable && for_portable) || (!m_portable && !for_portable)) && for_platform) {
valid.append(asset);
}
}
return valid;
}
GitHubReleaseAsset PrismUpdaterApp::selectAsset(const QList<GitHubReleaseAsset>& assets)
{
SelectReleaseAssetDialog dlg(assets);
auto result = dlg.exec();
if (result == QDialog::Rejected) { if (result == QDialog::Rejected) {
return {}; return {};
} }
return release; GitHubReleaseAsset asset = dlg.selectedAsset();
return asset;
} }
void PrismUpdaterApp::performUpdate(const GitHubRelease& release) void PrismUpdaterApp::performUpdate(const GitHubRelease& release)
{ {
qDebug() << "Updating to" << release.tag_name; qDebug() << "Updating to" << release.tag_name;
auto valid_assets = validReleaseArtifacts(release);
qDebug() << "vallid release assets:" << valid_assets;
GitHubReleaseAsset selected_asset;
if (valid_assets.isEmpty()) {
showFatalErrorMessage(tr("No Valid Release Assets"),
tr("Github release %1 has no valid assets for this platform: %2")
.arg(release.tag_name)
.arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_portable)));
return;
} else if (valid_assets.length() > 1) {
selected_asset = selectAsset(valid_assets);
} else {
selected_asset = valid_assets.takeFirst();
}
if (! selected_asset.isValid()) {
showFatalErrorMessage("No version selected.", "No version was selected.");
return;
}
qDebug() << "will intall" << selected_asset;
}
QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset)
{
// TODO! (researching what to do with appimages)
} }
void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path)
@ -562,14 +632,12 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page)
}); });
connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError); connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError);
m_current_task.reset(download); m_current_task.reset(download);
connect(download.get(), &Net::Download::finished, this, [this](){ connect(download.get(), &Net::Download::finished, this, [this]() {
qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed"; qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed";
m_current_task.reset(); m_current_task.reset();
m_current_url = ""; m_current_url = "";
}); });
QCoreApplication::processEvents(); QCoreApplication::processEvents();

View File

@ -39,7 +39,6 @@
#include "QObjectPtr.h" #include "QObjectPtr.h"
#include "net/Download.h" #include "net/Download.h"
#define PRISM_EXTERNAL_EXE #define PRISM_EXTERNAL_EXE
#include "FileSystem.h" #include "FileSystem.h"
@ -65,25 +64,34 @@ class PrismUpdaterApp : public QApplication {
void downloadReleasePage(const QString& api_url, int page); void downloadReleasePage(const QString& api_url, int page);
int parseReleasePage(const QByteArray* responce); int parseReleasePage(const QByteArray* responce);
GitHubRelease getLatestRelease();
bool needUpdate(const GitHubRelease& release); bool needUpdate(const GitHubRelease& release);
GitHubRelease getLatestRelease();
GitHubRelease selectRelease(); GitHubRelease selectRelease();
void performUpdate(const GitHubRelease& release);
void printReleases();
QList<GitHubRelease> newerReleases(); QList<GitHubRelease> newerReleases();
QList<GitHubRelease> nonDraftReleases(); QList<GitHubRelease> nonDraftReleases();
void printReleases();
QList<GitHubReleaseAsset> validReleaseArtifacts(const GitHubRelease& release);
GitHubReleaseAsset selectAsset(const QList<GitHubReleaseAsset>& assets);
void performUpdate(const GitHubRelease& release);
QFileInfo downloadAsset(const GitHubReleaseAsset& asset);
public slots: public slots:
void downloadError(QString reason); void downloadError(QString reason);
private: private:
const QString& root() { return m_rootPath; } const QString& root() { return m_rootPath; }
bool isPortable() { return m_portable; } bool isPortable() { return m_portable; }
QString m_rootPath; QString m_rootPath;
bool m_portable = false; bool m_portable = false;
bool m_appimage = false;
QString m_appimagePath;
QString m_prismExecutable; QString m_prismExecutable;
QUrl m_prismRepoUrl; QUrl m_prismRepoUrl;
Version m_userSelectedVersion; Version m_userSelectedVersion;

View File

@ -76,3 +76,71 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget
ui->changelogTextBrowser->setHtml(body); ui->changelogTextBrowser->setHtml(body);
} }
SelectReleaseAssetDialog::SelectReleaseAssetDialog( const QList<GitHubReleaseAsset>& assets, QWidget* parent)
: QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog)
{
ui->setupUi(this);
ui->changelogTextBrowser->setOpenExternalLinks(true);
ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
ui->versionsTree->setColumnCount(2);
ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch);
ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")});
ui->versionsTree->header()->setStretchLastSection(false);
ui->eplainLabel->setText(tr("Select a version to install."));
ui->changelogTextBrowser->setHidden(true);
loadAssets();
connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseAssetDialog::selectionChanged);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseAssetDialog::accept);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseAssetDialog::reject);
}
SelectReleaseAssetDialog::~SelectReleaseAssetDialog()
{
delete ui;
}
void SelectReleaseAssetDialog::loadAssets()
{
for (auto rls : m_assets) {
appendAsset(rls);
}
}
void SelectReleaseAssetDialog::appendAsset(GitHubReleaseAsset const& asset)
{
auto rls_item = new QTreeWidgetItem(ui->versionsTree);
rls_item->setText(0, asset.name);
rls_item->setExpanded(true);
rls_item->setText(1, asset.updated_at.toString());
rls_item->setData(0, Qt::UserRole, QVariant(asset.id));
ui->versionsTree->addTopLevelItem(rls_item);
}
GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) {
int id = item->data(0, Qt::UserRole).toInt();
GitHubReleaseAsset selected_asset;
for (auto asset: m_assets) {
if (asset.id == id)
selected_asset = asset;
}
return selected_asset;
}
void SelectReleaseAssetDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
GitHubReleaseAsset asset = getAsset(current);
m_selectedAsset = asset;
}

View File

@ -31,3 +31,23 @@ class SelectReleaseDialog : public QDialog {
Ui::SelectReleaseDialog* ui; Ui::SelectReleaseDialog* ui;
}; };
class SelectReleaseAssetDialog : public QDialog {
Q_OBJECT
public:
explicit SelectReleaseAssetDialog(const QList<GitHubReleaseAsset>& assets, QWidget* parent = 0);
~SelectReleaseAssetDialog();
void loadAssets();
void appendAsset(GitHubReleaseAsset const& asset);
GitHubReleaseAsset selectedAsset() { return m_selectedAsset; }
private slots:
GitHubReleaseAsset getAsset(QTreeWidgetItem* item);
void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
protected:
QList<GitHubReleaseAsset> m_assets;
GitHubReleaseAsset m_selectedAsset;
Ui::SelectReleaseDialog* ui;
};