mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 01:23:24 -08:00
Fix and rework client updater logic (#7038)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* first version * combined logic + better regex * Simplify, we only support 64bit, installer prevents wrong installs * lint + fix Linux versions (missing declaration) * update regex * improve/add comments * comments * ReleaseChannel: parse non-numeric OS versions on Windows Server QSysInfo::productVersion() returns "Server 2022" on Windows Server, so splitting on '.' and calling toInt() made the version unparseable and disabled the in-client updater there. Extract the first numeric token instead; Server 2022 becomes 2022 and matches the Win10 assets. * ReleaseChannel: don't offer 64-bit assets on 32-bit Windows The old downloadMatchesCurrentOS() had an explicit 32-bit branch that only accepted "32bit" assets. Restore that guard: 32-bit Windows builds return no match instead of being offered the 64-bit Win10 installer that won't run. * ReleaseChannel: only query the OS version on updater platforms The in-client updater only ships assets for macOS and Windows, but the host system version was still being resolved on every platform, including Linux where it is never used. Compute it only under the Q_OS_MACOS / Q_OS_WIN guards. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
0934380a5c
commit
5dc5390880
2 changed files with 106 additions and 60 deletions
|
|
@ -10,6 +10,7 @@
|
|||
#include <QRegularExpression>
|
||||
#include <QSysInfo>
|
||||
#include <QtGlobal>
|
||||
#include <optional>
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
#include <sys/sysctl.h>
|
||||
|
|
@ -26,6 +27,29 @@
|
|||
|
||||
#define GIT_SHORT_HASH_LEN 7
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||
// QSysInfo::productVersion() is not guaranteed to start with a number, e.g. it returns
|
||||
// "Server 2022" on Windows Server. Use the first numeric token as the major host version.
|
||||
std::optional<int> getProductVersionMajor()
|
||||
{
|
||||
const QString versionString = QSysInfo::productVersion();
|
||||
bool ok = false;
|
||||
static const QRegularExpression firstNumber(R"((\d+))");
|
||||
const auto match = firstNumber.match(versionString);
|
||||
const int version = match.hasMatch() ? match.captured(1).toInt(&ok) : 0;
|
||||
if (!ok) {
|
||||
qCWarning(ReleaseChannelLog) << "Unable to determine OS version from" << versionString;
|
||||
return std::nullopt;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
ReleaseChannel::ReleaseChannel() : netMan(new QNetworkAccessManager(this)), response(nullptr), lastRelease(nullptr)
|
||||
{
|
||||
}
|
||||
|
|
@ -43,55 +67,93 @@ void ReleaseChannel::checkForUpdates()
|
|||
connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished);
|
||||
}
|
||||
|
||||
// Different release channel checking functions for different operating systems
|
||||
bool ReleaseChannel::downloadMatchesCurrentOS(const QString &fileName)
|
||||
// Find assets compatible with host platform (Linux is not supported by in-client updater)
|
||||
std::optional<int> ReleaseChannel::getTargetVersionForCurrentOS(const QString &fileName)
|
||||
{
|
||||
#if defined(Q_OS_MACOS)
|
||||
static QRegularExpression version_regex("macOS(\\d+)");
|
||||
auto match = version_regex.match(fileName);
|
||||
if (!match.hasMatch()) {
|
||||
return false;
|
||||
#if defined(Q_OS_WIN) && Q_PROCESSOR_WORDSIZE == 4
|
||||
// The published Windows assets are 64-bit only
|
||||
Q_UNUSED(fileName);
|
||||
return std::nullopt;
|
||||
#endif
|
||||
|
||||
const QRegularExpression *regex = nullptr;
|
||||
|
||||
// Only platforms shipping updater assets (macOS, Windows) need the host version
|
||||
static const std::optional<int> systemVersion =
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||
getProductVersionMajor();
|
||||
#else
|
||||
std::nullopt;
|
||||
#endif
|
||||
|
||||
if (!systemVersion) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto getSystemVersion = [] {
|
||||
#if defined(Q_OS_MACOS)
|
||||
static const bool isIntel = [] {
|
||||
// QSysInfo does not go through translation layers
|
||||
// We need to use sysctl to reliably detect the underlying architecture
|
||||
char arch[255];
|
||||
size_t len = sizeof(arch);
|
||||
if (sysctlbyname("machdep.cpu.brand_string", arch, &len, nullptr, 0) == 0) {
|
||||
// Intel mac is only supported on macOS 13 versions
|
||||
if (QString::fromUtf8(arch).contains("Intel")) {
|
||||
return 13;
|
||||
}
|
||||
return QString::fromUtf8(arch).contains("Intel");
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
return QSysInfo::productVersion().split(".")[0].toInt();
|
||||
};
|
||||
|
||||
// older(smaller) releases are compatible with a newer or the same system version
|
||||
int sys_maj = getSystemVersion();
|
||||
int rel_maj = match.captured(1).toInt();
|
||||
return rel_maj == sys_maj;
|
||||
// Apple (ARM) --> Cockatrice-3.0.0-macOS15.dmg
|
||||
// Apple (x86) --> Cockatrice-3.0.0-macOS15_Intel.dmg
|
||||
static const QRegularExpression macIntelRegex(R"(-macOS(\d+)_Intel\.[^.]+$)",
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
static const QRegularExpression macRegex(R"(-macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption);
|
||||
regex = isIntel ? &macIntelRegex : &macRegex;
|
||||
|
||||
#elif defined(Q_OS_WIN)
|
||||
#if Q_PROCESSOR_WORDSIZE == 4
|
||||
return fileName.contains("32bit");
|
||||
#elif Q_PROCESSOR_WORDSIZE == 8
|
||||
const QString &version = QSysInfo::productVersion();
|
||||
if (version.startsWith("7") || version.startsWith("8")) {
|
||||
return fileName.contains("Win7");
|
||||
} else {
|
||||
return fileName.contains("Win10");
|
||||
}
|
||||
#else
|
||||
// Windows (x86) --> Cockatrice-3.0.0-Windows10.exe
|
||||
static const QRegularExpression winRegex(R"(-(?:Win|Windows)(\d+)\.[^.]+$)",
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
regex = &winRegex;
|
||||
|
||||
#else // if the OS doesn't fit one of the above #defines, then it will never match
|
||||
Q_UNUSED(fileName);
|
||||
return false;
|
||||
return std::nullopt;
|
||||
#endif
|
||||
|
||||
#else // If the OS doesn't fit one of the above #defines, then it will never match
|
||||
Q_UNUSED(fileName);
|
||||
return false;
|
||||
#endif
|
||||
// Any asset targeting an OS version up to the current host version works
|
||||
auto match = regex->match(fileName);
|
||||
if (!match.hasMatch()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const int targetVersion = match.captured(1).toInt();
|
||||
if (targetVersion > *systemVersion) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return targetVersion;
|
||||
}
|
||||
|
||||
// Pick newest targeted version (highest number) amongst all compatible ones
|
||||
QString ReleaseChannel::findBestDownloadUrl(const QVariantList &assets)
|
||||
{
|
||||
QString bestUrl;
|
||||
int bestVersion = -1;
|
||||
for (const auto &rawAsset : assets) {
|
||||
QVariantMap asset = rawAsset.toMap();
|
||||
QString name = asset["name"].toString();
|
||||
QString url = asset["browser_download_url"].toString();
|
||||
auto version = getTargetVersionForCurrentOS(name);
|
||||
if (!version) {
|
||||
continue;
|
||||
}
|
||||
if (*version > bestVersion) {
|
||||
bestVersion = *version;
|
||||
bestUrl = url;
|
||||
}
|
||||
}
|
||||
if (!bestUrl.isEmpty()) {
|
||||
qCInfo(ReleaseChannelLog) << "Best compatible asset=" << bestUrl;
|
||||
}
|
||||
return bestUrl;
|
||||
}
|
||||
|
||||
QString StableReleaseChannel::getManualDownloadUrl() const
|
||||
|
|
@ -138,16 +200,9 @@ void StableReleaseChannel::releaseListFinished()
|
|||
lastRelease->setPublishDate(resultMap["published_at"].toDate());
|
||||
|
||||
if (resultMap.contains("assets")) {
|
||||
auto rawAssets = resultMap["assets"].toList();
|
||||
for (const auto &rawAsset : rawAssets) {
|
||||
QVariantMap asset = rawAsset.toMap();
|
||||
QString name = asset["name"].toString();
|
||||
QString url = asset["browser_download_url"].toString();
|
||||
|
||||
if (downloadMatchesCurrentOS(name)) {
|
||||
lastRelease->setDownloadUrl(url);
|
||||
break;
|
||||
}
|
||||
auto url = findBestDownloadUrl(resultMap["assets"].toList());
|
||||
if (!url.isEmpty()) {
|
||||
lastRelease->setDownloadUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,21 +344,10 @@ void BetaReleaseChannel::fileListFinished()
|
|||
bool needToUpdate = (QString::compare(shortHash, myHash, Qt::CaseInsensitive) != 0);
|
||||
bool compatibleVersion = false;
|
||||
|
||||
QStringList resultUrlList{};
|
||||
for (QVariant file : resultList) {
|
||||
QVariantMap map = file.toMap();
|
||||
resultUrlList << map["browser_download_url"].toString();
|
||||
}
|
||||
|
||||
resultUrlList.sort();
|
||||
// iterate in reverse so the first item is the latest os version
|
||||
for (auto url = resultUrlList.rbegin(); url < resultUrlList.rend(); ++url) {
|
||||
if (downloadMatchesCurrentOS(*url)) {
|
||||
compatibleVersion = true;
|
||||
lastRelease->setDownloadUrl(*url);
|
||||
qCInfo(ReleaseChannelLog) << "Found compatible version url=" << *url;
|
||||
break;
|
||||
}
|
||||
QString downloadUrl = findBestDownloadUrl(resultList);
|
||||
if (!downloadUrl.isEmpty()) {
|
||||
compatibleVersion = true;
|
||||
lastRelease->setDownloadUrl(downloadUrl);
|
||||
}
|
||||
|
||||
emit finishedCheck(needToUpdate, compatibleVersion, lastRelease);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(ReleaseChannelLog, "release_channel");
|
||||
|
|
@ -96,7 +97,8 @@ protected:
|
|||
Release *lastRelease;
|
||||
|
||||
protected:
|
||||
static bool downloadMatchesCurrentOS(const QString &fileName);
|
||||
static std::optional<int> getTargetVersionForCurrentOS(const QString &fileName);
|
||||
static QString findBestDownloadUrl(const QVariantList &assets);
|
||||
[[nodiscard]] virtual QString getReleaseChannelUrl() const = 0;
|
||||
|
||||
public:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue