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.cpp
updater/prismupdater/GitHubRelease.h
updater/prismupdater/GitHubRelease.cpp
Json.h
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 <QString>
#include <QDebug>
#include "Version.h"
struct GitHubReleaseAsset {
@ -32,3 +34,7 @@ struct GitHubRelease {
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 <QMessageBox>
#include <QNetworkRequest>
#include <QNetworkProxy>
#include <QNetworkRequest>
#include <QProcess>
#include <memory>
@ -128,17 +128,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
QCommandLineParser parser;
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" },
{ { "I", "install-version" }, "Install a spesfic version.", "version name" },
{ { "U", "update-url" }, "Update from the spesified repo.", "github repo url" },
{ { "e", "executable" }, "Path to the prismluancher executable.", "path" },
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.", tr("version name") },
{ { "U", "update-url" }, tr("Update from the spesified repo."), tr("github repo url") },
{ { "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)." },
{ { "F", "force" }, "Force an update, even if one is not needed." },
{ { "l", "list" }, "List avalible releases." },
{ "debug", "Log debug to console." },
{ { "L", "latest" }, "Update to the latest avalible version." },
{ { "D", "allow-downgrade" }, "Allow the updater to downgrade to previous verisons." } });
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" }, tr("Force an update, even if one is not needed.") },
{ { "l", "list" }, tr("List avalible releases.") },
{ "debug", tr("Log debug to console.") },
{ { "L", "latest" }, tr("Update to the latest avalible version.") },
{ { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } });
parser.addHelpOption();
parser.addVersionOption();
@ -146,13 +145,25 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
logToConsole = parser.isSet("debug");
auto prism_executable = parser.value("executable");
if (prism_executable.isEmpty()) {
prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME;
auto prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME;
#if defined(Q_OS_WIN32)
prism_executable += ".exe";
#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;
auto prism_update_url = parser.value("update-url");
@ -226,8 +237,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
showFatalErrorMessage("The launcher data folder is not writable!",
QString("The launcher couldn't create a log file - the data folder is not writable.\n"
showFatalErrorMessage(tr("The launcher data folder is not writable!"),
tr("The launcher couldn't create a log file - the data folder is not writable.\n"
"\n"
"Make sure you have write permissions to the data folder.\n"
"(%1)\n"
@ -296,8 +307,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
qDebug() << "<> Log initialized.";
}
{ // log debug program info
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater"
<< ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
@ -341,7 +350,6 @@ PrismUpdaterApp::~PrismUpdaterApp()
FreeConsole();
}
#endif
}
void PrismUpdaterApp::fail(const QString& reason)
@ -477,17 +485,79 @@ GitHubRelease PrismUpdaterApp::selectRelease()
SelectReleaseDialog dlg(Version(m_prismVerison), releases);
auto result = dlg.exec();
if (result == QDialog::Rejected) {
return {};
}
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) {
return {};
}
return release;
GitHubReleaseAsset asset = dlg.selectedAsset();
return asset;
}
void PrismUpdaterApp::performUpdate(const GitHubRelease& release)
{
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)
@ -562,7 +632,6 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page)
});
connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError);
m_current_task.reset(download);
connect(download.get(), &Net::Download::finished, this, [this]() {
qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed";
@ -570,7 +639,6 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page)
m_current_url = "";
});
QCoreApplication::processEvents();
QMetaObject::invokeMethod(download.get(), &Task::start, Qt::QueuedConnection);

View File

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

View File

@ -76,3 +76,71 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget
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;
};
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;
};