Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit a9a01858 authored by Jan Grulich's avatar Jan Grulich

Automatically install/remove/update related refs

Summary:
Process related refs which should be automatically installed/remove/updated together with the
app or runtime which is being processed. This is mostly localization, but in case of runtime it
can automatically download styles, icons and so on.

Reviewers: apol

Reviewed By: apol

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D6677
parent 07a6fdf3
......@@ -966,7 +966,7 @@ Transaction* FlatpakBackend::installApplication(AbstractResource *app, const Add
FlatpakInstallation *installation = resource->installation();
if (resource->propertyState(FlatpakResource::RequiredRuntime) == FlatpakResource::NotKnownYet && resource->type() == FlatpakResource::DesktopApp) {
transaction = new FlatpakTransaction(installation, resource, Transaction::InstallRole, true);
transaction = new FlatpakTransaction(resource, Transaction::InstallRole, true);
connect(resource, &FlatpakResource::propertyStateChanged, [resource, transaction, this] (FlatpakResource::PropertyKind kind, FlatpakResource::PropertyState state) {
if (kind != FlatpakResource::RequiredRuntime) {
return;
......@@ -983,9 +983,9 @@ Transaction* FlatpakBackend::installApplication(AbstractResource *app, const Add
} else {
FlatpakResource *runtime = getRuntimeForApp(resource);
if (runtime && !runtime->isInstalled()) {
transaction = new FlatpakTransaction(installation, resource, runtime, Transaction::InstallRole);
transaction = new FlatpakTransaction(resource, runtime, Transaction::InstallRole);
} else {
transaction = new FlatpakTransaction(installation, resource, Transaction::InstallRole);
transaction = new FlatpakTransaction(resource, Transaction::InstallRole);
}
}
......@@ -1015,7 +1015,7 @@ Transaction* FlatpakBackend::removeApplication(AbstractResource *app)
}
FlatpakInstallation *installation = resource->installation();
FlatpakTransaction *transaction = new FlatpakTransaction(installation, resource, Transaction::RemoveRole);
FlatpakTransaction *transaction = new FlatpakTransaction(resource, Transaction::RemoveRole);
connect(transaction, &FlatpakTransaction::statusChanged, [this, installation, resource] (Transaction::Status status) {
if (status == Transaction::Status::DoneStatus) {
......
......@@ -27,18 +27,21 @@
#include <QDebug>
#include <QTimer>
FlatpakTransaction::FlatpakTransaction(FlatpakInstallation *installation, FlatpakResource *app, Role role, bool delayStart)
: FlatpakTransaction(installation, app, nullptr, role, delayStart)
extern "C" {
#include <flatpak.h>
#include <gio/gio.h>
#include <glib.h>
}
FlatpakTransaction::FlatpakTransaction(FlatpakResource *app, Role role, bool delayStart)
: FlatpakTransaction(app, nullptr, role, delayStart)
{
}
FlatpakTransaction::FlatpakTransaction(FlatpakInstallation* installation, FlatpakResource *app, FlatpakResource *runtime, Transaction::Role role, bool delayStart)
FlatpakTransaction::FlatpakTransaction(FlatpakResource *app, FlatpakResource *runtime, Transaction::Role role, bool delayStart)
: Transaction(app->backend(), app, role, {})
, m_appJobProgress(0)
, m_runtimeJobProgress(0)
, m_app(app)
, m_runtime(runtime)
, m_installation(installation)
{
setCancellable(true);
......@@ -54,9 +57,8 @@ FlatpakTransaction::~FlatpakTransaction()
void FlatpakTransaction::cancel()
{
Q_ASSERT(m_appJob);
m_appJob->cancel();
if (m_runtime) {
m_runtimeJob->cancel();
foreach (const QPointer<FlatpakTransactionJob> &job, m_jobs) {
job->cancel();
}
setStatus(CancelledStatus);
}
......@@ -69,75 +71,115 @@ void FlatpakTransaction::setRuntime(FlatpakResource *runtime)
void FlatpakTransaction::start()
{
if (m_runtime) {
m_runtimeJob = new FlatpakTransactionJob(m_installation, m_runtime, role(), this);
connect(m_runtimeJob, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onRuntimeJobFinished);
connect(m_runtimeJob, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onRuntimeJobProgressChanged);
m_runtimeJob->start();
QPointer<FlatpakTransactionJob> job = new FlatpakTransactionJob(m_runtime, QPair<QString, uint>(), role(), this);
connect(job, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onJobFinished);
connect(job, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onJobProgressChanged);
m_jobs << job;
processRelatedRefs(m_runtime);
}
// App job will be started everytime
m_appJob = new FlatpakTransactionJob(m_installation, m_app, role(), this);
connect(m_appJob, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onAppJobFinished);
connect(m_appJob, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onAppJobProgressChanged);
m_appJob->start();
}
// App job will be added everytime
m_appJob = new FlatpakTransactionJob(m_app, QPair<QString, uint>(), role(), this);
connect(m_appJob, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onJobFinished);
connect(m_appJob, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onJobProgressChanged);
m_jobs << m_appJob;
void FlatpakTransaction::onAppJobFinished()
{
m_appJobProgress = 100;
processRelatedRefs(m_app);
updateProgress();
if (!m_appJob->result()) {
Q_EMIT passiveMessage(m_appJob->errorMessage());
}
if ((m_runtimeJob && m_runtimeJob->isFinished()) || !m_runtimeJob) {
finishTransaction();
// Now start all the jobs together
foreach (const QPointer<FlatpakTransactionJob> &job, m_jobs) {
job->start();
}
}
void FlatpakTransaction::onAppJobProgressChanged(int progress)
void FlatpakTransaction::processRelatedRefs(FlatpakResource* resource)
{
m_appJobProgress = progress;
g_autoptr(GPtrArray) refs = nullptr;
g_autoptr(GError) error = nullptr;
g_autoptr(GCancellable) cancellable = g_cancellable_new();;
QList<FlatpakResource> additionalResources;
g_autofree gchar *ref = nullptr;
ref = g_strdup_printf ("%s/%s/%s/%s",
resource->typeAsString().toUtf8().constData(),
resource->flatpakName().toUtf8().constData(),
resource->arch().toUtf8().constData(),
resource->branch().toUtf8().constData());
if (role() == Transaction::Role::InstallRole) {
if (resource->state() == AbstractResource::Upgradeable) {
refs = flatpak_installation_list_installed_related_refs_sync(resource->installation(), resource->origin().toUtf8().constData(), ref, cancellable, &error);
if (error) {
qWarning() << "Failed to list installed related refs for update: " << error->message;
}
} else {
refs = flatpak_installation_list_remote_related_refs_sync(resource->installation(), resource->origin().toUtf8().constData(), ref, cancellable, &error);
if (error) {
qWarning() << "Failed to list related refs for installation: " << error->message;
}
}
} else if (role() == Transaction::Role::RemoveRole) {
refs = flatpak_installation_list_installed_related_refs_sync(resource->installation(), resource->origin().toUtf8().constData(), ref, cancellable, &error);
if (error) {
qWarning() << "Failed to list installed related refs for removal: " << error->message;
}
}
updateProgress();
if (refs) {
for (uint i = 0; i < refs->len; i++) {
FlatpakRef *flatpakRef = FLATPAK_REF(g_ptr_array_index(refs, i));
if (flatpak_related_ref_should_download(FLATPAK_RELATED_REF(flatpakRef))) {
QPointer<FlatpakTransactionJob> job = new FlatpakTransactionJob(resource, QPair<QString, uint>(QString::fromUtf8(flatpak_ref_get_name(flatpakRef)), flatpak_ref_get_kind(flatpakRef)), role(), this);
connect(job, &FlatpakTransactionJob::finished, this, &FlatpakTransaction::onJobFinished);
connect(job, &FlatpakTransactionJob::progressChanged, this, &FlatpakTransaction::onJobProgressChanged);
// Add to the list of all jobs
m_jobs << job;
}
}
}
}
void FlatpakTransaction::onRuntimeJobFinished()
void FlatpakTransaction::onJobFinished()
{
m_runtimeJobProgress = 100;
FlatpakTransactionJob *job = static_cast<FlatpakTransactionJob*>(sender());
updateProgress();
if (job != m_appJob) {
if (!job->result()) {
Q_EMIT passiveMessage(job->errorMessage());
}
if (!m_runtimeJob->result()) {
Q_EMIT passiveMessage(m_runtimeJob->errorMessage());
} else {
// This should be the only case when runtime is automatically installed, but better to check
if (role() == InstallRole) {
m_runtime->setState(AbstractResource::Installed);
// Mark runtime as installed
if (m_runtime && job->app()->flatpakName() == m_runtime->flatpakName() && !job->isRelated() && role() != Transaction::Role::RemoveRole) {
if (job->result()) {
m_runtime->setState(AbstractResource::Installed);
}
}
}
if (m_appJob->isFinished()) {
finishTransaction();
foreach (const QPointer<FlatpakTransactionJob> &job, m_jobs) {
if (job->isRunning()) {
return;
}
}
// No other job is running → finish transaction
finishTransaction();
}
void FlatpakTransaction::onRuntimeJobProgressChanged(int progress)
void FlatpakTransaction::onJobProgressChanged(int progress)
{
m_runtimeJobProgress = progress;
Q_UNUSED(progress);
updateProgress();
}
int total = 0;
void FlatpakTransaction::updateProgress()
{
if (m_runtime) {
setProgress((m_appJobProgress + m_runtimeJobProgress) / 2);
} else {
setProgress(m_appJobProgress);
// Count progress from all the jobs
foreach (const QPointer<FlatpakTransactionJob> &job, m_jobs) {
total += job->progress();
}
setProgress(total / m_jobs.count());
}
void FlatpakTransaction::finishTransaction()
......
......@@ -37,9 +37,8 @@ class FlatpakTransaction : public Transaction
{
Q_OBJECT
public:
FlatpakTransaction(FlatpakInstallation *installation, FlatpakResource *app, Role role, bool delayStart = false);
FlatpakTransaction(FlatpakInstallation *installation, FlatpakResource *app, FlatpakResource *runtime, Role role, bool delayStart = false);
// FIXME ignore addons, they are not used in flatpak world (yet)
FlatpakTransaction(FlatpakResource *app, Role role, bool delayStart = false);
FlatpakTransaction(FlatpakResource *app, FlatpakResource *runtime, Role role, bool delayStart = false);
~FlatpakTransaction();
......@@ -47,23 +46,19 @@ public:
void setRuntime(FlatpakResource *runtime);
public Q_SLOTS:
void onAppJobFinished();
void onAppJobProgressChanged(int progress);
void onRuntimeJobFinished();
void onRuntimeJobProgressChanged(int progress);
void onJobFinished();
void onJobProgressChanged(int progress);
void finishTransaction();
void start();
private:
void processRelatedRefs(FlatpakResource *resource);
void updateProgress();
int m_appJobProgress;
int m_runtimeJobProgress;
QPointer<FlatpakResource> m_app;
QPointer<FlatpakResource> m_runtime;
FlatpakInstallation *m_installation;
QPointer<FlatpakTransactionJob> m_appJob;
QPointer<FlatpakTransactionJob> m_runtimeJob;
QList<QPointer<FlatpakTransactionJob> > m_jobs;
};
#endif // FLATPAKTRANSACTION_H
......@@ -33,14 +33,18 @@ static void flatpakInstallationProgressCallback(const gchar *stats, guint progre
return;
}
transactionJob->setProgress(progress);
Q_EMIT transactionJob->progressChanged(progress);
}
FlatpakTransactionJob::FlatpakTransactionJob(FlatpakInstallation *installation, FlatpakResource *app, Transaction::Role role, QObject *parent)
FlatpakTransactionJob::FlatpakTransactionJob(FlatpakResource *app, const QPair<QString, uint> &relatedRef, Transaction::Role role, QObject *parent)
: QThread(parent)
, m_result(false)
, m_progress(0)
, m_relatedRef(relatedRef.first)
, m_relatedRefKind(relatedRef.second)
, m_app(app)
, m_installation(installation)
, m_role(role)
{
m_cancellable = g_cancellable_new();
......@@ -61,12 +65,15 @@ void FlatpakTransactionJob::run()
g_autoptr(GError) localError = nullptr;
g_autoptr(FlatpakInstalledRef) ref = nullptr;
const QString refName = m_relatedRef.isEmpty() ? m_app->flatpakName() : m_relatedRef;
const uint kind = m_relatedRef.isEmpty() ? (uint)m_app->type() : m_relatedRefKind;
if (m_role == Transaction::Role::InstallRole) {
if (m_app->state() == AbstractResource::Upgradeable) {
ref = flatpak_installation_update(m_installation,
ref = flatpak_installation_update(m_app->installation(),
FLATPAK_UPDATE_FLAGS_NONE,
m_app->type() == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME,
m_app->flatpakName().toUtf8().constData(),
kind == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME,
refName.toUtf8().constData(),
m_app->arch().toUtf8().constData(),
m_app->branch().toUtf8().constData(),
flatpakInstallationProgressCallback,
......@@ -76,32 +83,34 @@ void FlatpakTransactionJob::run()
if (m_app->flatpakFileType() == QStringLiteral("flatpak")) {
g_autoptr(GFile) file = g_file_new_for_path(m_app->resourceFile().toLocalFile().toUtf8().constData());
if (!file) {
qWarning() << "Failed to install bundled application" << m_app->name();
qWarning() << "Failed to install bundled application" << refName;
}
ref = flatpak_installation_install_bundle(m_installation, file, flatpakInstallationProgressCallback, this, m_cancellable, &localError);
ref = flatpak_installation_install_bundle(m_app->installation(), file, flatpakInstallationProgressCallback, this, m_cancellable, &localError);
} else {
ref = flatpak_installation_install(m_installation,
m_app->origin().toUtf8().constData(),
m_app->type() == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME,
m_app->flatpakName().toUtf8().constData(),
m_app->arch().toUtf8().constData(),
m_app->branch().toUtf8().constData(),
flatpakInstallationProgressCallback,
this,
m_cancellable, &localError);
ref = flatpak_installation_install(m_app->installation(),
m_app->origin().toUtf8().constData(),
kind == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME,
refName.toUtf8().constData(),
m_app->arch().toUtf8().constData(),
m_app->branch().toUtf8().constData(),
flatpakInstallationProgressCallback,
this,
m_cancellable, &localError);
}
}
if (!ref) {
m_result = false;
m_errorMessage = QString::fromUtf8(localError->message);
qWarning() << "Failed to install" << m_app->name() << ':' << m_errorMessage;
// We are done so we can set the progress to 100
m_progress = 100;
qWarning() << "Failed to install" << refName << ':' << m_errorMessage;
return;
}
} else if (m_role == Transaction::Role::RemoveRole) {
if (!flatpak_installation_uninstall(m_installation,
m_app->type() == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME,
m_app->flatpakName().toUtf8().constData(),
if (!flatpak_installation_uninstall(m_app->installation(),
kind == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME,
refName.toUtf8().constData(),
m_app->arch().toUtf8().constData(),
m_app->branch().toUtf8().constData(),
flatpakInstallationProgressCallback,
......@@ -109,12 +118,38 @@ void FlatpakTransactionJob::run()
m_cancellable, &localError)) {
m_result = false;
m_errorMessage = QString::fromUtf8(localError->message);
qWarning() << "Failed to uninstall" << m_app->name() << ':' << m_errorMessage;
// We are done so we can set the progress to 100
m_progress = 100;
qWarning() << "Failed to uninstall" << refName << ':' << m_errorMessage;
return;
}
}
// We are done so we can set the progress to 100
m_progress = 100;
m_result = true;
Q_EMIT progressChanged(m_progress);
}
FlatpakResource * FlatpakTransactionJob::app() const
{
return m_app;
}
bool FlatpakTransactionJob::isRelated() const
{
return !m_relatedRef.isEmpty();
}
int FlatpakTransactionJob::progress() const
{
return m_progress;
}
void FlatpakTransactionJob::setProgress(int progress)
{
m_progress = progress;
}
QString FlatpakTransactionJob::errorMessage() const
......
......@@ -35,12 +35,19 @@ class FlatpakTransactionJob : public QThread
{
Q_OBJECT
public:
FlatpakTransactionJob(FlatpakInstallation *installation, FlatpakResource *app, Transaction::Role role, QObject *parent = nullptr);
FlatpakTransactionJob(FlatpakResource *app, const QPair<QString, uint> &relatedRef, Transaction::Role role, QObject *parent = nullptr);
~FlatpakTransactionJob();
void cancel();
void run() override;
FlatpakResource * app() const;
bool isRelated() const;
int progress() const;
void setProgress(int progress);
QString errorMessage() const;
bool result() const;
......@@ -49,10 +56,12 @@ Q_SIGNALS:
private:
bool m_result;
int m_progress;
QString m_errorMessage;
QString m_relatedRef;
uint m_relatedRefKind;
GCancellable *m_cancellable;
FlatpakResource *m_app;
FlatpakInstallation *m_installation;
Transaction::Role m_role;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment