From 2ec22e9be83dd2fcba7e0670d823d3d6c1e98a0b Mon Sep 17 00:00:00 2001 From: TableFlipper9 Date: Fri, 1 Aug 2025 12:00:16 +0300 Subject: [PATCH] Calamares 3.3.14: introducing Gentoo Stage3 Chooser * introduced stage3 choosing module * created boxes to choose mirrors, arches and stage3 archives * introduced simple warning to new user regarding blank mirror, which means default mirror * error showing for faild fetches or network problems Signed-off-by: Morovan Mihai --- src/modules/stagechoose/CMakeLists.txt | 13 ++ src/modules/stagechoose/Config.cpp | 128 +++++++++++++ src/modules/stagechoose/Config.h | 65 +++++++ src/modules/stagechoose/SetStage3Job.cpp | 61 +++++++ src/modules/stagechoose/SetStage3Job.h | 22 +++ src/modules/stagechoose/StageChoosePage.cpp | 145 +++++++++++++++ src/modules/stagechoose/StageChoosePage.h | 51 ++++++ src/modules/stagechoose/StageChoosePage.ui | 172 ++++++++++++++++++ .../stagechoose/StageChooseViewStep.cpp | 80 ++++++++ src/modules/stagechoose/StageChooseViewStep.h | 53 ++++++ src/modules/stagechoose/StageFetcher.cpp | 152 ++++++++++++++++ src/modules/stagechoose/StageFetcher.h | 42 +++++ src/modules/stagechoose/stagechoose.conf | 7 + .../stagechoose/stagechoose.schema.yaml | 17 ++ 14 files changed, 1008 insertions(+) create mode 100644 src/modules/stagechoose/CMakeLists.txt create mode 100644 src/modules/stagechoose/Config.cpp create mode 100644 src/modules/stagechoose/Config.h create mode 100644 src/modules/stagechoose/SetStage3Job.cpp create mode 100644 src/modules/stagechoose/SetStage3Job.h create mode 100644 src/modules/stagechoose/StageChoosePage.cpp create mode 100644 src/modules/stagechoose/StageChoosePage.h create mode 100644 src/modules/stagechoose/StageChoosePage.ui create mode 100644 src/modules/stagechoose/StageChooseViewStep.cpp create mode 100644 src/modules/stagechoose/StageChooseViewStep.h create mode 100644 src/modules/stagechoose/StageFetcher.cpp create mode 100644 src/modules/stagechoose/StageFetcher.h create mode 100644 src/modules/stagechoose/stagechoose.conf create mode 100644 src/modules/stagechoose/stagechoose.schema.yaml diff --git a/src/modules/stagechoose/CMakeLists.txt b/src/modules/stagechoose/CMakeLists.txt new file mode 100644 index 0000000000..f1ee399f9e --- /dev/null +++ b/src/modules/stagechoose/CMakeLists.txt @@ -0,0 +1,13 @@ +calamares_add_plugin(stagechoose + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + Config.cpp + StageChooseViewStep.cpp + StageChoosePage.cpp + SetStage3Job.cpp + StageFetcher.cpp + UI + StageChoosePage.ui + SHARED_LIB +) diff --git a/src/modules/stagechoose/Config.cpp b/src/modules/stagechoose/Config.cpp new file mode 100644 index 0000000000..ac5feb3960 --- /dev/null +++ b/src/modules/stagechoose/Config.cpp @@ -0,0 +1,128 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "Config.h" +#include "locale/Global.h" +#include "JobQueue.h" +#include "GlobalStorage.h" +#include "StageFetcher.h" + +#include + +Config::Config(QObject* parent) + : QObject(parent) + , m_fetcher(new StageFetcher(this)) +{ + connect(m_fetcher, &StageFetcher::variantsFetched, this, [this](const QStringList &variants) { + emit variantsReady(variants); + }); + + connect(m_fetcher, &StageFetcher::tarballFetched, this, [this](const QString &tarball) { + updateTarball(tarball); + }); + + connect(m_fetcher, &StageFetcher::fetchStatusChanged,this,&Config::fetchStatusChanged); + connect(m_fetcher, &StageFetcher::fetchError,this,&Config::fetchError); + /// change Config into function handles the fetcher signals + m_fetcher->setMirrorBase(m_mirrorBase); +} + +QList Config::availableArchitecturesInfo() +{ + QList list; + list << ArchitectureInfo{ QStringLiteral("alpha"), QStringLiteral("Digital Alpha (alpha)") } + << ArchitectureInfo{ QStringLiteral("amd64"), QStringLiteral("64-bit Intel/AMD (amd64)") } + << ArchitectureInfo{ QStringLiteral("x86"), QStringLiteral("32-bit Intel/AMD (x86)") } + << ArchitectureInfo{ QStringLiteral("arm"), QStringLiteral("ARM 32-bit (arm)") } + << ArchitectureInfo{ QStringLiteral("arm64"), QStringLiteral("ARM 64-bit (arm64)") } + << ArchitectureInfo{ QStringLiteral("hppa"), QStringLiteral("HPPA (hppa)") } + << ArchitectureInfo{ QStringLiteral("ia64"), QStringLiteral("Intel Itanium (ia64)") } + << ArchitectureInfo{ QStringLiteral("loong"), QStringLiteral("Loongson MIPS-based (loong)") } + << ArchitectureInfo{ QStringLiteral("m68k"), QStringLiteral("Motorola 68k (m68k)") } + << ArchitectureInfo{ QStringLiteral("mips"), QStringLiteral("MIPS 32/64-bit (mips)") } + << ArchitectureInfo{ QStringLiteral("ppc"), QStringLiteral("PowerPC (ppc)") } + << ArchitectureInfo{ QStringLiteral("riscv"), QStringLiteral("RISC-V 32/64-bit (riscv)") } + << ArchitectureInfo{ QStringLiteral("s390"), QStringLiteral("IBM System z (s390)") } + << ArchitectureInfo{ QStringLiteral("sh"), QStringLiteral("SuperH legacy (sh)") } + << ArchitectureInfo{ QStringLiteral("sparc"), QStringLiteral("SPARC 64-bit (sparc)") } + << ArchitectureInfo{ QStringLiteral("livecd"), QStringLiteral("Live CD (unsafe)") }; + return list; +} + +void Config::availableStagesFor(const QString& arch) +{ + m_selectedArch = arch; + m_selectedVariant.clear(); + if(arch == "livecd"){ + m_fetcher->cancelOngoingRequest(); + m_selectedTarball = "livecd"; + emit tarballReady(m_selectedTarball); + emit fetchStatusChanged("LiveCD mode"); + emit validityChanged(isValid()); + return; + } + else{ + m_selectedTarball.clear(); + m_fetcher->fetchVariants(arch); + } +} + +void Config::selectVariant(const QString& variant) +{ + m_selectedVariant = variant; + + m_fetcher->fetchLatestTarball(m_selectedArch,variant); +} + +QString Config::selectedStage3() const +{ + if(!m_selectedTarball.isEmpty()) + return m_selectedTarball; + + return "No tar fetched"; +} + +bool Config::isValid() const +{ + return (!m_selectedTarball.isEmpty()) ; +} + +void Config::setMirrorBase(const QString& mirror){ + QString base = mirror.trimmed(); + while(base.endsWith('/')) base.chop(1); + + if(base.isEmpty()) base = QStringLiteral("http://distfiles.gentoo.org/releases"); + + if(base == m_mirrorBase) return; + + m_mirrorBase = base; + if(m_fetcher) m_fetcher->setMirrorBase(m_mirrorBase); +} + +void Config::updateTarball(const QString &tarball){ + m_selectedTarball = tarball; + emit tarballReady(tarball); + emit validityChanged(isValid()); +} + +void Config::updateGlobalStorage() +{ + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + + if(m_selectedArch == "livecd") + gs->insert("GENTOO_LIVECD","yes"); + else{ + gs->insert("GENTOO_LIVECD","no"); + gs->insert( "BASE_DOWNLOAD_URL", QString("%1/%2/autobuilds/%3/").arg(m_mirrorBase,m_selectedArch,m_selectedVariant)); + gs->insert( "FINAL_DOWNLOAD_URL", QString("%1/%2/autobuilds/%3/%4").arg(m_mirrorBase,m_selectedArch,m_selectedVariant,m_selectedTarball)); + gs->insert( "STAGE_NAME_TAR", m_selectedTarball ); + } +} + + diff --git a/src/modules/stagechoose/Config.h b/src/modules/stagechoose/Config.h new file mode 100644 index 0000000000..6ba116ed7e --- /dev/null +++ b/src/modules/stagechoose/Config.h @@ -0,0 +1,65 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include "StageFetcher.h" +#include +#include +#include +#include + +struct ArchitectureInfo +{ + QString name; + QString description; + + ArchitectureInfo() = default; + ArchitectureInfo(const QString& n, const QString& d): + name(n),description(d){} +}; + +class Config : public QObject +{ + Q_OBJECT + +public: + explicit Config(QObject* parent = nullptr); + + QList availableArchitecturesInfo(); + QStringList availableArchitectures(); + void availableStagesFor(const QString& architecture); + void selectVariant(const QString& variantKey); + + QString selectedStage3() const; + bool isValid() const; + + void updateGlobalStorage(); + void updateTarball(const QString &tarball); + void setMirrorBase(const QString& mirror); + QString mirrorBase(); + +signals: + void variantsReady(const QStringList& variants); + void tarballReady(const QString& tarball); + void fetchStatusChanged(const QString& status); + void fetchError(const QString& error); + void validityChanged(bool validity); + +private: + StageFetcher* m_fetcher; + QString m_mirrorBase {QStringLiteral("http://distfiles.gentoo.org/releases")}; + QString m_selectedArch; + QString m_selectedVariant; + QString m_selectedTarball; +}; + +#endif // CONFIG_H + diff --git a/src/modules/stagechoose/SetStage3Job.cpp b/src/modules/stagechoose/SetStage3Job.cpp new file mode 100644 index 0000000000..086085862a --- /dev/null +++ b/src/modules/stagechoose/SetStage3Job.cpp @@ -0,0 +1,61 @@ +#include "SetStage3Job.h" + +#include "utils/Logger.h" +#include +#include +#include + +SetStage3Job::SetStage3Job(const QString& tarballName) + : m_tarballName(tarballName) +{ +} + +QString SetStage3Job::prettyName() const +{ + return QString("Write selected Gentoo Stage3 to config: %1").arg(m_tarballName); +} + +Calamares::JobResult SetStage3Job::exec() +{ + if(m_tarballName.isEmpty()){ + return Calamares::JobResult::error( + "No stage3 tarball selected.","Stage3 tarball name is empty." + ); + } + + QString configPath = "/etc/calamares.conf"; + QFile file(configPath); + QString contents; + + if (file.exists()) { + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return Calamares::JobResult::error( + "Failed to open Calamares config file for reading.", + configPath); + } + QTextStream in(&file); + contents = in.readAll(); + file.close(); + } + + QString stage3Line = QString("stage3 = %1").arg(m_tarballName); + + if (contents.contains(QRegularExpression(R"(stage3\s*=)"))) { + contents.replace(QRegularExpression(R"(stage3\s*=.*)"), stage3Line); + } else { + contents.append("\n" + stage3Line + "\n"); + } + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + return Calamares::JobResult::error( + "Failed to open Calamares config file for writing.", + configPath); + } + + QTextStream out(&file); + out << contents; + file.close(); + + cDebug() << "Wrote stage3 tarball to config:" << m_tarballName; + return Calamares::JobResult::ok(); +} diff --git a/src/modules/stagechoose/SetStage3Job.h b/src/modules/stagechoose/SetStage3Job.h new file mode 100644 index 0000000000..edea12ce2c --- /dev/null +++ b/src/modules/stagechoose/SetStage3Job.h @@ -0,0 +1,22 @@ +#ifndef SETSTAGE3JOB_H +#define SETSTAGE3JOB_H + +#include +#include + +/** + * @brief A job to write the selected Stage3 tarball name to /etc/calamares.conf + */ +class SetStage3Job : public Calamares::Job +{ +public: + explicit SetStage3Job(const QString& tarballName); + + QString prettyName() const override; + Calamares::JobResult exec() override; + +private: + QString m_tarballName; +}; + +#endif // SETSTAGE3JOB_H diff --git a/src/modules/stagechoose/StageChoosePage.cpp b/src/modules/stagechoose/StageChoosePage.cpp new file mode 100644 index 0000000000..5bf3eacb5e --- /dev/null +++ b/src/modules/stagechoose/StageChoosePage.cpp @@ -0,0 +1,145 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2015 Anke Boersma + * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ +#include "StageChoosePage.h" +#include "Config.h" +#include "ui_StageChoosePage.h" + +#include +#include +#include +#include + +StageChoosePage::StageChoosePage(Config* config, QWidget* parent) + : QWidget(parent) + , ui(new Ui::StageChoosePage) + , m_config(config) +{ + ui->setupUi(this); + + connect(ui->architectureComboBox, QOverload::of(&QComboBox::activated), + this, &StageChoosePage::onArchitectureChanged); + connect(ui->variantComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &StageChoosePage::onVariantChanged); + + connect(ui->mirrorLineEdit, &QLineEdit::editingFinished, this, &StageChoosePage::onMirrorChanged); + connect(ui->restartFetcherButton, &QPushButton::clicked, this, &StageChoosePage::onRestartFetcherClicked); + + if(m_config){ + connect(m_config, &Config::fetchStatusChanged,this,&StageChoosePage::setFetcherStatus); + connect(m_config, &Config::fetchError,this,[this](const QString& error){setFetcherStatus("Error" + error);showRestartFetcherButton(true);}); + connect(m_config, &Config::variantsReady, this, &StageChoosePage::whenVariantsReady); + connect(m_config, &Config::tarballReady, this, [this](const QString&){updateSelectedTarballLabel();}); + } + + setFetcherStatus("Idle"); + updateSelectedTarballLabel(); + showRestartFetcherButton(false); + + populateArchs(); +} + +void StageChoosePage::onMirrorChanged() +{ + if(!m_config) return; + QString mirror = ui->mirrorLineEdit->text().trimmed(); + m_config->setMirrorBase(mirror); +} + +void StageChoosePage::setFetcherStatus(const QString& status) +{ + ui->fetcherStatusLabel->setText("Status: " + status); +} + +void StageChoosePage::showRestartFetcherButton(bool visible) +{ + ui->restartFetcherButton->setVisible(false); + // To implement +} + +void StageChoosePage::onRestartFetcherClicked(){ + // Logic here + setFetcherStatus("Restarting..."); + showRestartFetcherButton(false); +} + +void StageChoosePage::populateArchs() +{ + if (!m_config) + return; + + const auto archs = m_config->availableArchitecturesInfo(); + ui->architectureComboBox->clear(); + for(const auto& arch : archs){ + ui->architectureComboBox->addItem(arch.description,arch.name); + } + ui->architectureComboBox->setCurrentIndex(-1); +} + +void StageChoosePage::onArchitectureChanged(int index) +{ + if (!m_config) + return; + + const QString archKey = ui->architectureComboBox->itemData(index).toString(); + ui->variantComboBox->clear(); + + m_config->availableStagesFor(archKey); + + if(archKey == "livecd"){ + ui->variantComboBox->setVisible(false); + ui->variantLabel->setVisible(false); + + // setFetcherStatus("LiveCD mode"); + // m_config->updateTarball("livecd"); + showRestartFetcherButton(false); + return; + } + else{ + ui->variantComboBox->setVisible(true); + ui->variantLabel->setVisible(true); + } +} + +void StageChoosePage::onVariantChanged(int index) +{ + if (!m_config) + return; + + const QString variantKey = ui->variantComboBox->itemData(index).toString(); + m_config->selectVariant(variantKey); +} + +void StageChoosePage::whenVariantsReady(const QStringList &stages) +{ + ui->variantComboBox->clear(); + + for(const QString& stage : stages){ + ui->variantComboBox->addItem(stage, stage); + } + + if(!stages.isEmpty()){ + ui->variantComboBox->setCurrentIndex(0); + onVariantChanged(0); + } +} + +void StageChoosePage::updateSelectedTarballLabel() +{ + if (!m_config) + return; + + ui->selectedTarballLabel->setText("Selected: " + m_config->selectedStage3()); +} + +StageChoosePage::~StageChoosePage() +{ + delete ui; +} diff --git a/src/modules/stagechoose/StageChoosePage.h b/src/modules/stagechoose/StageChoosePage.h new file mode 100644 index 0000000000..65e6633184 --- /dev/null +++ b/src/modules/stagechoose/StageChoosePage.h @@ -0,0 +1,51 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef STAGECHOOSEPAGE_H +#define STAGECHOOSEPAGE_H + +#include + +class QComboBox; +class QLabel; +class Config; + +namespace Ui { +class StageChoosePage; +} + +class StageChoosePage : public QWidget +{ + Q_OBJECT + +public: + explicit StageChoosePage( Config* config, QWidget* parent = nullptr); + ~StageChoosePage() override; + + void populateArchs(); + void setFetcherStatus(const QString& status); + void showRestartFetcherButton(bool visible); + void onRestartFetcherClicked(); + void whenVariantsReady(const QStringList &stages); + + void onMirrorChanged(); + +private slots: + void onArchitectureChanged(int index); + void onVariantChanged(int index); + void updateSelectedTarballLabel(); + +private: + Ui::StageChoosePage* ui; + Config* m_config; +}; + +#endif // STAGECHOOSEPAGE_H + diff --git a/src/modules/stagechoose/StageChoosePage.ui b/src/modules/stagechoose/StageChoosePage.ui new file mode 100644 index 0000000000..f78482abd0 --- /dev/null +++ b/src/modules/stagechoose/StageChoosePage.ui @@ -0,0 +1,172 @@ + + + StageChoosePage + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 40 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + + + + + + + If you leave mirror link blank, it will choose the default option. + + + true + + + + + + + + + + Mirror Link: + + + + + + + https://distfiles.gentoo.org/releases/ + + + + + + + + + + + + Select Architecture: + + + + + + + Select Stage3 Option: + + + + + + + + + + + + 20025 + + + 20025 + + + + + + + 20025 + + + 20025 + + + + + + + + + + Selected: + + + + + + + + Status: Idle + + + Qt::AlignCenter + + + + + + + + Restart Fetcher + + + false + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + + + + + diff --git a/src/modules/stagechoose/StageChooseViewStep.cpp b/src/modules/stagechoose/StageChooseViewStep.cpp new file mode 100644 index 0000000000..24857f07f0 --- /dev/null +++ b/src/modules/stagechoose/StageChooseViewStep.cpp @@ -0,0 +1,80 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "StageChooseViewStep.h" + +#include "Config.h" +#include "StageChoosePage.h" +#include "SetStage3Job.h" + +#include "utils/Logger.h" + +CALAMARES_PLUGIN_FACTORY_DEFINITION(StageChooseViewStepFactory, registerPlugin();) + +StageChooseViewStep::StageChooseViewStep(QObject* parent) + : Calamares::ViewStep(parent) + , m_config(new Config(this)) + , m_widget(new StageChoosePage(m_config)) +{ + connect(m_config,&Config::validityChanged,this,[this](bool valid){emit nextStatusChanged(valid);}); +} + +StageChooseViewStep::~StageChooseViewStep() +{ + if ( m_widget && m_widget->parent() == nullptr ) + { + m_widget->deleteLater(); + } +} + +QString StageChooseViewStep::prettyName() const +{ + return tr("Select Stage"); +} + +QWidget* StageChooseViewStep::widget() +{ + return m_widget; +} + +bool StageChooseViewStep::isNextEnabled() const +{ + return m_config->isValid(); +} + +bool StageChooseViewStep::isBackEnabled() const +{ + return true; +} + +bool StageChooseViewStep::isAtBeginning() const +{ + return true; +} + +bool StageChooseViewStep::isAtEnd() const +{ + return true; +} + +void StageChooseViewStep::onLeave() +{ + m_config->updateGlobalStorage(); +} + +Calamares::JobList StageChooseViewStep::jobs() const +{ + Calamares::JobList list; + if (m_config->isValid()) + { + list.append(QSharedPointer::create(m_config->selectedStage3())); + } + return list; +} diff --git a/src/modules/stagechoose/StageChooseViewStep.h b/src/modules/stagechoose/StageChooseViewStep.h new file mode 100644 index 0000000000..d6efed30f4 --- /dev/null +++ b/src/modules/stagechoose/StageChooseViewStep.h @@ -0,0 +1,53 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef STAGECHOOSEVIEWSTEP_H +#define STAGECHOOSEVIEWSTEP_H + +#include +#include +#include + +#include "DllMacro.h" +#include "utils/PluginFactory.h" +#include "viewpages/ViewStep.h" + +class StageChoosePage; +class Config; + +class PLUGINDLLEXPORT StageChooseViewStep : public Calamares::ViewStep +{ + Q_OBJECT + +public: + explicit StageChooseViewStep(QObject* parent = nullptr); + ~StageChooseViewStep() override; + + QString prettyName() const override; + + QWidget* widget() override; + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + bool isAtBeginning() const override; + bool isAtEnd() const override; + + Calamares::JobList jobs() const override; + + void onLeave() override; + +private: + Config* m_config; + StageChoosePage* m_widget; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( StageChooseViewStepFactory ) + +#endif // STAGECHOOSEVIEWSTEP_H + diff --git a/src/modules/stagechoose/StageFetcher.cpp b/src/modules/stagechoose/StageFetcher.cpp new file mode 100644 index 0000000000..287ab59023 --- /dev/null +++ b/src/modules/stagechoose/StageFetcher.cpp @@ -0,0 +1,152 @@ +#include "StageFetcher.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +StageFetcher :: StageFetcher(QObject* parent):QObject(parent) +{ +} + +QString StageFetcher::extractvariantBase(const QString& variant){ + if(variant.startsWith("current-")) + return variant.mid(8); + return variant; +} + +void StageFetcher::setMirrorBase(const QString& mirror) +{ + QString base = mirror.trimmed(); + while(base.endsWith('/')) base.chop(1); + + if(base.isEmpty()) + base = QStringLiteral("http://distfiles.gentoo.org/releases"); + + if(!base.endsWith("/releases")) + base += "/releases"; + + m_mirrorBase = base; +} + +void StageFetcher::cancelOngoingRequest() +{ + if(m_currentReply){ + disconnect(m_currentReply,nullptr,this,nullptr); + if(m_currentReply->isRunning()) + m_currentReply->abort(); + m_currentReply->deleteLater(); + m_currentReply = nullptr; + } +} + +void StageFetcher::fetchVariants(const QString& arch) +{ + cancelOngoingRequest(); + emit fetchStatusChanged("Fetching variants for " + arch + "..."); + + QString urlStr = QString("%1/%2/autobuilds/").arg(m_mirrorBase, arch); + QUrl url(urlStr); + QNetworkRequest request(url); + + QNetworkReply* reply = m_nam.get(request); + m_currentReply = reply; + connect(reply, &QNetworkReply::finished, this,[this, reply](){onVariantsReplyFinished(reply);}); +} + +void StageFetcher::onVariantsReplyFinished(QNetworkReply* reply) +{ + if(!reply) + return; + + if(reply != m_currentReply){ + reply->deleteLater(); + return; + } + + QStringList variants; + if(reply->error() != QNetworkReply::NoError){ + emit fetchError(reply->errorString()); + reply->deleteLater(); + if(m_currentReply == reply) m_currentReply = nullptr; + return; + } + + QString html = reply->readAll(); + if(html.isEmpty()) + emit variantsFetched(variants); + + QRegularExpression re(R"((current-stage3-[^"/]+)[/])"); + QRegularExpressionMatchIterator iterator = re.globalMatch(html); + + QStringList seen; + while(iterator.hasNext()){ + QRegularExpressionMatch match = iterator.next(); + QString variant = match.captured(1); + if(!seen.contains(variant)){ + variants.append(variant); + seen.append(variant); + } + } + + emit variantsFetched(variants); + emit fetchStatusChanged("Idle"); + reply->deleteLater(); + if(reply == m_currentReply) m_currentReply = nullptr; +} + +void StageFetcher::fetchLatestTarball(const QString& arch, const QString& variant) +{ + cancelOngoingRequest(); + emit fetchStatusChanged("Fetching Tarball for "+ variant +"..."); + const QString baseUrl = QString("%1/%2/autobuilds/%3/").arg(m_mirrorBase, arch, variant); + QUrl url(baseUrl); + QNetworkRequest request(url); + + QNetworkReply* reply = m_nam.get(request); + m_currentReply = reply; + connect(reply, &QNetworkReply::finished, this, [this, reply, variant](){onTarballReplyFinished(reply, variant);}); +} + +void StageFetcher::onTarballReplyFinished(QNetworkReply* reply, const QString& variant) +{ + if(!reply) + return; + + if(reply != m_currentReply){ + reply->deleteLater(); + return; + } + + QString latest; + if(reply->error() != QNetworkReply::NoError){ + emit fetchError(reply->errorString()); + reply->deleteLater(); + if(m_currentReply == reply) m_currentReply = nullptr; + return; + } + + QString html = reply->readAll(); + if(html.isEmpty()) + emit tarballFetched(latest); + + QRegularExpression re(QString("(%1-[\\dTZ]+\\.tar\\.xz)").arg(StageFetcher::extractvariantBase(variant))); + QRegularExpressionMatchIterator iterator = re.globalMatch(html); + + while(iterator.hasNext()){ + QRegularExpressionMatch match = iterator.next(); + QString filename = match.captured(1); + if(filename > latest){ + latest = filename; + } + } + + emit tarballFetched(latest); + emit fetchStatusChanged("Idle"); + reply->deleteLater(); + if(reply == m_currentReply) m_currentReply = nullptr; +} \ No newline at end of file diff --git a/src/modules/stagechoose/StageFetcher.h b/src/modules/stagechoose/StageFetcher.h new file mode 100644 index 0000000000..8c97f29b55 --- /dev/null +++ b/src/modules/stagechoose/StageFetcher.h @@ -0,0 +1,42 @@ +#ifndef STAGEFETCHER_H +#define STAGEFETCHER_H + +#include +#include +#include +#include +#include +#include +#include + +class StageFetcher : public QObject +{ + Q_OBJECT + +public: + explicit StageFetcher(QObject* parent =nullptr); + + void fetchVariants(const QString& arch); + QString extractvariantBase(const QString& varaint); + void fetchLatestTarball(const QString& arch, const QString& variant); + + void setMirrorBase(const QString& mirror); + void cancelOngoingRequest(); + +signals: + void fetchStatusChanged(const QString& status); + void fetchError(const QString& error); + void variantsFetched(const QStringList& variants); + void tarballFetched(const QString& tarballs); + +private slots: + void onVariantsReplyFinished(QNetworkReply* reply); + void onTarballReplyFinished(QNetworkReply* reply, const QString& variant); + +private: + QString m_mirrorBase {QStringLiteral("http://distfiles.gentoo.org/releases")}; + QNetworkAccessManager m_nam; + QPointer m_currentReply; +}; + +#endif //STAGEFETCHER_H \ No newline at end of file diff --git a/src/modules/stagechoose/stagechoose.conf b/src/modules/stagechoose/stagechoose.conf new file mode 100644 index 0000000000..59602206da --- /dev/null +++ b/src/modules/stagechoose/stagechoose.conf @@ -0,0 +1,7 @@ +--- +type: viewmodule +interface: qtplugin +module: stagechoose + +viewmodule: + weight: 30 diff --git a/src/modules/stagechoose/stagechoose.schema.yaml b/src/modules/stagechoose/stagechoose.schema.yaml new file mode 100644 index 0000000000..d1e3d9aa05 --- /dev/null +++ b/src/modules/stagechoose/stagechoose.schema.yaml @@ -0,0 +1,17 @@ +--- +type: map +mapping: + type: + type: str + required: true + interface: + type: str + required: true + module: + type: str + required: true + viewmodule: + type: map + mapping: + weight: + type: int