This commit is contained in:
tooomm 2026-09-20 20:40:09 +02:00 committed by GitHub
commit fd3eb1571f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 80 additions and 60 deletions

View file

@ -10,6 +10,7 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <QSysInfo> #include <QSysInfo>
#include <QtGlobal> #include <QtGlobal>
#include <optional>
#if defined(Q_OS_MACOS) #if defined(Q_OS_MACOS)
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -43,55 +44,90 @@ void ReleaseChannel::checkForUpdates()
connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished); connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished);
} }
// Different release channel checking functions for different operating systems // Find assets compatible with host platform (Linux is not supported by in-client updater)
bool ReleaseChannel::downloadMatchesCurrentOS(const QString &fileName) std::optional<int> ReleaseChannel::getTargetVersionForCurrentOS(const QString &fileName)
{ {
#if defined(Q_OS_MACOS) const QRegularExpression *regex = nullptr;
static QRegularExpression version_regex("macOS(\\d+)");
auto match = version_regex.match(fileName); static const std::optional<int> systemVersion = [] {
if (!match.hasMatch()) { bool ok = false;
return false; const QString versionString = QSysInfo::productVersion();
const int version = versionString.split('.').first().toInt(&ok);
if (!ok) {
qCWarning(ReleaseChannelLog) << "Unable to determine OS version from" << versionString;
return std::optional<int>();
}
return std::optional<int>{version};
}();
if (!systemVersion) {
return std::nullopt;
} }
auto getSystemVersion = [] { #if defined(Q_OS_MACOS)
static const bool isIntel = [] {
// QSysInfo does not go through translation layers // QSysInfo does not go through translation layers
// We need to use sysctl to reliably detect the underlying architecture // We need to use sysctl to reliably detect the underlying architecture
char arch[255]; char arch[255];
size_t len = sizeof(arch); size_t len = sizeof(arch);
if (sysctlbyname("machdep.cpu.brand_string", arch, &len, nullptr, 0) == 0) { if (sysctlbyname("machdep.cpu.brand_string", arch, &len, nullptr, 0) == 0) {
// Intel mac is only supported on macOS 13 versions return QString::fromUtf8(arch).contains("Intel");
if (QString::fromUtf8(arch).contains("Intel")) {
return 13;
}
} }
return false;
}();
return QSysInfo::productVersion().split(".")[0].toInt(); // 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\.[^.]+$)",
// older(smaller) releases are compatible with a newer or the same system version QRegularExpression::CaseInsensitiveOption);
int sys_maj = getSystemVersion(); static const QRegularExpression macRegex(R"(-macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption);
int rel_maj = match.captured(1).toInt(); regex = isIntel ? &macIntelRegex : &macRegex;
return rel_maj == sys_maj;
#elif defined(Q_OS_WIN) #elif defined(Q_OS_WIN)
#if Q_PROCESSOR_WORDSIZE == 4 // Windows (x86) --> Cockatrice-3.0.0-Windows10.exe
return fileName.contains("32bit"); static const QRegularExpression winRegex(R"(-(?:Win|Windows)(\d+)\.[^.]+$)",
#elif Q_PROCESSOR_WORDSIZE == 8 QRegularExpression::CaseInsensitiveOption);
const QString &version = QSysInfo::productVersion(); regex = &winRegex;
if (version.startsWith("7") || version.startsWith("8")) {
return fileName.contains("Win7"); #else // if the OS doesn't fit one of the above #defines, then it will never match
} else {
return fileName.contains("Win10");
}
#else
Q_UNUSED(fileName); Q_UNUSED(fileName);
return false; return std::nullopt;
#endif #endif
#else // If the OS doesn't fit one of the above #defines, then it will never match // Any asset targeting an OS version up to the current host version works
Q_UNUSED(fileName); auto match = regex->match(fileName);
return false; if (!match.hasMatch()) {
#endif 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 QString StableReleaseChannel::getManualDownloadUrl() const
@ -138,16 +174,9 @@ void StableReleaseChannel::releaseListFinished()
lastRelease->setPublishDate(resultMap["published_at"].toDate()); lastRelease->setPublishDate(resultMap["published_at"].toDate());
if (resultMap.contains("assets")) { if (resultMap.contains("assets")) {
auto rawAssets = resultMap["assets"].toList(); auto url = findBestDownloadUrl(resultMap["assets"].toList());
for (const auto &rawAsset : rawAssets) { if (!url.isEmpty()) {
QVariantMap asset = rawAsset.toMap();
QString name = asset["name"].toString();
QString url = asset["browser_download_url"].toString();
if (downloadMatchesCurrentOS(name)) {
lastRelease->setDownloadUrl(url); lastRelease->setDownloadUrl(url);
break;
}
} }
} }
@ -289,21 +318,10 @@ void BetaReleaseChannel::fileListFinished()
bool needToUpdate = (QString::compare(shortHash, myHash, Qt::CaseInsensitive) != 0); bool needToUpdate = (QString::compare(shortHash, myHash, Qt::CaseInsensitive) != 0);
bool compatibleVersion = false; bool compatibleVersion = false;
QStringList resultUrlList{}; QString downloadUrl = findBestDownloadUrl(resultList);
for (QVariant file : resultList) { if (!downloadUrl.isEmpty()) {
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; compatibleVersion = true;
lastRelease->setDownloadUrl(*url); lastRelease->setDownloadUrl(downloadUrl);
qCInfo(ReleaseChannelLog) << "Found compatible version url=" << *url;
break;
}
} }
emit finishedCheck(needToUpdate, compatibleVersion, lastRelease); emit finishedCheck(needToUpdate, compatibleVersion, lastRelease);

View file

@ -12,6 +12,7 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariantMap> #include <QVariantMap>
#include <optional>
#include <utility> #include <utility>
inline Q_LOGGING_CATEGORY(ReleaseChannelLog, "release_channel"); inline Q_LOGGING_CATEGORY(ReleaseChannelLog, "release_channel");
@ -96,7 +97,8 @@ protected:
Release *lastRelease; Release *lastRelease;
protected: 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; [[nodiscard]] virtual QString getReleaseChannelUrl() const = 0;
public: public: