From b51def8a99e334d17591cc5cd77a7b64571b2e39 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 28 Sep 2024 22:01:01 +1000 Subject: [PATCH] Qt: Support update checking without installing i.e. what I'm going to do with packages, just open the download page and let the user install it. --- .github/workflows/rolling-release.yml | 49 +++++++++++-- src/duckstation-qt/autoupdaterdialog.cpp | 92 +++++++++++++----------- src/duckstation-qt/qtutils.cpp | 4 +- src/duckstation-qt/qtutils.h | 2 +- 4 files changed, 97 insertions(+), 50 deletions(-) diff --git a/.github/workflows/rolling-release.yml b/.github/workflows/rolling-release.yml index 0899cbe80..b3ce42803 100644 --- a/.github/workflows/rolling-release.yml +++ b/.github/workflows/rolling-release.yml @@ -53,6 +53,11 @@ jobs: shell: cmd run: | echo #pragma once > src/scmversion/tag.h + + - name: Set Build Tag Asset + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' + shell: cmd + run: | echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-release.zip" >> src/scmversion/tag.h echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h @@ -139,6 +144,11 @@ jobs: shell: cmd run: | echo #pragma once > src/scmversion/tag.h + + - name: Set Build Tag Asset + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' + shell: cmd + run: | echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-sse2-release.zip" >> src/scmversion/tag.h echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h @@ -226,6 +236,11 @@ jobs: shell: cmd run: | echo #pragma once > src/scmversion/tag.h + + - name: Set Build Tag Asset + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' + shell: cmd + run: | echo #define SCM_RELEASE_ASSET "duckstation-windows-arm64-release.zip" >> src/scmversion/tag.h echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h @@ -308,6 +323,10 @@ jobs: - name: Initialize Build Tag run: | echo '#pragma once' > src/scmversion/tag.h + + - name: Set Build Tag Asset + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' + run: | echo '#define SCM_RELEASE_ASSET "DuckStation-x64.AppImage"' >> src/scmversion/tag.h echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h @@ -368,6 +387,10 @@ jobs: - name: Initialize Build Tag run: | echo '#pragma once' > src/scmversion/tag.h + + - name: Set Build Tag Asset + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' + run: | echo '#define SCM_RELEASE_ASSET "DuckStation-x64-SSE2.AppImage"' >> src/scmversion/tag.h echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h @@ -420,6 +443,21 @@ jobs: run: | echo '#pragma once' > src/scmversion/tag.h + - name: Set Build Tags + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' + run: | + echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h + + - name: Tag as Preview Release + if: github.ref == 'refs/heads/master' + run: | + echo '#define SCM_RELEASE_TAG "preview"' >> src/scmversion/tag.h + + - name: Tag as Rolling Release + if: github.ref == 'refs/heads/dev' + run: | + echo '#define SCM_RELEASE_TAG "latest"' >> src/scmversion/tag.h + - name: Generate AppStream XML run: | scripts/generate-metainfo.sh scripts/flatpak @@ -498,18 +536,20 @@ jobs: run: | echo '#pragma once' > src/scmversion/tag.h - - name: Tag as Preview Release - if: github.ref == 'refs/heads/master' + - name: Set Build Tags + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' run: | echo '#define SCM_RELEASE_ASSET "duckstation-mac-release.zip"' >> src/scmversion/tag.h echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h + + - name: Tag as Preview Release + if: github.ref == 'refs/heads/master' + run: | echo '#define SCM_RELEASE_TAG "preview"' >> src/scmversion/tag.h - name: Tag as Rolling Release if: github.ref == 'refs/heads/dev' run: | - echo '#define SCM_RELEASE_ASSET "duckstation-mac-release.zip"' >> src/scmversion/tag.h - echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h echo '#define SCM_RELEASE_TAG "latest"' >> src/scmversion/tag.h - name: Compile and Zip .app @@ -532,6 +572,7 @@ jobs: create-release: + name: Create Release needs: [windows-x64-build, windows-x64-sse2-build, windows-arm64-build, linux-x64-appimage-build, linux-x64-sse2-appimage-build, linux-flatpak-build, macos-build] runs-on: ubuntu-22.04 if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' diff --git a/src/duckstation-qt/autoupdaterdialog.cpp b/src/duckstation-qt/autoupdaterdialog.cpp index dad1ab6f2..30aa12443 100644 --- a/src/duckstation-qt/autoupdaterdialog.cpp +++ b/src/duckstation-qt/autoupdaterdialog.cpp @@ -50,20 +50,28 @@ static constexpr u32 HTTP_POLL_INTERVAL = 10; // Requires that the channel be defined by the buildbot. #if __has_include("scmversion/tag.h") #include "scmversion/tag.h" -#if defined(SCM_RELEASE_TAGS) && defined(SCM_RELEASE_TAG) && defined(SCM_RELEASE_ASSET) +#if defined(SCM_RELEASE_TAGS) && defined(SCM_RELEASE_TAG) +#define UPDATE_CHECKER_SUPPORTED +#ifdef SCM_RELEASE_ASSET #define AUTO_UPDATER_SUPPORTED #endif #endif +#endif -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED static const char* LATEST_TAG_URL = "https://api.github.com/repos/stenzek/duckstation/tags"; static const char* LATEST_RELEASE_URL = "https://api.github.com/repos/stenzek/duckstation/releases/tags/{}"; static const char* CHANGES_URL = "https://api.github.com/repos/stenzek/duckstation/compare/{}...{}"; -static const char* UPDATE_ASSET_FILENAME = SCM_RELEASE_ASSET; static const char* UPDATE_TAGS[] = SCM_RELEASE_TAGS; static const char* THIS_RELEASE_TAG = SCM_RELEASE_TAG; +#ifdef AUTO_UPDATER_SUPPORTED +static const char* UPDATE_ASSET_FILENAME = SCM_RELEASE_ASSET; +#else +static const char* DOWNLOAD_PAGE_URL = "https://github.com/stenzek/duckstation/releases/tag/{}"; +#endif + #endif LOG_CHANNEL(AutoUpdaterDialog); @@ -87,20 +95,8 @@ AutoUpdaterDialog::~AutoUpdaterDialog() = default; bool AutoUpdaterDialog::isSupported() { -#ifdef AUTO_UPDATER_SUPPORTED -#ifdef __linux__ - // For Linux, we need to check whether we're running from the appimage. - if (!std::getenv("APPIMAGE")) - { - INFO_LOG("We're a CI release, but not running from an AppImage. Disabling automatic updater."); - return false; - } - +#ifdef UPDATE_CHECKER_SUPPORTED return true; -#else - // Windows/Mac - always supported. - return true; -#endif #else return false; #endif @@ -204,7 +200,7 @@ bool AutoUpdaterDialog::warnAboutUnofficialBuild() QStringList AutoUpdaterDialog::getTagList() { -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED return QStringList(std::begin(UPDATE_TAGS), std::end(UPDATE_TAGS)); #else return QStringList(); @@ -213,7 +209,7 @@ QStringList AutoUpdaterDialog::getTagList() std::string AutoUpdaterDialog::getDefaultTag() { -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED return THIS_RELEASE_TAG; #else return {}; @@ -222,7 +218,7 @@ std::string AutoUpdaterDialog::getDefaultTag() std::string AutoUpdaterDialog::getCurrentUpdateTag() const { -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED return Host::GetBaseStringSettingValue("AutoUpdater", "UpdateTag", THIS_RELEASE_TAG); #else return {}; @@ -271,7 +267,7 @@ void AutoUpdaterDialog::queueUpdateCheck(bool display_message) { m_display_messages = display_message; -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED if (!ensureHttpReady()) { emit updateCheckCompleted(); @@ -287,7 +283,7 @@ void AutoUpdaterDialog::queueUpdateCheck(bool display_message) void AutoUpdaterDialog::queueGetLatestRelease() { -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED if (!ensureHttpReady()) { emit updateCheckCompleted(); @@ -302,7 +298,7 @@ void AutoUpdaterDialog::queueGetLatestRelease() void AutoUpdaterDialog::getLatestTagComplete(s32 status_code, std::vector response) { -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED const std::string selected_tag(getCurrentUpdateTag()); const QString selected_tag_qstr = QString::fromStdString(selected_tag); @@ -362,7 +358,7 @@ void AutoUpdaterDialog::getLatestTagComplete(s32 status_code, std::vector re void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, std::vector response) { -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED if (status_code == HTTPDownloader::HTTP_STATUS_OK) { QJsonParseError parse_error; @@ -372,9 +368,14 @@ void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, std::vectorsetText(tr("Current Version: %1 (%2)").arg(g_scm_hash_str).arg(g_scm_date_str)); + m_ui.newVersion->setText(tr("New Version: %1 (%2)").arg(m_latest_sha).arg(doc_object["published_at"].toString())); + +#ifdef AUTO_UPDATER_SUPPORTED // search for the correct file const QJsonArray assets(doc_object["assets"].toArray()); const QString asset_filename(UPDATE_ASSET_FILENAME); + bool asset_found = false; for (const QJsonValue& asset : assets) { const QJsonObject asset_obj(asset.toObject()); @@ -382,27 +383,28 @@ void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, std::vectorsetText(tr("Current Version: %1 (%2)").arg(g_scm_hash_str).arg(g_scm_date_str)); - m_ui.newVersion->setText( - tr("New Version: %1 (%2)").arg(m_latest_sha).arg(doc_object["published_at"].toString())); - m_ui.updateNotes->setText(tr("Loading...")); - m_ui.downloadAndInstall->setEnabled(true); - queueGetChanges(); - - // We have to defer this, because it comes back through the timer/HTTP callback... - QMetaObject::invokeMethod(this, "exec", Qt::QueuedConnection); - - emit updateCheckCompleted(); - return; - } - + asset_found = true; break; } } - reportError("Asset/asset download not found"); + if (!asset_found) + { + reportError("Asset/asset download not found"); + return; + } +#else + // Just display the version and a download link. + m_ui.downloadAndInstall->setText(tr("Download...")); +#endif + + m_ui.downloadAndInstall->setEnabled(true); + m_ui.updateNotes->setText(tr("Loading...")); + queueGetChanges(); + + // We have to defer this, because it comes back through the timer/HTTP callback... + QMetaObject::invokeMethod(this, "exec", Qt::QueuedConnection); } else { @@ -420,7 +422,7 @@ void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, std::vector response) { -#ifdef AUTO_UPDATER_SUPPORTED +#ifdef UPDATE_CHECKER_SUPPORTED if (status_code == HTTPDownloader::HTTP_STATUS_OK) { QJsonParseError parse_error; @@ -506,6 +508,7 @@ void AutoUpdaterDialog::getChangesComplete(s32 status_code, std::vector resp void AutoUpdaterDialog::downloadUpdateClicked() { +#ifdef AUTO_UPDATER_SUPPORTED m_display_messages = true; std::optional download_result; @@ -539,8 +542,8 @@ void AutoUpdaterDialog::downloadUpdateClicked() }, &progress); - // Since we're going to block, don't allow the timer to poll, otherwise the progress callback can cause the timer to - // run, and recursively poll again. + // Since we're going to block, don't allow the timer to poll, otherwise the progress callback can cause the timer + // to run, and recursively poll again. m_http_poll_timer->stop(); // Block until completion. @@ -558,6 +561,9 @@ void AutoUpdaterDialog::downloadUpdateClicked() QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, true)); done(0); } +#elif defined(UPDATE_CHECKER_SUPPORTED) + QtUtils::OpenURL(this, fmt::format(fmt::runtime(DOWNLOAD_PAGE_URL), getCurrentUpdateTag())); +#endif } bool AutoUpdaterDialog::updateNeeded() const diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index f71161ea6..284cd0d20 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -153,9 +153,9 @@ void QtUtils::OpenURL(QWidget* parent, const QUrl& qurl) } } -void QtUtils::OpenURL(QWidget* parent, const char* url) +void QtUtils::OpenURL(QWidget* parent, const std::string_view url) { - return OpenURL(parent, QUrl::fromEncoded(QByteArray(url, static_cast(std::strlen(url))))); + return OpenURL(parent, QUrl::fromEncoded(QByteArray(url.data(), static_cast(url.length())))); } std::optional QtUtils::PromptForAddress(QWidget* parent, const QString& title, const QString& label, diff --git a/src/duckstation-qt/qtutils.h b/src/duckstation-qt/qtutils.h index d51956003..ac0bbb714 100644 --- a/src/duckstation-qt/qtutils.h +++ b/src/duckstation-qt/qtutils.h @@ -84,7 +84,7 @@ u32 KeyEventToCode(const QKeyEvent* ev); void OpenURL(QWidget* parent, const QUrl& qurl); /// Opens a URL string with the default handler. -void OpenURL(QWidget* parent, const char* url); +void OpenURL(QWidget* parent, const std::string_view url); /// Prompts for an address in hex. std::optional PromptForAddress(QWidget* parent, const QString& title, const QString& label, bool code);