update/install: Install/Update runtime dependencies as needed

When an application requires a runtime that is not installed, search
for it and prompt for permissions to install it. Also, update required
runtimes when the app is being updated.
This commit is contained in:
Alexander Larsson committed 2016-10-19 12:40:49 +02:00
1 parent b2af78fb6f
commit 28261d839e
13 files changed
+892 -311

No files matched your search

+4
View File
@@ -5,6 +5,10 @@ bin_PROGRAMS += \
flatpak_SOURCES = \
app/flatpak-main.c \
app/flatpak-builtins.h \
app/flatpak-builtins-utils.h \
app/flatpak-builtins-utils.c \
app/flatpak-transaction.h \
app/flatpak-transaction.c \
app/flatpak-builtins-add-remote.c \
app/flatpak-builtins-delete-remote.c \
app/flatpak-builtins-list-remotes.c \
+25 -139
View File
@@ -32,6 +32,8 @@
#include "libglnx/libglnx.h"
#include "flatpak-builtins.h"
#include "flatpak-builtins-utils.h"
#include "flatpak-transaction.h"
#include "flatpak-utils.h"
#include "lib/flatpak-error.h"
#include "flatpak-chain-input-stream.h"
@@ -53,7 +55,7 @@ static GOptionEntry options[] = {
{ "no-pull", 0, 0, G_OPTION_ARG_NONE, &opt_no_pull, N_("Don't pull, only install from local cache"), NULL },
{ "no-deploy", 0, 0, G_OPTION_ARG_NONE, &opt_no_deploy, N_("Don't deploy, only download to local cache"), NULL },
{ "no-related", 0, 0, G_OPTION_ARG_NONE, &opt_no_related, N_("Don't install related refs"), NULL },
{ "no-deps", 0, 0, G_OPTION_ARG_NONE, &opt_no_deps, N_("Don't verify runtime dependencies"), NULL },
{ "no-deps", 0, 0, G_OPTION_ARG_NONE, &opt_no_deps, N_("Don't verify/install runtime dependencies"), NULL },
{ "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, N_("Look for runtime with the specified name"), NULL },
{ "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, N_("Look for app with the specified name"), NULL },
{ "bundle", 0, 0, G_OPTION_ARG_NONE, &opt_bundle, N_("Install from local bundle file"), NULL },
@@ -142,78 +144,6 @@ install_bundle (FlatpakDir *dir,
return TRUE;
}
static gboolean
do_install (FlatpakDir *dir,
gboolean no_pull,
gboolean no_deploy,
const char *ref,
const char *remote_name,
const char **opt_subpaths,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GPtrArray) related = NULL;
const char *slash;
int i;
slash = strchr (ref, '/');
g_print (_("Installing: %s\n"), slash + 1);
if (!flatpak_dir_install (dir,
opt_no_pull,
opt_no_deploy,
ref, remote_name,
(const char **)opt_subpaths,
NULL,
cancellable, error))
return FALSE;
if (!opt_no_related)
{
g_autoptr(GError) local_error = NULL;
if (opt_no_pull)
related = flatpak_dir_find_local_related (dir, ref, remote_name, NULL, &local_error);
else
related = flatpak_dir_find_remote_related (dir, ref, remote_name, 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);
g_auto(GStrv) parts = NULL;
if (!rel->download)
continue;
parts = g_strsplit (rel->ref, "/", 0);
g_print (_("Installing related: %s\n"), parts[1]);
if (!flatpak_dir_install_or_update (dir,
opt_no_pull,
opt_no_deploy,
rel->ref, remote_name,
(const char **)rel->subpaths,
NULL,
cancellable, &local_error))
{
if (!g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED))
g_printerr (_("Warning: Failed to install related ref %s: %s\n"),
rel->ref, local_error->message);
g_clear_error (&local_error);
}
}
}
}
return TRUE;
}
static gboolean
install_from (FlatpakDir *dir,
@@ -232,6 +162,7 @@ install_from (FlatpakDir *dir,
g_auto(GStrv) parts = NULL;
const char *slash;
FlatpakDir *clone;
g_autoptr(FlatpakTransaction) transaction = NULL;
if (argc < 2)
return usage_error (context, _("Filename must be specified"), error);
@@ -259,30 +190,13 @@ install_from (FlatpakDir *dir,
slash = strchr (ref, '/');
g_print (_("Installing: %s\n"), slash + 1);
if (!do_install (clone,
opt_no_pull,
opt_no_deploy,
ref, remote,
(const char **)opt_subpaths,
cancellable, error))
transaction = flatpak_transaction_new (dir, opt_no_pull, opt_no_deploy,
!opt_no_deps, !opt_no_related);
if (!flatpak_transaction_add_install (transaction, remote, ref, (const char **)opt_subpaths, error))
return FALSE;
return TRUE;
}
static gboolean
looks_like_branch (const char *branch)
{
/* In particular, / is not a valid branch char, so
this lets us distinguish full or partial refs as
non-branches. */
if (!flatpak_is_valid_branch (branch, NULL))
return FALSE;
/* Dots are allowed in branches, but not really used much, while
they are required for app ids, so thats a good check to
distinguish the two */
if (strchr (branch, '.') != NULL)
if (!flatpak_transaction_run (transaction, TRUE, cancellable, error))
return FALSE;
return TRUE;
@@ -293,16 +207,17 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
{
g_autoptr(GOptionContext) context = NULL;
g_autoptr(FlatpakDir) dir = NULL;
const char *repository;
const char *remote;
char **prefs = NULL;
int i, n_prefs;
g_autofree char *target_branch = NULL;
g_autofree char *default_branch = NULL;
FlatpakKinds kinds;
g_autoptr(FlatpakTransaction) transaction = NULL;
g_autoptr(GPtrArray) refs = NULL;
g_autoptr(GHashTable) refs_hash = NULL;
context = g_option_context_new (_("REPOSITORY REF... - Install applications or runtimes"));
context = g_option_context_new (_("REMOTE REF... - Install applications or runtimes"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
@@ -315,24 +230,25 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
return install_from (dir, context, argc, argv, cancellable, error);
if (argc < 3)
return usage_error (context, _("REPOSITORY and REF must be specified"), error);
return usage_error (context, _("REMOTE and REF must be specified"), error);
repository = argv[1];
remote = argv[1];
prefs = &argv[2];
n_prefs = argc - 2;
/* Backwards compat for old "REPOSITORY NAME [BRANCH]" argument version */
/* Backwards compat for old "REMOTE NAME [BRANCH]" argument version */
if (argc == 4 && looks_like_branch (argv[3]))
{
target_branch = g_strdup (argv[3]);
n_prefs = 1;
}
default_branch = flatpak_dir_get_remote_default_branch (dir, repository);
default_branch = flatpak_dir_get_remote_default_branch (dir, remote);
kinds = flatpak_kinds_from_bools (opt_app, opt_runtime);
refs = g_ptr_array_new_with_free_func (g_free);
refs_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
transaction = flatpak_transaction_new (dir, opt_no_pull, opt_no_deploy,
!opt_no_deps, !opt_no_related);
for (i = 0; i < n_prefs; i++)
{
const char *pref = prefs[i];
@@ -342,54 +258,24 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
g_autofree char *branch = NULL;
FlatpakKinds kind;
g_autofree char *ref = NULL;
g_autofree char *metadata = NULL;
g_autoptr(GKeyFile) metakey = NULL;
g_autofree char *runtime_ref = NULL;
if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, target_branch,
&matched_kinds, &id, &arch, &branch, error))
return FALSE;
ref = flatpak_dir_find_remote_ref (dir, repository, id, branch, default_branch, arch,
ref = flatpak_dir_find_remote_ref (dir, remote, id, branch, default_branch, arch,
matched_kinds, &kind, cancellable, error);
if (ref == NULL)
return FALSE;
metakey = g_key_file_new ();
if (!opt_no_deps &&
flatpak_dir_fetch_ref_cache (dir, repository, ref, NULL, NULL, &metadata, cancellable, NULL) &&
g_key_file_load_from_data (metakey, metadata, -1, 0, NULL))
{
g_autofree char *runtime_ref = NULL;
g_autofree char *full_runtime_ref = NULL;
runtime_ref = g_key_file_get_string (metakey, "Application", "runtime", NULL);
full_runtime_ref = g_strconcat ("runtime/", runtime_ref, NULL);
if (runtime_ref && !g_hash_table_lookup_extended (refs_hash, full_runtime_ref, NULL, NULL))
{
g_autoptr(FlatpakDeploy) deploy = flatpak_find_deploy_for_ref (full_runtime_ref, NULL, NULL);
if (deploy == NULL)
return flatpak_fail (error,
"The Application %s requires the runtime %s which is not installed",
id, runtime_ref);
}
}
if (g_hash_table_insert (refs_hash, g_strdup (ref), NULL))
g_ptr_array_add (refs, g_steal_pointer (&ref));
}
for (i = 0; i < refs->len; i++)
{
const char *ref = g_ptr_array_index (refs, i);
if (!do_install (dir,
opt_no_pull,
opt_no_deploy,
ref, repository,
(const char **)opt_subpaths,
cancellable, error))
if (!flatpak_transaction_add_install (transaction, remote, ref, (const char **)opt_subpaths, error))
return FALSE;
}
if (!flatpak_transaction_run (transaction, TRUE, cancellable, error))
return FALSE;
return TRUE;
}
+1 -18
View File
@@ -30,6 +30,7 @@
#include "libglnx/libglnx.h"
#include "flatpak-builtins.h"
#include "flatpak-builtins-utils.h"
#include "flatpak-utils.h"
static char *opt_arch;
@@ -49,24 +50,6 @@ static GOptionEntry options[] = {
{ NULL }
};
static gboolean
looks_like_branch (const char *branch)
{
/* In particular, / is not a valid branch char, so
this lets us distinguish full or partial refs as
non-branches. */
if (!flatpak_is_valid_branch (branch, NULL))
return FALSE;
/* Dots are allowed in branches, but not really used much, while
they are required for app ids, so thats a good check to
distinguish the two */
if (strchr (branch, '.') != NULL)
return FALSE;
return TRUE;
}
gboolean
flatpak_builtin_uninstall (int argc, char **argv, GCancellable *cancellable, GError **error)
{
+15 -154
View File
@@ -30,6 +30,8 @@
#include "libglnx/libglnx.h"
#include "flatpak-builtins.h"
#include "flatpak-builtins-utils.h"
#include "flatpak-transaction.h"
#include "flatpak-utils.h"
#include "flatpak-error.h"
@@ -40,6 +42,7 @@ static gboolean opt_force_remove;
static gboolean opt_no_pull;
static gboolean opt_no_deploy;
static gboolean opt_no_related;
static gboolean opt_no_deps;
static gboolean opt_runtime;
static gboolean opt_app;
static gboolean opt_appstream;
@@ -51,6 +54,7 @@ static GOptionEntry options[] = {
{ "no-pull", 0, 0, G_OPTION_ARG_NONE, &opt_no_pull, N_("Don't pull, only update from local cache"), NULL },
{ "no-deploy", 0, 0, G_OPTION_ARG_NONE, &opt_no_deploy, N_("Don't deploy, only download to local cache"), NULL },
{ "no-related", 0, 0, G_OPTION_ARG_NONE, &opt_no_related, N_("Don't update related refs"), NULL},
{ "no-deps", 0, 0, G_OPTION_ARG_NONE, &opt_no_deps, N_("Don't verify/install runtime dependencies"), NULL },
{ "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, N_("Look for runtime with the specified name"), NULL },
{ "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, N_("Look for app with the specified name"), NULL },
{ "appstream", 0, 0, G_OPTION_ARG_NONE, &opt_appstream, N_("Update appstream for remote"), NULL },
@@ -96,138 +100,6 @@ update_appstream (FlatpakDir *dir, const char *remote, GCancellable *cancellable
return TRUE;
}
static gboolean
do_update (FlatpakDir * dir,
const char *ref,
GCancellable *cancellable,
GError **error)
{
g_auto(GStrv) parts = flatpak_decompose_ref (ref, error);
g_autofree char *repository = NULL;
g_autoptr(GPtrArray) related = NULL;
g_autoptr(GError) update_error = NULL;
int i;
if (parts == NULL)
return FALSE;
repository = flatpak_dir_get_origin (dir, ref, cancellable, error);
if (repository == NULL)
return FALSE;
if (strcmp (parts[0], "app") == 0)
g_print (_("Updating application %s, branch %s\n"), parts[1], parts[3]);
else
g_print (_("Updating runtime %s, branch %s\n"), parts[1], parts[3]);
if (flatpak_dir_get_remote_disabled (dir, repository))
{
g_print (_("Remote %s disabled\n"), repository);
return TRUE;
}
if (!flatpak_dir_update (dir,
opt_no_pull,
opt_no_deploy,
ref, repository, opt_commit, (const char **)opt_subpaths,
NULL,
cancellable, &update_error))
{
if (g_error_matches (update_error, FLATPAK_ERROR,
FLATPAK_ERROR_ALREADY_INSTALLED))
g_print (_("No updates.\n"));
else
g_printerr ("Error updating: %s\n", update_error->message);
}
else
{
g_autoptr(GVariant) deploy_data = NULL;
g_autofree char *commit = NULL;
deploy_data = flatpak_dir_get_deploy_data (dir, ref, NULL, NULL);
commit = g_strndup (flatpak_deploy_data_get_commit (deploy_data), 12);
g_print (_("Now at %s.\n"), commit);
}
if (!opt_no_related)
{
g_autoptr(GError) local_error = NULL;
if (opt_no_pull)
related = flatpak_dir_find_local_related (dir, ref, repository, NULL,
&local_error);
else
related = flatpak_dir_find_remote_related (dir, ref, repository, 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);
g_autoptr(GError) local_error = NULL;
g_auto(GStrv) parts = NULL;
if (!rel->download)
continue;
parts = g_strsplit (rel->ref, "/", 0);
g_print (_("Updating related: %s\n"), parts[1]);
if (!flatpak_dir_install_or_update (dir,
opt_no_pull,
opt_no_deploy,
rel->ref, repository,
(const char **)rel->subpaths,
NULL,
cancellable, &local_error))
{
if (g_error_matches (local_error, FLATPAK_ERROR,
FLATPAK_ERROR_ALREADY_INSTALLED))
g_print (_("No updates.\n"));
else
g_printerr ("Error updating: %s\n", local_error->message);
g_clear_error (&local_error);
}
else
{
g_autoptr(GVariant) deploy_data = NULL;
g_autofree char *commit = NULL;
deploy_data = flatpak_dir_get_deploy_data (dir, rel->ref, NULL, NULL);
commit = g_strndup (flatpak_deploy_data_get_commit (deploy_data), 12);
g_print (_("Now at %s.\n"), commit);
}
}
}
}
return TRUE;
}
static gboolean
looks_like_branch (const char *branch)
{
/* In particular, / is not a valid branch char, so
this lets us distinguish full or partial refs as
non-branches. */
if (!flatpak_is_valid_branch (branch, NULL))
return FALSE;
/* Dots are allowed in branches, but not really used much, while
they are required for app ids, so thats a good check to
distinguish the two */
if (strchr (branch, '.') != NULL)
return FALSE;
return TRUE;
}
gboolean
flatpak_builtin_update (int argc,
char **argv,
@@ -239,10 +111,8 @@ flatpak_builtin_update (int argc,
char **prefs = NULL;
int i, j, n_prefs;
const char *default_branch = NULL;
gboolean failed = FALSE;
FlatpakKinds kinds;
g_autoptr(GHashTable) update_refs_hash = NULL;
g_autoptr(GPtrArray) update_refs = NULL;
g_autoptr(FlatpakTransaction) transaction = NULL;
context = g_option_context_new (_("[REF...] - Update applications or runtimes"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
@@ -263,10 +133,12 @@ flatpak_builtin_update (int argc,
n_prefs = 1;
}
update_refs = g_ptr_array_new_with_free_func (g_free);
update_refs_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
transaction = flatpak_transaction_new (dir, opt_no_pull, opt_no_deploy,
!opt_no_deps, !opt_no_related);
kinds = flatpak_kinds_from_bools (opt_app, opt_runtime);
g_print ("Looking for updates...\n");
for (j = 0; j == 0 || j < n_prefs; j++)
{
const char *pref = NULL;
@@ -313,8 +185,8 @@ flatpak_builtin_update (int argc,
continue;
found = TRUE;
if (g_hash_table_insert (update_refs_hash, g_strdup (refs[i]), NULL))
g_ptr_array_add (update_refs, g_strdup (refs[i]));
if (!flatpak_transaction_add_update (transaction, refs[i], (const char **)opt_subpaths, error))
return FALSE;
}
}
@@ -344,8 +216,8 @@ flatpak_builtin_update (int argc,
continue;
found = TRUE;
if (g_hash_table_insert (update_refs_hash, g_strdup (refs[i]), NULL))
g_ptr_array_add (update_refs, g_strdup (refs[i]));
if (!flatpak_transaction_add_update (transaction, refs[i], (const char **)opt_subpaths, error))
return FALSE;
}
}
@@ -357,19 +229,8 @@ flatpak_builtin_update (int argc,
}
}
for (i = 0; i < update_refs->len; i++)
{
const char *ref = (char *)g_ptr_array_index (update_refs, i);
g_autoptr(GError) local_error = NULL;
if (!do_update (dir, ref, cancellable, &local_error))
{
g_printerr (_("Error updating: %s\n"), local_error->message);
failed = TRUE;
}
}
if (failed)
return flatpak_fail (error, _("One or more updates failed"));
if (!flatpak_transaction_run (transaction, FALSE, cancellable, error))
return FALSE;
return TRUE;
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright © 2014 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 <glib/gi18n.h>
#include "flatpak-builtins-utils.h"
#include "flatpak-utils.h"
gboolean
looks_like_branch (const char *branch)
{
/* In particular, / is not a valid branch char, so
this lets us distinguish full or partial refs as
non-branches. */
if (!flatpak_is_valid_branch (branch, NULL))
return FALSE;
/* Dots are allowed in branches, but not really used much, while
they are required for app ids, so thats a good check to
distinguish the two */
if (strchr (branch, '.') != NULL)
return FALSE;
return TRUE;
}
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright © 2014 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>
*/
#ifndef __FLATPAK_BUILTINS_UTILS_H__
#define __FLATPAK_BUILTINS_UTILS_H__
#include <glib.h>
#include "libglnx/libglnx.h"
gboolean looks_like_branch (const char *branch);
#endif /* __FLATPAK_BUILTINS_UTILS_H__ */
+546
View File
@@ -0,0 +1,546 @@
/*
* 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 <glib/gi18n.h>
#include "flatpak-transaction.h"
#include "flatpak-utils.h"
#include "flatpak-builtins-utils.h"
#include "flatpak-error.h"
typedef struct FlatpakTransactionOp FlatpakTransactionOp;
struct FlatpakTransactionOp {
char *remote;
char *ref;
char **subpaths;
char *commit; /* TODO: handle */
gboolean update;
gboolean install;
gboolean non_fatal;
};
struct FlatpakTransaction {
FlatpakDir *dir;
GHashTable *refs;
GList *ops;
gboolean no_pull;
gboolean no_deploy;
gboolean add_deps;
gboolean add_related;
};
static gboolean
ref_is_installed (const char *ref)
{
g_autoptr(FlatpakDeploy) deploy = NULL;
deploy = flatpak_find_deploy_for_ref (ref, NULL, NULL);
return deploy != NULL;
}
static gboolean
dir_ref_is_installed (FlatpakDir *dir, const char *ref, char **remote_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));
return TRUE;
}
static char *
transaction_fetch_runtime_ref (FlatpakDir *dir, const char *remote, const char *ref)
{
g_autofree char *metadata = NULL;
g_autoptr(GKeyFile) metakey = NULL;
g_autofree char *runtime_ref = NULL;
if (!g_str_has_prefix (ref, "app/"))
return NULL;
if (!flatpak_dir_fetch_ref_cache (dir, remote, ref, NULL, NULL, &metadata, NULL, NULL))
return NULL;
metakey = g_key_file_new ();
if (!g_key_file_load_from_data (metakey, metadata, -1, 0, NULL))
return NULL;
runtime_ref = g_key_file_get_string (metakey, "Application", "runtime", NULL);
return g_steal_pointer (&runtime_ref);
}
static FlatpakTransactionOp *
flatpak_transaction_operation_new (const char *remote,
const char *ref,
const char **subpaths,
gboolean install,
gboolean update)
{
FlatpakTransactionOp *self = g_new0 (FlatpakTransactionOp, 1);
self->remote = g_strdup (remote);
self->ref = g_strdup (ref);
self->subpaths = g_strdupv ((char **)subpaths);
self->update = update;
self->install = install;
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_free (self);
}
FlatpakTransaction *
flatpak_transaction_new (FlatpakDir *dir,
gboolean no_pull,
gboolean no_deploy,
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_pull = no_pull;
t->no_deploy = no_deploy;
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);
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);
}
FlatpakTransactionOp *
flatpak_transaction_add_op (FlatpakTransaction *self,
const char *remote,
const char *ref,
const char **subpaths,
gboolean install,
gboolean update)
{
FlatpakTransactionOp *op;
g_autofree char *subpaths_str = NULL;
const char *opname;
if (install)
{
if (update)
opname = "install/update";
else
opname = "install";
}
else
opname = "update";
subpaths_str = subpaths_to_string (subpaths);
g_debug ("Transaction: %s %s:%s%s",
opname, remote, ref, 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, install, update);
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 (const char **remotes)
{
int n_remotes = g_strv_length ((char **)remotes);
int chosen = 0;
int i;
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,
TRUE, TRUE);
op->non_fatal = TRUE;
}
}
return TRUE;
}
static gboolean
add_deps (FlatpakTransaction *self,
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;
runtime_ref = transaction_fetch_runtime_ref (self->dir, remote, ref);
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))
{
if (!ref_is_installed (full_runtime_ref))
{
g_auto(GStrv) remotes = NULL;
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 ((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, TRUE, TRUE);
}
else
{
/* Update if in same dir */
if (dir_ref_is_installed (self->dir, full_runtime_ref, &runtime_remote))
{
flatpak_transaction_add_op (self, runtime_remote, full_runtime_ref, NULL, FALSE, 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,
gboolean is_update,
GError **error)
{
g_autofree char *origin = NULL;
const char *pref;
pref = strchr (ref, '/') + 1;
if (is_update)
{
if (!dir_ref_is_installed (self->dir, ref, &origin))
{
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
{
g_assert (remote != NULL);
if (dir_ref_is_installed (self->dir, ref, NULL))
{
g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED,
_("%s already installed"), pref);
return FALSE;
}
}
if (self->add_deps)
add_deps (self, remote, ref, error);
flatpak_transaction_add_op (self, remote, ref, subpaths, !is_update, is_update);
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, FALSE, error);
}
gboolean
flatpak_transaction_add_update (FlatpakTransaction *self,
const char *ref,
const char **subpaths,
GError **error)
{
return flatpak_transaction_add_ref (self, NULL, ref, subpaths, TRUE, error);
}
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;
g_autofree char *subpaths_str = subpaths_to_string ((const char **)op->subpaths);
const char *pref;
const char *opname;
if (op->install && op->update)
{
if (dir_ref_is_installed (self->dir, op->ref, NULL))
op->install = FALSE;
else
op->update = FALSE;
}
pref = strchr (op->ref, '/') + 1;
if (op->install)
{
opname = _("install");
g_print (_("Installing: %s from %s\n"), pref, op->remote);
res = TRUE;
res = flatpak_dir_install (self->dir,
self->no_pull,
self->no_deploy,
op->ref, op->remote,
(const char **)op->subpaths,
NULL,
cancellable, &local_error);
}
else /* update */
{
opname = _("update");
g_print (_("Updating: %s from %s\n"), pref, op->remote);
res = flatpak_dir_update (self->dir,
self->no_pull,
self->no_deploy,
op->ref, op->remote, op->commit,
(const char **)op->subpaths,
NULL,
cancellable, &local_error);
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);
}
}
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, local_error);
return FALSE;
}
}
}
return succeeded;
}
+54
View File
@@ -0,0 +1,54 @@
/*
* 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>
*/
#ifndef __FLATPAK_TRANSACTION_H__
#define __FLATPAK_TRANSACTION_H__
#include <glib.h>
#include "libglnx/libglnx.h"
#include "flatpak-dir.h"
typedef struct FlatpakTransaction FlatpakTransaction;
FlatpakTransaction *flatpak_transaction_new (FlatpakDir *dir,
gboolean no_pull,
gboolean no_deploy,
gboolean add_deps,
gboolean add_related);
void flatpak_transaction_free (FlatpakTransaction *self);
gboolean flatpak_transaction_run (FlatpakTransaction *self,
gboolean stop_on_first_errror,
GCancellable *cancellable,
GError **error);
gboolean flatpak_transaction_add_install (FlatpakTransaction *self,
const char *remote,
const char *ref,
const char **subpaths,
GError **error);
gboolean flatpak_transaction_add_update (FlatpakTransaction *self,
const char *ref,
const char **subpaths,
GError **error);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakTransaction, flatpak_transaction_free)
#endif /* __FLATPAK_TRANSACTION_H__ */
+83
View File
@@ -4227,6 +4227,29 @@ flatpak_dir_remote_fetch_summary (FlatpakDir *self,
return TRUE;
}
gboolean
flatpak_dir_remote_has_ref (FlatpakDir *self,
const char *remote,
const char *ref)
{
g_autoptr(GBytes) summary_bytes = NULL;
g_autoptr(GVariant) summary = NULL;
g_autoptr(GError) local_error = NULL;
if (!flatpak_dir_remote_fetch_summary (self, remote,
&summary_bytes,
NULL, &local_error))
{
g_debug ("Can't get summary for remote %s: %s\n", remote, local_error->message);
return FALSE;
}
summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT,
summary_bytes, FALSE));
return flatpak_summary_lookup_ref (summary, ref, NULL);
}
/* This duplicates ostree_repo_list_refs so it can use flatpak_dir_remote_fetch_summary
and get caching */
static gboolean
@@ -4978,6 +5001,66 @@ flatpak_dir_list_remotes (FlatpakDir *self,
return res;
}
char **
flatpak_dir_list_enumerated_remotes (FlatpakDir *self,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GPtrArray) res = g_ptr_array_new_with_free_func (g_free);
g_auto(GStrv) remotes = NULL;
int i;
remotes = flatpak_dir_list_remotes (self, cancellable, error);
if (remotes == NULL)
return NULL;
for (i = 0; remotes != NULL && remotes[i] != NULL; i++)
{
const char *remote = remotes[i];
if (flatpak_dir_get_remote_disabled (self, remote))
continue;
if (flatpak_dir_get_remote_noenumerate (self, remote))
continue;
g_ptr_array_add (res, g_strdup (remote));
}
g_ptr_array_add (res, NULL);
return (char **)g_ptr_array_free (g_steal_pointer (&res), FALSE);
}
char **
flatpak_dir_search_for_dependency (FlatpakDir *self,
const char *runtime_ref,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GPtrArray) found = g_ptr_array_new_with_free_func (g_free);
g_auto(GStrv) remotes = NULL;
int i;
remotes = flatpak_dir_list_enumerated_remotes (self, cancellable, error);
if (remotes == NULL)
return NULL;
for (i = 0; remotes != NULL && remotes[i] != NULL; i++)
{
const char *remote = remotes[i];
if (flatpak_dir_get_remote_nodeps (self, remote))
continue;
if (flatpak_dir_remote_has_ref (self, remote, runtime_ref))
g_ptr_array_add (found, g_strdup (remote));
}
g_ptr_array_add (found, NULL);
return (char **)g_ptr_array_free (g_steal_pointer (&found), FALSE);
}
gboolean
flatpak_dir_remove_remote (FlatpakDir *self,
gboolean force_remove,
+10
View File
@@ -153,6 +153,13 @@ GFile * flatpak_dir_get_if_deployed (FlatpakDir *self,
const char *ref,
const char *checksum,
GCancellable *cancellable);
gboolean flatpak_dir_remote_has_ref (FlatpakDir *self,
const char *remote,
const char *ref);
char ** flatpak_dir_search_for_dependency (FlatpakDir *self,
const char *runtime_ref,
GCancellable *cancellable,
GError **error);
char * flatpak_dir_find_remote_ref (FlatpakDir *self,
const char *remote,
const char *name,
@@ -395,6 +402,9 @@ gboolean flatpak_dir_create_remote_for_ref_file (FlatpakDir *self,
char **flatpak_dir_list_remotes (FlatpakDir *self,
GCancellable *cancellable,
GError **error);
char **flatpak_dir_list_enumerated_remotes (FlatpakDir *self,
GCancellable *cancellable,
GError **error);
gboolean flatpak_dir_modify_remote (FlatpakDir *self,
const char *remote_name,
GKeyFile *config,
+69
View File
@@ -3823,6 +3823,75 @@ flatpak_allocate_tmpdir (int tmpdir_dfd,
return TRUE;
}
gboolean
flatpak_yes_no_prompt (const char *prompt, ...)
{
char buf[512];
va_list var_args;
gchar *s;
va_start (var_args, prompt);
s = g_strdup_vprintf (prompt, var_args);
while (TRUE)
{
g_print ("%s %s: ", s, "[y/n]");
if (fgets (buf, sizeof (buf), stdin) == NULL)
return FALSE;
g_strstrip (buf);
if (g_ascii_strcasecmp (buf, "y") == 0 ||
g_ascii_strcasecmp (buf, "yes") == 0)
return TRUE;
if (g_ascii_strcasecmp (buf, "n") == 0 ||
g_ascii_strcasecmp (buf, "no") == 0)
return FALSE;
}
}
static gboolean
is_number (const char *s)
{
while (*s != 0)
{
if (!g_ascii_isdigit (*s))
return FALSE;
s++;
}
return TRUE;
}
long
flatpak_number_prompt (int min, int max, const char *prompt, ...)
{
char buf[512];
va_list var_args;
gchar *s;
va_start (var_args, prompt);
s = g_strdup_vprintf (prompt, var_args);
while (TRUE)
{
g_print ("%s [%d-%d]: ", s, min, max);
if (fgets (buf, sizeof (buf), stdin) == NULL)
return FALSE;
g_strstrip (buf);
if (is_number (buf))
{
long res = strtol (buf, NULL, 10);
if (res >= min && res <= max)
return res;
}
}
}
/* Uncomment to get debug traces in /tmp/flatpak-completion-debug.txt (nice
* to not have it interfere with stdout/stderr)
*/
+3
View File
@@ -467,6 +467,9 @@ gboolean flatpak_allocate_tmpdir (int tmpdir_dfd,
GError **error);
gboolean flatpak_yes_no_prompt (const char *prompt, ...);
long flatpak_number_prompt (int min, int max, const char *prompt, ...);
typedef struct {
char *shell_cur;
char *cur;
+8
View File
@@ -157,6 +157,14 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--no-deps</option></term>
<listitem><para>
Don't update or install runtime dependencies when installing.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--app</option></term>