forked from Mirrors/flatpak-builder
804 lines
25 KiB
C
804 lines
25 KiB
C
/*
|
|
* Copyright © 2016 Red Hat, Inc
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors:
|
|
* Alexander Larsson <alexl@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#include "flatpak-transaction.h"
|
|
#include "flatpak-utils.h"
|
|
#include "flatpak-builtins-utils.h"
|
|
#include "flatpak-error.h"
|
|
|
|
typedef struct FlatpakTransactionOp FlatpakTransactionOp;
|
|
|
|
typedef enum {
|
|
FLATPAK_TRANSACTION_OP_KIND_INSTALL,
|
|
FLATPAK_TRANSACTION_OP_KIND_UPDATE,
|
|
FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE,
|
|
FLATPAK_TRANSACTION_OP_KIND_BUNDLE
|
|
} FlatpakTransactionOpKind;
|
|
|
|
struct FlatpakTransactionOp {
|
|
char *remote;
|
|
char *ref;
|
|
char **subpaths;
|
|
char *commit;
|
|
GFile *bundle;
|
|
FlatpakTransactionOpKind kind;
|
|
gboolean non_fatal;
|
|
};
|
|
|
|
struct FlatpakTransaction {
|
|
FlatpakDir *dir;
|
|
GHashTable *refs;
|
|
GPtrArray *system_dirs;
|
|
GList *ops;
|
|
|
|
gboolean no_interaction;
|
|
gboolean no_pull;
|
|
gboolean no_deploy;
|
|
gboolean no_static_deltas;
|
|
gboolean add_deps;
|
|
gboolean add_related;
|
|
};
|
|
|
|
|
|
/* Check if the ref is in the dir, or in the system dir, in case its a
|
|
* user-dir or another system-wide installation. We want to avoid depending
|
|
* on user-installed things when installing to the system dir.
|
|
*/
|
|
static gboolean
|
|
ref_is_installed (FlatpakTransaction *self,
|
|
const char *ref,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GFile) deploy_dir = NULL;
|
|
FlatpakDir *dir = self->dir;
|
|
int i;
|
|
|
|
deploy_dir = flatpak_dir_get_if_deployed (dir, ref, NULL, NULL);
|
|
if (deploy_dir != NULL)
|
|
return TRUE;
|
|
|
|
/* Don't try to fallback for the system's default directory. */
|
|
if (!flatpak_dir_is_user (dir) && flatpak_dir_get_id (dir) == NULL)
|
|
return FALSE;
|
|
|
|
/* Lazy initialization of this, once per transaction */
|
|
if (self->system_dirs == NULL)
|
|
{
|
|
self->system_dirs = flatpak_dir_get_system_list (NULL, error);
|
|
if (self->system_dirs == NULL)
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < self->system_dirs->len; i++)
|
|
{
|
|
FlatpakDir *system_dir = g_ptr_array_index (self->system_dirs, i);
|
|
|
|
if (g_strcmp0 (flatpak_dir_get_id (dir), flatpak_dir_get_id (system_dir)) == 0)
|
|
continue;
|
|
|
|
deploy_dir = flatpak_dir_get_if_deployed (system_dir, ref, NULL, NULL);
|
|
if (deploy_dir != NULL)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
dir_ref_is_installed (FlatpakDir *dir, const char *ref, char **remote_out, GVariant **deploy_data_out)
|
|
{
|
|
g_autoptr(GVariant) deploy_data = NULL;
|
|
|
|
deploy_data = flatpak_dir_get_deploy_data (dir, ref, NULL, NULL);
|
|
if (deploy_data == NULL)
|
|
return FALSE;
|
|
|
|
if (remote_out)
|
|
*remote_out = g_strdup (flatpak_deploy_data_get_origin (deploy_data));
|
|
|
|
if (deploy_data_out)
|
|
*deploy_data_out = g_variant_ref (deploy_data);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static FlatpakTransactionOp *
|
|
flatpak_transaction_operation_new (const char *remote,
|
|
const char *ref,
|
|
const char **subpaths,
|
|
const char *commit,
|
|
GFile *bundle,
|
|
FlatpakTransactionOpKind kind)
|
|
{
|
|
FlatpakTransactionOp *self = g_new0 (FlatpakTransactionOp, 1);
|
|
|
|
self->remote = g_strdup (remote);
|
|
self->ref = g_strdup (ref);
|
|
self->subpaths = g_strdupv ((char **)subpaths);
|
|
self->commit = g_strdup (commit);
|
|
if (bundle)
|
|
self->bundle = g_object_ref (bundle);
|
|
self->kind = kind;
|
|
|
|
return self;
|
|
}
|
|
|
|
static void
|
|
flatpak_transaction_operation_free (FlatpakTransactionOp *self)
|
|
{
|
|
g_free (self->remote);
|
|
g_free (self->ref);
|
|
g_free (self->commit);
|
|
g_strfreev (self->subpaths);
|
|
g_clear_object (&self->bundle);
|
|
g_free (self);
|
|
}
|
|
|
|
FlatpakTransaction *
|
|
flatpak_transaction_new (FlatpakDir *dir,
|
|
gboolean no_interaction,
|
|
gboolean no_pull,
|
|
gboolean no_deploy,
|
|
gboolean no_static_deltas,
|
|
gboolean add_deps,
|
|
gboolean add_related)
|
|
{
|
|
FlatpakTransaction *t = g_new0 (FlatpakTransaction, 1);
|
|
|
|
t->dir = g_object_ref (dir);
|
|
t->refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
|
|
t->no_interaction = no_interaction;
|
|
t->no_pull = no_pull;
|
|
t->no_deploy = no_deploy;
|
|
t->no_static_deltas = no_static_deltas;
|
|
t->add_deps = add_deps;
|
|
t->add_related = add_related;
|
|
return t;
|
|
}
|
|
|
|
void
|
|
flatpak_transaction_free (FlatpakTransaction *self)
|
|
{
|
|
g_hash_table_unref (self->refs);
|
|
g_list_free_full (self->ops, (GDestroyNotify)flatpak_transaction_operation_free);
|
|
g_object_unref (self->dir);
|
|
|
|
if (self->system_dirs != NULL)
|
|
g_ptr_array_free (self->system_dirs, TRUE);
|
|
|
|
g_free (self);
|
|
}
|
|
|
|
gboolean
|
|
flatpak_transaction_contains_ref (FlatpakTransaction *self,
|
|
const char *ref)
|
|
{
|
|
FlatpakTransactionOp *op;
|
|
|
|
op = g_hash_table_lookup (self->refs, ref);
|
|
|
|
return op != NULL;
|
|
}
|
|
|
|
|
|
static char *
|
|
subpaths_to_string (const char **subpaths)
|
|
{
|
|
GString *s = NULL;
|
|
int i;
|
|
|
|
if (subpaths == NULL)
|
|
return g_strdup ("[$old]");
|
|
|
|
if (*subpaths == 0)
|
|
return g_strdup ("[*]");
|
|
|
|
s = g_string_new ("[");
|
|
for (i = 0; subpaths[i] != NULL; i++)
|
|
{
|
|
if (i != 0)
|
|
g_string_append (s, ", ");
|
|
g_string_append (s, subpaths[i]);
|
|
}
|
|
g_string_append (s, "]");
|
|
|
|
return g_string_free (s, FALSE);
|
|
}
|
|
|
|
static const char *
|
|
kind_to_str (FlatpakTransactionOpKind kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case FLATPAK_TRANSACTION_OP_KIND_INSTALL:
|
|
return "install";
|
|
case FLATPAK_TRANSACTION_OP_KIND_UPDATE:
|
|
return "update";
|
|
case FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE:
|
|
return "install/update";
|
|
case FLATPAK_TRANSACTION_OP_KIND_BUNDLE:
|
|
return "install bundle";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
FlatpakTransactionOp *
|
|
flatpak_transaction_add_op (FlatpakTransaction *self,
|
|
const char *remote,
|
|
const char *ref,
|
|
const char **subpaths,
|
|
const char *commit,
|
|
GFile *bundle,
|
|
FlatpakTransactionOpKind kind)
|
|
{
|
|
FlatpakTransactionOp *op;
|
|
g_autofree char *subpaths_str = NULL;
|
|
|
|
subpaths_str = subpaths_to_string (subpaths);
|
|
g_debug ("Transaction: %s %s:%s%s%s%s",
|
|
kind_to_str (kind), remote, ref,
|
|
commit != NULL ? "@" : "",
|
|
commit != NULL ? commit : "",
|
|
subpaths_str);
|
|
|
|
op = g_hash_table_lookup (self->refs, ref);
|
|
if (op != NULL)
|
|
{
|
|
/* Only override subpaths if already specified,
|
|
we always want the un-subpathed to win if specified. */
|
|
if (op->subpaths != NULL && op->subpaths[0] != NULL && subpaths != NULL)
|
|
{
|
|
g_strfreev (op->subpaths);
|
|
op->subpaths = g_strdupv ((char **)subpaths);
|
|
}
|
|
|
|
return op;
|
|
}
|
|
|
|
op = flatpak_transaction_operation_new (remote, ref, subpaths, commit, bundle, kind);
|
|
g_hash_table_insert (self->refs, g_strdup (ref), op);
|
|
self->ops = g_list_prepend (self->ops, op);
|
|
|
|
return op;
|
|
}
|
|
|
|
static char *
|
|
ask_for_remote (FlatpakTransaction *self, const char **remotes)
|
|
{
|
|
int n_remotes = g_strv_length ((char **)remotes);
|
|
int chosen = 0;
|
|
int i;
|
|
|
|
if (self->no_interaction)
|
|
{
|
|
chosen = 1;
|
|
g_print ("Found in remote %s\n", remotes[0]);
|
|
}
|
|
else if (n_remotes == 1)
|
|
{
|
|
if (flatpak_yes_no_prompt (_("Found in remote %s, do you want to install it?"), remotes[0]))
|
|
chosen = 1;
|
|
}
|
|
else
|
|
{
|
|
g_print (_("Found in several remotes:\n"));
|
|
for (i = 0; remotes[i] != NULL; i++)
|
|
{
|
|
g_print ("%d) %s\n", i + 1, remotes[i]);
|
|
}
|
|
chosen = flatpak_number_prompt (0, n_remotes, _("Which do you want to install (0 to abort)?"));
|
|
}
|
|
|
|
if (chosen == 0)
|
|
return NULL;
|
|
|
|
return g_strdup (remotes[chosen-1]);
|
|
}
|
|
|
|
static gboolean
|
|
add_related (FlatpakTransaction *self,
|
|
const char *remote,
|
|
const char *ref,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GPtrArray) related = NULL;
|
|
g_autoptr(GError) local_error = NULL;
|
|
int i;
|
|
|
|
if (!self->add_related)
|
|
return TRUE;
|
|
|
|
if (self->no_pull)
|
|
related = flatpak_dir_find_local_related (self->dir, ref, remote, NULL, &local_error);
|
|
else
|
|
related = flatpak_dir_find_remote_related (self->dir, ref, remote, NULL, &local_error);
|
|
if (related == NULL)
|
|
{
|
|
g_printerr (_("Warning: Problem looking for related refs: %s\n"), local_error->message);
|
|
g_clear_error (&local_error);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < related->len; i++)
|
|
{
|
|
FlatpakRelated *rel = g_ptr_array_index (related, i);
|
|
FlatpakTransactionOp *op;
|
|
|
|
if (!rel->download)
|
|
continue;
|
|
|
|
op = flatpak_transaction_add_op (self, remote, rel->ref,
|
|
(const char **)rel->subpaths,
|
|
NULL, NULL,
|
|
FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE);
|
|
op->non_fatal = TRUE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
add_deps (FlatpakTransaction *self,
|
|
GKeyFile *metakey,
|
|
const char *remote,
|
|
const char *ref,
|
|
GError **error)
|
|
{
|
|
g_autofree char *runtime_ref = NULL;
|
|
g_autofree char *full_runtime_ref = NULL;
|
|
g_autofree char *runtime_remote = NULL;
|
|
const char *pref;
|
|
|
|
if (!g_str_has_prefix (ref, "app/"))
|
|
return TRUE;
|
|
|
|
if (metakey)
|
|
runtime_ref = g_key_file_get_string (metakey, "Application", "runtime", NULL);
|
|
if (runtime_ref == NULL)
|
|
return TRUE;
|
|
|
|
pref = strchr (ref, '/') + 1;
|
|
|
|
full_runtime_ref = g_strconcat ("runtime/", runtime_ref, NULL);
|
|
|
|
if (!flatpak_transaction_contains_ref (self, full_runtime_ref))
|
|
{
|
|
g_autoptr(GError) local_error = NULL;
|
|
|
|
if (!ref_is_installed (self, full_runtime_ref, &local_error))
|
|
{
|
|
g_auto(GStrv) remotes = NULL;
|
|
|
|
if (local_error != NULL)
|
|
{
|
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
|
return FALSE;
|
|
}
|
|
|
|
g_print (_("Required runtime for %s (%s) is not installed, searching...\n"),
|
|
pref, runtime_ref);
|
|
|
|
remotes = flatpak_dir_search_for_dependency (self->dir, full_runtime_ref, NULL, NULL);
|
|
if (remotes == NULL || *remotes == NULL)
|
|
{
|
|
g_print (_("The required runtime %s was not found in a configured remote.\n"),
|
|
runtime_ref);
|
|
}
|
|
else
|
|
{
|
|
runtime_remote = ask_for_remote (self, (const char **)remotes);
|
|
}
|
|
|
|
if (runtime_remote == NULL)
|
|
return flatpak_fail (error,
|
|
"The Application %s requires the runtime %s which is not installed",
|
|
pref, runtime_ref);
|
|
|
|
flatpak_transaction_add_op (self, runtime_remote, full_runtime_ref, NULL, NULL, NULL,
|
|
FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE);
|
|
}
|
|
else
|
|
{
|
|
/* Update if in same dir */
|
|
if (dir_ref_is_installed (self->dir, full_runtime_ref, &runtime_remote, NULL))
|
|
{
|
|
FlatpakTransactionOp *op;
|
|
g_debug ("Updating dependent runtime %s", full_runtime_ref);
|
|
op = flatpak_transaction_add_op (self, runtime_remote, full_runtime_ref, NULL, NULL, NULL,
|
|
FLATPAK_TRANSACTION_OP_KIND_UPDATE);
|
|
op->non_fatal = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (runtime_remote != NULL &&
|
|
!add_related (self, runtime_remote, full_runtime_ref, error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
flatpak_transaction_add_ref (FlatpakTransaction *self,
|
|
const char *remote,
|
|
const char *ref,
|
|
const char **subpaths,
|
|
const char *commit,
|
|
FlatpakTransactionOpKind kind,
|
|
GFile *bundle,
|
|
const char *metadata,
|
|
GError **error)
|
|
{
|
|
g_autofree char *origin = NULL;
|
|
const char *pref;
|
|
g_autofree char *remote_metadata = NULL;
|
|
g_autoptr(GKeyFile) metakey = NULL;
|
|
g_autoptr(GError) local_error = NULL;
|
|
|
|
pref = strchr (ref, '/') + 1;
|
|
|
|
if (kind == FLATPAK_TRANSACTION_OP_KIND_UPDATE)
|
|
{
|
|
if (!dir_ref_is_installed (self->dir, ref, &origin, NULL))
|
|
{
|
|
g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED,
|
|
_("%s not installed"), pref);
|
|
return FALSE;
|
|
}
|
|
|
|
if (flatpak_dir_get_remote_disabled (self->dir, origin))
|
|
{
|
|
g_debug (_("Remote %s disabled, ignoring %s update"), origin, pref);
|
|
return TRUE;
|
|
}
|
|
remote = origin;
|
|
}
|
|
else if (kind == FLATPAK_TRANSACTION_OP_KIND_INSTALL)
|
|
{
|
|
g_assert (remote != NULL);
|
|
if (dir_ref_is_installed (self->dir, ref, NULL, NULL))
|
|
{
|
|
g_printerr (_("%s already installed, skipping\n"), pref);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (metadata == NULL && remote != NULL)
|
|
{
|
|
if (flatpak_dir_fetch_ref_cache (self->dir, remote, ref, NULL, NULL, &remote_metadata, NULL, &local_error))
|
|
metadata = remote_metadata;
|
|
else
|
|
{
|
|
g_print ("Warning: Can't find dependencies: %s\n", local_error->message);
|
|
g_clear_error (&local_error);
|
|
}
|
|
}
|
|
|
|
if (metadata)
|
|
{
|
|
metakey = g_key_file_new ();
|
|
if (!g_key_file_load_from_data (metakey, metadata, -1, 0, NULL))
|
|
g_clear_object (&metakey);
|
|
}
|
|
|
|
if (metakey)
|
|
{
|
|
g_autofree char *required_version = NULL;
|
|
const char *group;
|
|
int required_major, required_minor, required_micro;
|
|
|
|
if (g_str_has_prefix (ref, "app/"))
|
|
group = "Application";
|
|
else
|
|
group = "Runtime";
|
|
|
|
required_version = g_key_file_get_string (metakey, group, "required-flatpak", NULL);
|
|
if (required_version)
|
|
{
|
|
if (sscanf (required_version, "%d.%d.%d", &required_major, &required_minor, &required_micro) != 3)
|
|
g_print ("Invalid require-flatpak argument %s\n", required_version);
|
|
else
|
|
{
|
|
if (required_major > PACKAGE_MAJOR_VERSION ||
|
|
(required_major == PACKAGE_MAJOR_VERSION && required_minor > PACKAGE_MINOR_VERSION) ||
|
|
(required_major == PACKAGE_MAJOR_VERSION && required_minor == PACKAGE_MINOR_VERSION && required_micro > PACKAGE_MICRO_VERSION))
|
|
return flatpak_fail (error, _("%s needs a later flatpak version (%s)"), ref, required_version);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (self->add_deps)
|
|
{
|
|
if (!add_deps (self, metakey, remote, ref, error))
|
|
return FALSE;
|
|
}
|
|
|
|
flatpak_transaction_add_op (self, remote, ref, subpaths, commit, bundle, kind);
|
|
|
|
if (!add_related (self, remote, ref, error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
flatpak_transaction_add_install (FlatpakTransaction *self,
|
|
const char *remote,
|
|
const char *ref,
|
|
const char **subpaths,
|
|
GError **error)
|
|
{
|
|
const char *all_paths[] = { NULL };
|
|
|
|
/* If we install with no special args pull all subpaths */
|
|
if (subpaths == NULL)
|
|
subpaths = all_paths;
|
|
|
|
return flatpak_transaction_add_ref (self, remote, ref, subpaths, NULL, FLATPAK_TRANSACTION_OP_KIND_INSTALL, NULL, NULL, error);
|
|
}
|
|
|
|
gboolean
|
|
flatpak_transaction_add_install_bundle (FlatpakTransaction *self,
|
|
GFile *file,
|
|
GBytes *gpg_data,
|
|
GError **error)
|
|
{
|
|
g_autofree char *remote = NULL;
|
|
g_autofree char *ref = NULL;
|
|
g_autofree char *metadata = NULL;
|
|
gboolean created_remote;
|
|
|
|
remote = flatpak_dir_ensure_bundle_remote (self->dir, file, gpg_data,
|
|
&ref, &metadata, &created_remote,
|
|
NULL, error);
|
|
if (remote == NULL)
|
|
return FALSE;
|
|
|
|
if (!flatpak_dir_recreate_repo (self->dir, NULL, error))
|
|
return FALSE;
|
|
|
|
return flatpak_transaction_add_ref (self, remote, ref, NULL, NULL, FLATPAK_TRANSACTION_OP_KIND_BUNDLE, file, metadata, error);
|
|
}
|
|
|
|
gboolean
|
|
flatpak_transaction_add_update (FlatpakTransaction *self,
|
|
const char *ref,
|
|
const char **subpaths,
|
|
const char *commit,
|
|
GError **error)
|
|
{
|
|
const char *all_paths[] = { NULL };
|
|
|
|
/* If specify an empty subpath, that means all subpaths */
|
|
if (subpaths != NULL && subpaths[0] != NULL && subpaths[0][0] == 0)
|
|
subpaths = all_paths;
|
|
|
|
return flatpak_transaction_add_ref (self, NULL, ref, subpaths, commit, FLATPAK_TRANSACTION_OP_KIND_UPDATE, NULL, NULL, error);
|
|
}
|
|
|
|
gboolean
|
|
flatpak_transaction_update_metadata (FlatpakTransaction *self,
|
|
gboolean all_remotes,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
g_auto(GStrv) remotes = NULL;
|
|
int i;
|
|
GList *l;
|
|
|
|
/* Collect all dir+remotes used in this transaction */
|
|
|
|
if (all_remotes)
|
|
{
|
|
remotes = flatpak_dir_list_remotes (self->dir, NULL, error);
|
|
if (remotes == NULL)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
g_autoptr(GHashTable) ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
for (l = self->ops; l != NULL; l = l->next)
|
|
{
|
|
FlatpakTransactionOp *op = l->data;
|
|
g_hash_table_add (ht, g_strdup (op->remote));
|
|
}
|
|
remotes = (char **)g_hash_table_get_keys_as_array (ht, NULL);
|
|
g_hash_table_steal_all (ht); /* Move ownership to remotes */
|
|
}
|
|
|
|
/* Update metadata for said remotes */
|
|
for (i = 0; remotes[i] != NULL; i++)
|
|
{
|
|
char *remote = remotes[i];
|
|
|
|
g_debug ("Updating remote metadata for %s", remote);
|
|
if (!flatpak_dir_update_remote_configuration (self->dir, remote, cancellable, error))
|
|
return FALSE;
|
|
}
|
|
|
|
/* Reload changed configuration */
|
|
if (!flatpak_dir_recreate_repo (self->dir, cancellable, error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
flatpak_transaction_run (FlatpakTransaction *self,
|
|
gboolean stop_on_first_error,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
gboolean succeeded = TRUE;
|
|
|
|
self->ops = g_list_reverse (self->ops);
|
|
|
|
for (l = self->ops; l != NULL; l = l->next)
|
|
{
|
|
FlatpakTransactionOp *op = l->data;
|
|
g_autoptr(GError) local_error = NULL;
|
|
gboolean res;
|
|
const char *pref;
|
|
const char *opname;
|
|
FlatpakTransactionOpKind kind;
|
|
FlatpakTerminalProgress terminal_progress = { 0 };
|
|
|
|
kind = op->kind;
|
|
if (kind == FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE)
|
|
{
|
|
g_autoptr(GVariant) deploy_data = NULL;
|
|
|
|
if (dir_ref_is_installed (self->dir, op->ref, NULL, &deploy_data))
|
|
{
|
|
g_autofree const char **current_subpaths = NULL;
|
|
|
|
/* When we update a dependency, we always inherit the subpaths
|
|
rather than use the default. */
|
|
g_strfreev (op->subpaths);
|
|
current_subpaths = flatpak_deploy_data_get_subpaths (deploy_data);
|
|
op->subpaths = g_strdupv ((char **)current_subpaths);
|
|
|
|
/* Don't use the remote from related ref on update, always use
|
|
the current remote. */
|
|
g_free (op->remote);
|
|
op->remote = g_strdup (flatpak_deploy_data_get_origin (deploy_data));
|
|
|
|
kind = FLATPAK_TRANSACTION_OP_KIND_UPDATE;
|
|
}
|
|
else
|
|
kind = FLATPAK_TRANSACTION_OP_KIND_INSTALL;
|
|
}
|
|
|
|
pref = strchr (op->ref, '/') + 1;
|
|
|
|
|
|
if (kind == FLATPAK_TRANSACTION_OP_KIND_INSTALL)
|
|
{
|
|
g_autoptr(OstreeAsyncProgress) progress = flatpak_progress_new (flatpak_terminal_progress_cb, &terminal_progress);
|
|
opname = _("install");
|
|
g_print (_("Installing: %s from %s\n"), pref, op->remote);
|
|
res = flatpak_dir_install (self->dir,
|
|
self->no_pull,
|
|
self->no_deploy,
|
|
self->no_static_deltas,
|
|
op->ref, op->remote,
|
|
(const char **)op->subpaths,
|
|
progress,
|
|
cancellable, &local_error);
|
|
ostree_async_progress_finish (progress);
|
|
flatpak_terminal_progress_end (&terminal_progress);
|
|
}
|
|
else if (kind == FLATPAK_TRANSACTION_OP_KIND_UPDATE)
|
|
{
|
|
opname = _("update");
|
|
g_autofree char *target_commit = flatpak_dir_check_for_update (self->dir, op->ref, op->remote, op->commit,
|
|
(const char **)op->subpaths,
|
|
self->no_pull,
|
|
cancellable, &local_error);
|
|
if (target_commit != NULL)
|
|
{
|
|
g_print (_("Updating: %s from %s\n"), pref, op->remote);
|
|
g_autoptr(OstreeAsyncProgress) progress = flatpak_progress_new (flatpak_terminal_progress_cb, &terminal_progress);
|
|
res = flatpak_dir_update (self->dir,
|
|
self->no_pull,
|
|
self->no_deploy,
|
|
self->no_static_deltas,
|
|
op->commit != NULL, /* Allow downgrade if we specify commit */
|
|
op->ref, op->remote, target_commit,
|
|
(const char **)op->subpaths,
|
|
progress,
|
|
cancellable, &local_error);
|
|
ostree_async_progress_finish (progress);
|
|
flatpak_terminal_progress_end (&terminal_progress);
|
|
if (res)
|
|
{
|
|
g_autoptr(GVariant) deploy_data = NULL;
|
|
g_autofree char *commit = NULL;
|
|
deploy_data = flatpak_dir_get_deploy_data (self->dir, op->ref, NULL, NULL);
|
|
commit = g_strndup (flatpak_deploy_data_get_commit (deploy_data), 12);
|
|
g_print (_("Now at %s.\n"), commit);
|
|
}
|
|
|
|
/* Handle noop-updates */
|
|
if (!res && g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED))
|
|
{
|
|
g_print (_("No updates.\n"));
|
|
res = TRUE;
|
|
g_clear_error (&local_error);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res = FALSE;
|
|
if (g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED))
|
|
{
|
|
res = TRUE;
|
|
g_clear_error (&local_error);
|
|
}
|
|
}
|
|
}
|
|
else if (kind == FLATPAK_TRANSACTION_OP_KIND_BUNDLE)
|
|
{
|
|
g_autofree char *bundle_basename = g_file_get_basename (op->bundle);
|
|
opname = _("install bundle");
|
|
g_print (_("Installing: %s from bundle %s\n"), pref, bundle_basename);
|
|
res = flatpak_dir_install_bundle (self->dir, op->bundle,
|
|
op->remote, NULL,
|
|
cancellable, &local_error);
|
|
}
|
|
else
|
|
g_assert_not_reached ();
|
|
|
|
if (!res)
|
|
{
|
|
if (op->non_fatal)
|
|
{
|
|
g_printerr (_("Warning: Failed to %s %s: %s\n"),
|
|
opname, pref, local_error->message);
|
|
}
|
|
else if (!stop_on_first_error)
|
|
{
|
|
g_printerr (_("Error: Failed to %s %s: %s\n"),
|
|
opname, pref, local_error->message);
|
|
if (succeeded)
|
|
{
|
|
succeeded = FALSE;
|
|
flatpak_fail (error, _("One or more operations failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return succeeded;
|
|
}
|