aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-23 20:32:16 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-23 20:32:16 -0500
commit48b91ef0b04d8b8fc5ea003288cb72b5753fba34 (patch)
tree899894670ef37e350568f37dac6b8dbd35607b3c /src
parent7df72eb247b44cddb59ea46a241c3f877814fb70 (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.cpp47
-rw-r--r--src/src/settingsdialogupdates.cpp41
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);