diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-23 20:32:16 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-23 20:32:16 -0500 |
| commit | 48b91ef0b04d8b8fc5ea003288cb72b5753fba34 (patch) | |
| tree | 899894670ef37e350568f37dac6b8dbd35607b3c /src | |
| parent | 7df72eb247b44cddb59ea46a241c3f877814fb70 (diff) | |
Updater: accept .zip release assets too
GitHub Releases serves whatever's uploaded, but some CI flows produce
.zip artifacts (or users re-upload as zip). Teach the release-info
parser to pick up .zip when no .tar.gz is attached, and teach the
install flow to call `unzip` vs `tar` based on the URL extension.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/src/fluorineupdater.cpp | 47 | ||||
| -rw-r--r-- | src/src/settingsdialogupdates.cpp | 41 |
2 files changed, 63 insertions, 25 deletions
diff --git a/src/src/fluorineupdater.cpp b/src/src/fluorineupdater.cpp index dd96eb9..0d11786 100644 --- a/src/src/fluorineupdater.cpp +++ b/src/src/fluorineupdater.cpp @@ -221,16 +221,26 @@ bool FluorineUpdater::parseStableRelease(const QJsonObject& obj, out.versionString.remove(0, 1); } - // Pick the first tar.gz asset as the download target. + // Pick a tar.gz asset if available, otherwise a zip. const QJsonArray assets = obj.value(QStringLiteral("assets")).toArray(); + QString zipFallback; for (const QJsonValue& v : assets) { const QJsonObject a = v.toObject(); const QString name = a.value(QStringLiteral("name")).toString(); - if (name.endsWith(QStringLiteral(".tar.gz"), Qt::CaseInsensitive)) { - out.downloadUrl = - a.value(QStringLiteral("browser_download_url")).toString(); + const QString url = + a.value(QStringLiteral("browser_download_url")).toString(); + if (name.endsWith(QStringLiteral(".tar.gz"), Qt::CaseInsensitive) || + name.endsWith(QStringLiteral(".tgz"), Qt::CaseInsensitive)) { + out.downloadUrl = url; break; } + if (name.endsWith(QStringLiteral(".zip"), Qt::CaseInsensitive) && + zipFallback.isEmpty()) { + zipFallback = url; + } + } + if (out.downloadUrl.isEmpty()) { + out.downloadUrl = zipFallback; } return true; } @@ -277,27 +287,42 @@ bool FluorineUpdater::parseBetaRelease(const QJsonObject& obj, } } - // Prefer a tar.gz asset with "beta" in the name, else first tar.gz. + // Prefer a .tar.gz asset with "beta" in the name, else any .tar.gz, + // else fall back to a .zip. GitHub Releases serves whatever we upload, + // but some CI flows produce zip artifacts; accepting both lets users + // swap formats without breaking the in-app updater. const QJsonArray assets = obj.value(QStringLiteral("assets")).toArray(); - QString fallback; + QString tarFallback; + QString zipFallback; + auto isArchive = [](const QString& name) { + return name.endsWith(QStringLiteral(".tar.gz"), Qt::CaseInsensitive) || + name.endsWith(QStringLiteral(".tgz"), Qt::CaseInsensitive) || + name.endsWith(QStringLiteral(".zip"), Qt::CaseInsensitive); + }; + auto isZip = [](const QString& name) { + return name.endsWith(QStringLiteral(".zip"), Qt::CaseInsensitive); + }; for (const QJsonValue& v : assets) { const QJsonObject a = v.toObject(); const QString name = a.value(QStringLiteral("name")).toString(); - if (!name.endsWith(QStringLiteral(".tar.gz"), Qt::CaseInsensitive)) { + if (!isArchive(name)) { continue; } const QString url = a.value(QStringLiteral("browser_download_url")).toString(); - if (name.contains(QStringLiteral("beta"), Qt::CaseInsensitive)) { + if (name.contains(QStringLiteral("beta"), Qt::CaseInsensitive) && + !isZip(name)) { out.downloadUrl = url; break; } - if (fallback.isEmpty()) { - fallback = url; + if (isZip(name)) { + if (zipFallback.isEmpty()) zipFallback = url; + } else if (tarFallback.isEmpty()) { + tarFallback = url; } } if (out.downloadUrl.isEmpty()) { - out.downloadUrl = fallback; + out.downloadUrl = !tarFallback.isEmpty() ? tarFallback : zipFallback; } return true; } diff --git a/src/src/settingsdialogupdates.cpp b/src/src/settingsdialogupdates.cpp index b903975..83c698c 100644 --- a/src/src/settingsdialogupdates.cpp +++ b/src/src/settingsdialogupdates.cpp @@ -216,10 +216,16 @@ void UpdatesSettingsTab::onInstall() QStringLiteral("/fluorine"); const QString stagingDir = dataRoot + QStringLiteral("/update-staging"); QDir().mkpath(stagingDir); - const QString tarPath = stagingDir + QStringLiteral("/download.tar.gz"); + // Pick archive file extension from the download URL so tar vs unzip + // picks the right tool below. + const bool isZip = + downloadUrl.endsWith(QStringLiteral(".zip"), Qt::CaseInsensitive); + const QString archivePath = + stagingDir + (isZip ? QStringLiteral("/download.zip") + : QStringLiteral("/download.tar.gz")); const QString extractDir = stagingDir + QStringLiteral("/extract"); - QFile::remove(tarPath); + QFile::remove(archivePath); QDir(extractDir).removeRecursively(); QDir().mkpath(extractDir); @@ -237,11 +243,11 @@ void UpdatesSettingsTab::onInstall() QNetworkRequest::NoLessSafeRedirectPolicy); QNetworkReply* reply = nam->get(req); - auto* outFile = new QFile(tarPath, reply); + auto* outFile = new QFile(archivePath, reply); if (!outFile->open(QIODevice::WriteOnly)) { m_statusLabel->setText( tr("<i>Install failed:</i> cannot open '%1' for writing") - .arg(tarPath)); + .arg(archivePath)); m_progressBar->setVisible(false); m_installButton->setEnabled(true); m_checkNowButton->setEnabled(true); @@ -266,7 +272,7 @@ void UpdatesSettingsTab::onInstall() }); QObject::connect( reply, &QNetworkReply::finished, m_installButton, - [this, reply, outFile, tarPath, extractDir, stagingDir]() { + [this, reply, outFile, archivePath, extractDir, stagingDir, isZip]() { outFile->close(); const QNetworkReply::NetworkError err = reply->error(); const QString errString = reply->errorString(); @@ -282,22 +288,29 @@ void UpdatesSettingsTab::onInstall() } m_statusLabel->setText(tr("Extracting update…")); - m_progressBar->setRange(0, 0); // indeterminate while tar runs + m_progressBar->setRange(0, 0); // indeterminate while extract runs - auto* tar = new QProcess(m_installButton); - tar->setWorkingDirectory(extractDir); - tar->setProgram(QStringLiteral("tar")); - tar->setArguments({QStringLiteral("xzf"), tarPath}); + auto* extractor = new QProcess(m_installButton); + extractor->setWorkingDirectory(extractDir); + if (isZip) { + extractor->setProgram(QStringLiteral("unzip")); + extractor->setArguments( + {QStringLiteral("-q"), archivePath}); + } else { + extractor->setProgram(QStringLiteral("tar")); + extractor->setArguments( + {QStringLiteral("xzf"), archivePath}); + } QObject::connect( - tar, + extractor, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), m_installButton, - [this, tar, tarPath, extractDir, stagingDir]( + [this, extractor, archivePath, extractDir, stagingDir]( int code, QProcess::ExitStatus st) { - tar->deleteLater(); + extractor->deleteLater(); if (st != QProcess::NormalExit || code != 0) { m_statusLabel->setText( - tr("<i>Extraction failed:</i> tar exited with %1") + tr("<i>Extraction failed:</i> extractor exited with %1") .arg(code)); m_progressBar->setVisible(false); m_installButton->setEnabled(true); |
