From 2dc2f2e04888fcc7391ee544ee8ae3e3d042a297 Mon Sep 17 00:00:00 2001 From: saturneric Date: Sun, 1 Dec 2024 22:13:50 +0100 Subject: [PATCH] feat: check commit hash by current version --- src/m_ver_check/SoftwareVersion.cpp | 19 +- src/m_ver_check/SoftwareVersion.h | 15 +- src/m_ver_check/UpdateTab.cpp | 14 ++ src/m_ver_check/VersionCheckTask.cpp | 223 +++++++++++++--------- src/m_ver_check/VersionCheckTask.h | 27 ++- src/m_ver_check/VersionCheckingModule.cpp | 2 +- 6 files changed, 186 insertions(+), 114 deletions(-) diff --git a/src/m_ver_check/SoftwareVersion.cpp b/src/m_ver_check/SoftwareVersion.cpp index becc396..6c4ccf1 100644 --- a/src/m_ver_check/SoftwareVersion.cpp +++ b/src/m_ver_check/SoftwareVersion.cpp @@ -44,21 +44,24 @@ auto SoftwareVersion::NeedUpgrade() const -> bool { GFModuleStrDup(current_version.toUtf8()), GFModuleStrDup(latest_version.toUtf8())))); - MLogDebug(QString("load done: %1, pre-release: %2, draft: %3") - .arg(static_cast(loading_done)) - .arg(static_cast(latest_prerelease_version_from_remote)) - .arg(static_cast(latest_draft_from_remote))); - return loading_done && !latest_prerelease_version_from_remote && + FLOG_DEBUG("load done: %1, pre-release: %2, draft: %3", latest_version, + latest_prerelease_version_from_remote, latest_draft_from_remote); + return !latest_version.isEmpty() && !latest_prerelease_version_from_remote && !latest_draft_from_remote && GFCompareSoftwareVersion(GFModuleStrDup(current_version.toUtf8()), GFModuleStrDup(latest_version.toUtf8())) < 0; } auto SoftwareVersion::VersionWithdrawn() const -> bool { - return loading_done && !current_version_publish_in_remote && + return !latest_version.isEmpty() && !current_version_publish_in_remote && current_version_is_a_prerelease && !current_version_is_drafted; } auto SoftwareVersion::CurrentVersionReleased() const -> bool { - return loading_done && current_version_publish_in_remote; -} \ No newline at end of file + return !latest_version.isEmpty() && current_version_publish_in_remote; +} + +auto SoftwareVersion::GitCommitHashMismatch() const -> bool { + if (remote_commit_hash_by_tag.isEmpty()) return false; + return remote_commit_hash_by_tag.trimmed() != local_commit_hash.trimmed(); +} diff --git a/src/m_ver_check/SoftwareVersion.h b/src/m_ver_check/SoftwareVersion.h index 89158aa..f66c0dd 100644 --- a/src/m_ver_check/SoftwareVersion.h +++ b/src/m_ver_check/SoftwareVersion.h @@ -41,10 +41,11 @@ struct SoftwareVersion { bool latest_draft_from_remote = false; ///< bool current_version_is_a_prerelease = false; ///< bool current_version_is_drafted = false; ///< - bool loading_done = false; ///< bool current_version_publish_in_remote = false; ///< QString publish_date; ///< QString release_note; ///< + QString remote_commit_hash_by_tag; + QString local_commit_hash; /** * @brief @@ -52,7 +53,9 @@ struct SoftwareVersion { * @return true * @return false */ - [[nodiscard]] auto InfoValid() const -> bool { return loading_done; } + [[nodiscard]] auto IsInfoValid() const -> bool { + return !latest_version.isEmpty(); + } /** * @brief @@ -70,6 +73,14 @@ struct SoftwareVersion { */ [[nodiscard]] auto VersionWithdrawn() const -> bool; + /** + * @brief + * + * @return true + * @return false + */ + [[nodiscard]] auto GitCommitHashMismatch() const -> bool; + /** * @brief * diff --git a/src/m_ver_check/UpdateTab.cpp b/src/m_ver_check/UpdateTab.cpp index 3c4c2c4..64c2a86 100644 --- a/src/m_ver_check/UpdateTab.cpp +++ b/src/m_ver_check/UpdateTab.cpp @@ -120,6 +120,9 @@ void UpdateTab::slot_show_version_status() { auto is_current_version_released = GFModuleRetrieveRTValueOrDefaultBool( GFGetModuleID(), GFModuleStrDup("version.current_version_released"), 0); + auto is_git_commit_hash_mismatch = GFModuleRetrieveRTValueOrDefaultBool( + GFGetModuleID(), GFModuleStrDup("version.git_commit_hash_mismatch"), 0); + QString const latest_version = UDUP(GFModuleRetrieveRTValueOrDefault( GFGetModuleID(), GFModuleStrDup("version.latest_version"), GFModuleStrDup(""))); @@ -160,6 +163,17 @@ void UpdateTab::slot_show_version_status() { tr("here") + " " + tr("to download the latest stable version.") + ""); upgrade_label_->show(); + } else if (is_git_commit_hash_mismatch != 0) { + upgrade_label_->setText( + "
" + + tr("The current version's commit hash does not match the official " + "release. This may indicate a modified or unofficial build.") + + "
" + tr("Click") + + " " + + tr("here") + " " + + tr("to verify your installation or download the official version.") + + "
"); + upgrade_label_->show(); } else { upgrade_label_->setText( "
" + diff --git a/src/m_ver_check/VersionCheckTask.cpp b/src/m_ver_check/VersionCheckTask.cpp index 38572cc..f0da4bb 100644 --- a/src/m_ver_check/VersionCheckTask.cpp +++ b/src/m_ver_check/VersionCheckTask.cpp @@ -44,123 +44,146 @@ VersionCheckTask::VersionCheckTask() : network_manager_(new QNetworkAccessManager(this)), current_version_(GFProjectVersion()) { qRegisterMetaType("SoftwareVersion"); - version_.current_version = current_version_; + version_meta_data_.current_version = current_version_; + version_meta_data_.local_commit_hash = GFProjectGitCommitHash(); } auto VersionCheckTask::Run() -> int { - QString latest_version_url = - "https://api.github.com/repos/saturneric/gpgfrontend/releases/latest"; + QString base_url = "https://api.github.com/repos/saturneric/gpgfrontend"; + QList urls = { + {base_url + "/releases/latest"}, + {base_url + "/releases/tags/" + current_version_}, + {base_url + "/git/ref/tags/" + current_version_}, + }; - QNetworkRequest latest_request(latest_version_url); - latest_request.setHeader(QNetworkRequest::UserAgentHeader, - GFHttpRequestUserAgent()); + connect(network_manager_, &QNetworkAccessManager::finished, this, + &VersionCheckTask::slot_parse_reply); + + for (const QUrl& url : urls) { + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::UserAgentHeader, + GFHttpRequestUserAgent()); + QNetworkReply* reply = network_manager_->get(request); + replies_.append(reply); + } - latest_reply_ = network_manager_->get(latest_request); - connect(latest_reply_, &QNetworkReply::finished, this, - &VersionCheckTask::slot_parse_latest_version_info); return 0; } -void VersionCheckTask::slot_parse_latest_version_info() { - if (latest_reply_ == nullptr) { - version_.latest_version = current_version_; - version_.loading_done = false; - } else if (latest_reply_->error() != QNetworkReply::NoError) { - MLogError(QString("latest version request error: %1") - .arg(latest_reply_->errorString())); - version_.latest_version = current_version_; +void VersionCheckTask::slot_parse_reply(QNetworkReply* reply) { + if (reply->error() == QNetworkReply::NoError) { + FLOG_DEBUG("get reply from url: %1", reply->url().toString()); } else { - latest_reply_bytes_ = latest_reply_->readAll(); - auto latest_reply_json = QJsonDocument::fromJson(latest_reply_bytes_); - - if (latest_reply_json.isObject()) { - QString latest_version = latest_reply_json["tag_name"].toString(); - - QRegularExpression re(R"(^[vV](\d+\.)?(\d+\.)?(\*|\d+))"); - auto version_match = re.match(latest_version); - if (version_match.hasMatch()) { - latest_version = version_match.captured(0); - } else { - latest_version = current_version_; - MLogWarn(QString("latest version unknown, set to current version: %1") - .arg(current_version_)); - } - - bool prerelease = latest_reply_json["prerelease"].toBool(); - bool draft = latest_reply_json["draft"].toBool(); - auto publish_date = latest_reply_json["published_at"].toString(); - auto release_note = latest_reply_json["body"].toString(); - version_.latest_version = latest_version; - version_.latest_prerelease_version_from_remote = prerelease; - version_.latest_draft_from_remote = draft; - version_.publish_date = publish_date; - version_.release_note = release_note; - } else { - MLogWarn(QString("cannot parse data got from github: %1") - .arg(latest_reply_bytes_)); - } + FLOG_DEBUG("get reply from url: %1, error: %2", reply->url().toString(), + reply->errorString()); } - if (latest_reply_ != nullptr) { - latest_reply_->deleteLater(); + switch (replies_.indexOf(reply)) { + case 0: + slot_parse_latest_version_info(reply); + break; + case 1: + slot_parse_current_version_info(reply); + break; + case 2: + slot_parse_current_tag_info(reply); + break; } - try { - QString current_version_url = - "https://api.github.com/repos/saturneric/gpgfrontend/releases/tags/" + - current_version_; + replies_.removeAll(reply); + reply->deleteLater(); - QNetworkRequest current_request(current_version_url); - current_request.setHeader(QNetworkRequest::UserAgentHeader, - GFHttpRequestUserAgent()); - - current_reply_ = network_manager_->get(current_request); - - connect(current_reply_, &QNetworkReply::finished, this, - &VersionCheckTask::slot_parse_current_version_info); - } catch (...) { - GFModuleLogError("current version request create error"); + if (replies_.isEmpty()) { + slot_fill_grt_with_version_info(version_meta_data_); + emit SignalUpgradeVersion(version_meta_data_); } } -void VersionCheckTask::slot_parse_current_version_info() { - if (current_reply_ == nullptr) { - // loading done - version_.loading_done = false; - - } else if (current_reply_->error() != QNetworkReply::NoError) { - MLogError(QString("current version request network error: {}") - .arg(current_reply_->errorString())); - - // loading done - version_.loading_done = true; - version_.current_version_publish_in_remote = false; - } else { - version_.current_version_publish_in_remote = true; - current_reply_bytes_ = current_reply_->readAll(); - auto current_reply_json = QJsonDocument::fromJson(current_reply_bytes_); - - if (current_reply_json.isObject()) { - bool current_prerelease = current_reply_json["prerelease"].toBool(); - bool current_draft = current_reply_json["draft"].toBool(); - version_.latest_prerelease_version_from_remote = current_prerelease; - version_.latest_draft_from_remote = current_draft; - // loading done - version_.loading_done = true; - } else { - MLogWarn(QString("cannot parse data got from github: %1") - .arg(current_reply_bytes_)); - } +void VersionCheckTask::slot_parse_latest_version_info(QNetworkReply* reply) { + if (reply == nullptr || reply->error() != QNetworkReply::NoError) { + return; } - if (current_reply_ != nullptr) current_reply_->deleteLater(); + auto reply_bytes = reply->readAll(); + auto latest_reply_json = QJsonDocument::fromJson(reply_bytes); - slot_fill_grt_with_version_info(version_); - emit SignalUpgradeVersion(version_); + if (!latest_reply_json.isObject()) { + FLOG_WARN("cannot parse data from github: %1", reply_bytes); + return; + } + + QString latest_version = latest_reply_json["tag_name"].toString(); + + QRegularExpression re(R"(^[vV](\d+\.)?(\d+\.)?(\*|\d+))"); + auto version_match = re.match(latest_version); + if (version_match.hasMatch()) { + latest_version = version_match.captured(0); + } else { + latest_version = current_version_; + MLogWarn(QString("latest version unknown, set to current version: %1") + .arg(current_version_)); + } + + bool prerelease = latest_reply_json["prerelease"].toBool(); + bool draft = latest_reply_json["draft"].toBool(); + auto publish_date = latest_reply_json["published_at"].toString(); + auto release_note = latest_reply_json["body"].toString(); + version_meta_data_.latest_version = latest_version; + version_meta_data_.latest_prerelease_version_from_remote = prerelease; + version_meta_data_.latest_draft_from_remote = draft; + version_meta_data_.publish_date = publish_date; + version_meta_data_.release_note = release_note; +} + +void VersionCheckTask::slot_parse_current_version_info(QNetworkReply* reply) { + if (reply == nullptr || reply->error() != QNetworkReply::NoError) { + version_meta_data_.current_version_publish_in_remote = false; + return; + } + + version_meta_data_.current_version_publish_in_remote = true; + auto reply_bytes = reply->readAll(); + auto current_reply_json = QJsonDocument::fromJson(reply_bytes); + + if (!current_reply_json.isObject()) { + FLOG_WARN("cannot parse data from github: %1", reply_bytes); + return; + } + + bool current_prerelease = current_reply_json["prerelease"].toBool(); + bool current_draft = current_reply_json["draft"].toBool(); + version_meta_data_.latest_prerelease_version_from_remote = current_prerelease; + version_meta_data_.latest_draft_from_remote = current_draft; +} + +void VersionCheckTask::slot_parse_current_tag_info(QNetworkReply* reply) { + if (reply == nullptr || reply->error() != QNetworkReply::NoError) { + version_meta_data_.current_version_publish_in_remote = false; + return; + } + + version_meta_data_.current_version_publish_in_remote = true; + auto reply_bytes = reply->readAll(); + auto current_reply_json = QJsonDocument::fromJson(reply_bytes); + + if (!current_reply_json.isObject()) { + FLOG_WARN("cannot parse data from github: %1", reply_bytes); + return; + } + + auto object = current_reply_json["object"].toObject(); + if (object["type"].toString() != "commit") { + FLOG_WARN("remote tag: %1 is not a ref: %2", + version_meta_data_.current_version, object["type"].toString()); + return; + } + + auto sha = object["sha"].toString(); + version_meta_data_.remote_commit_hash_by_tag = sha; } void VersionCheckTask::slot_fill_grt_with_version_info( - const SoftwareVersion &version) { + const SoftwareVersion& version) { GFModuleLogDebug("filling software information info in rt..."); GFModuleUpsertRTValue(GFGetModuleID(), @@ -169,6 +192,13 @@ void VersionCheckTask::slot_fill_grt_with_version_info( GFModuleUpsertRTValue(GFGetModuleID(), GFModuleStrDup("version.latest_version"), GFModuleStrDup(version.latest_version.toUtf8())); + GFModuleUpsertRTValue( + GFGetModuleID(), GFModuleStrDup("version.remote_commit_hash_by_tag"), + GFModuleStrDup(version.remote_commit_hash_by_tag.toUtf8())); + GFModuleUpsertRTValue(GFGetModuleID(), + GFModuleStrDup("version.local_commit_hash"), + GFModuleStrDup(version.local_commit_hash.toUtf8())); + GFModuleUpsertRTValueBool( GFGetModuleID(), GFModuleStrDup("version.current_version_is_drafted"), version.current_version_is_drafted ? 1 : 0); @@ -193,12 +223,15 @@ void VersionCheckTask::slot_fill_grt_with_version_info( GFModuleUpsertRTValueBool( GFGetModuleID(), GFModuleStrDup("version.current_a_withdrawn_version"), version.VersionWithdrawn() ? 1 : 0); + GFModuleUpsertRTValueBool(GFGetModuleID(), + GFModuleStrDup("version.git_commit_hash_mismatch"), + version.GitCommitHashMismatch() ? 1 : 0); GFModuleUpsertRTValue(GFGetModuleID(), GFModuleStrDup("version.release_note"), GFModuleStrDup(version.release_note.toUtf8())); GFModuleUpsertRTValueBool(GFGetModuleID(), GFModuleStrDup("version.loading_done"), - version.loading_done ? 1 : 0); + version.IsInfoValid() ? 1 : 0); GFModuleLogDebug("software information filled in rt"); } diff --git a/src/m_ver_check/VersionCheckTask.h b/src/m_ver_check/VersionCheckTask.h index 3d9c6b6..764c46e 100644 --- a/src/m_ver_check/VersionCheckTask.h +++ b/src/m_ver_check/VersionCheckTask.h @@ -70,14 +70,28 @@ class VersionCheckTask : public QObject { * @brief * */ - void slot_parse_latest_version_info(); + void slot_parse_reply(QNetworkReply* reply); /** * @brief * + * @param reply */ - void slot_parse_current_version_info(); + void slot_parse_latest_version_info(QNetworkReply* reply); + /** + * @brief + * + * @param reply + */ + void slot_parse_current_version_info(QNetworkReply* reply); + + /** + * @brief + * + * @param reply + */ + void slot_parse_current_tag_info(QNetworkReply* reply); /** * @brief * @@ -85,11 +99,8 @@ class VersionCheckTask : public QObject { void slot_fill_grt_with_version_info(const SoftwareVersion&); private: - QByteArray latest_reply_bytes_; ///< - QByteArray current_reply_bytes_; ///< - QNetworkReply* latest_reply_ = nullptr; ///< latest version info reply - QNetworkReply* current_reply_ = nullptr; ///< current version info reply + QList replies_; ///< QNetworkAccessManager* network_manager_; ///< - QString current_version_; - SoftwareVersion version_; + QString current_version_; ///< + SoftwareVersion version_meta_data_; }; diff --git a/src/m_ver_check/VersionCheckingModule.cpp b/src/m_ver_check/VersionCheckingModule.cpp index 723e833..d672fe7 100644 --- a/src/m_ver_check/VersionCheckingModule.cpp +++ b/src/m_ver_check/VersionCheckingModule.cpp @@ -44,7 +44,7 @@ #include "VersionCheckTask.h" GF_MODULE_API_DEFINE("com.bktus.gpgfrontend.module.version_checking", - "VersionChecking", "1.1.0", + "VersionChecking", "1.2.0", "Try checking GpgFrontend version.", "Saturneric"); DEFINE_TRANSLATIONS_STRUCTURE(ModuleVersionChecking);