summaryrefslogtreecommitdiff
path: root/app/flatpak-cli-transaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/flatpak-cli-transaction.c')
-rw-r--r--app/flatpak-cli-transaction.c359
1 files changed, 295 insertions, 64 deletions
diff --git a/app/flatpak-cli-transaction.c b/app/flatpak-cli-transaction.c
index d553e244..5a08d537 100644
--- a/app/flatpak-cli-transaction.c
+++ b/app/flatpak-cli-transaction.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -40,6 +40,8 @@ struct _FlatpakCliTransaction
GError *first_operation_error;
GHashTable *eol_actions;
+ GHashTable *runtime_app_map;
+ GHashTable *extension_app_map;
int rows;
int cols;
@@ -193,28 +195,6 @@ install_authenticator (FlatpakTransaction *old_transaction,
return;
}
-static char *
-op_type_to_string (FlatpakTransactionOperationType operation_type)
-{
- switch (operation_type)
- {
- case FLATPAK_TRANSACTION_OPERATION_INSTALL:
- return _("install");
-
- case FLATPAK_TRANSACTION_OPERATION_UPDATE:
- return _("update");
-
- case FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE:
- return _("install bundle");
-
- case FLATPAK_TRANSACTION_OPERATION_UNINSTALL:
- return _("uninstall");
-
- default:
- return "Unknown type"; /* Should not happen */
- }
-}
-
static gboolean
redraw (FlatpakCliTransaction *self)
{
@@ -492,52 +472,123 @@ operation_error (FlatpakTransaction *transaction,
FlatpakTransactionOperationType op_type = flatpak_transaction_operation_get_operation_type (op);
const char *ref = flatpak_transaction_operation_get_ref (op);
g_autoptr(FlatpakRef) rref = flatpak_ref_parse (ref, NULL);
- g_autofree char *msg = NULL;
gboolean non_fatal = (detail & FLATPAK_TRANSACTION_ERROR_DETAILS_NON_FATAL) != 0;
g_autofree char *text = NULL;
+ const char *on = "";
+ const char *off = "";
+
+ if (flatpak_fancy_output ())
+ {
+ on = FLATPAK_ANSI_BOLD_ON;
+ off = FLATPAK_ANSI_BOLD_OFF;
+ }
if (g_error_matches (error, FLATPAK_ERROR, FLATPAK_ERROR_SKIPPED))
{
set_op_progress (self, op, "⍻");
- msg = g_strdup_printf (_("Info: %s was skipped"), flatpak_ref_get_name (rref));
+ text = g_strdup_printf (_("Info: %s was skipped"), flatpak_ref_get_name (rref));
if (flatpak_fancy_output ())
{
- flatpak_table_printer_set_cell (self->printer, self->progress_row, 0, msg);
+ flatpak_table_printer_set_cell (self->printer, self->progress_row, 0, text);
self->progress_row++;
flatpak_table_printer_add_span (self->printer, "");
flatpak_table_printer_finish_row (self->printer);
redraw (self);
}
else
- g_print ("%s\n", msg);
+ g_print ("%s\n", text);
return TRUE;
}
set_op_progress (self, op, "✗");
+ /* Here we go to great lengths not to split the sentences. See
+ * https://wiki.gnome.org/TranslationProject/DevGuidelines/Never%20split%20sentences
+ */
if (g_error_matches (error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED))
- msg = g_strdup_printf (_("%s already installed"), flatpak_ref_get_name (rref));
- else if (g_error_matches (error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED))
- msg = g_strdup_printf (_("%s not installed"), flatpak_ref_get_name (rref));
+ {
+ if (non_fatal)
+ text = g_strdup_printf (_("Warning: %s%s%s already installed"),
+ on, flatpak_ref_get_name (rref), off);
+ else
+ text = g_strdup_printf (_("Error: %s%s%s already installed"),
+ on, flatpak_ref_get_name (rref), off);
+ }
else if (g_error_matches (error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED))
- msg = g_strdup_printf (_("%s not installed"), flatpak_ref_get_name (rref));
+ {
+ if (non_fatal)
+ text = g_strdup_printf (_("Warning: %s%s%s not installed"),
+ on, flatpak_ref_get_name (rref), off);
+ else
+ text = g_strdup_printf (_("Error: %s%s%s not installed"),
+ on, flatpak_ref_get_name (rref), off);
+ }
else if (g_error_matches (error, FLATPAK_ERROR, FLATPAK_ERROR_NEED_NEW_FLATPAK))
- msg = g_strdup_printf (_("%s needs a later flatpak version"), flatpak_ref_get_name (rref));
+ {
+ if (non_fatal)
+ text = g_strdup_printf (_("Warning: %s%s%s needs a later flatpak version"),
+ on, flatpak_ref_get_name (rref), off);
+ else
+ text = g_strdup_printf (_("Error: %s%s%s needs a later flatpak version"),
+ on, flatpak_ref_get_name (rref), off);
+ }
else if (g_error_matches (error, FLATPAK_ERROR, FLATPAK_ERROR_OUT_OF_SPACE))
- msg = g_strdup (_("Not enough disk space to complete this operation"));
+ {
+ if (non_fatal)
+ text = g_strdup (_("Warning: Not enough disk space to complete this operation"));
+ else
+ text = g_strdup (_("Error: Not enough disk space to complete this operation"));
+ }
else if (error)
- msg = g_strdup (error->message);
+ {
+ if (non_fatal)
+ text = g_strdup_printf (_("Warning: %s"), error->message);
+ else
+ text = g_strdup_printf (_("Error: %s"), error->message);
+ }
else
- msg = g_strdup (_("(internal error, please report)"));
+ text = g_strdup ("(internal error, please report)");
if (!non_fatal && self->first_operation_error == NULL)
- g_propagate_prefixed_error (&self->first_operation_error,
- g_error_copy (error),
- _("Failed to %s %s: "),
- op_type_to_string (op_type), flatpak_ref_get_name (rref));
-
- text = g_strconcat (non_fatal ? _("Warning:") : _("Error:"), " ", msg, NULL);
+ {
+ /* Here we go to great lengths not to split the sentences. See
+ * https://wiki.gnome.org/TranslationProject/DevGuidelines/Never%20split%20sentences
+ */
+ switch (op_type)
+ {
+ case FLATPAK_TRANSACTION_OPERATION_INSTALL:
+ g_propagate_prefixed_error (&self->first_operation_error,
+ g_error_copy (error),
+ _("Failed to install %s%s%s: "),
+ on, flatpak_ref_get_name (rref), off);
+ break;
+
+ case FLATPAK_TRANSACTION_OPERATION_UPDATE:
+ g_propagate_prefixed_error (&self->first_operation_error,
+ g_error_copy (error),
+ _("Failed to update %s%s%s: "),
+ on, flatpak_ref_get_name (rref), off);
+ break;
+
+ case FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE:
+ g_propagate_prefixed_error (&self->first_operation_error,
+ g_error_copy (error),
+ _("Failed to install bundle %s%s%s: "),
+ on, flatpak_ref_get_name (rref), off);
+ break;
+
+ case FLATPAK_TRANSACTION_OPERATION_UNINSTALL:
+ g_propagate_prefixed_error (&self->first_operation_error,
+ g_error_copy (error),
+ _("Failed to uninstall %s%s%s: "),
+ on, flatpak_ref_get_name (rref), off);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+ }
if (flatpak_fancy_output ())
{
@@ -650,6 +701,175 @@ typedef enum {
EOL_REBASE, /* Choose to rebase */
} EolAction;
+static void
+print_eol_info_message (FlatpakDir *dir,
+ FlatpakDecomposed *ref,
+ const char *ref_name,
+ const char *rebased_to_ref,
+ const char *reason)
+{
+ gboolean is_pinned = flatpak_dir_ref_is_pinned (dir, flatpak_decomposed_get_ref (ref));
+ g_autofree char *ref_branch = flatpak_decomposed_dup_branch (ref);
+ const char *on = "";
+ const char *off = "";
+
+ if (flatpak_fancy_output ())
+ {
+ on = FLATPAK_ANSI_BOLD_ON;
+ off = FLATPAK_ANSI_BOLD_OFF;
+ }
+
+ /* Here we go to great lengths not to split the sentences. See
+ * https://wiki.gnome.org/TranslationProject/DevGuidelines/Never%20split%20sentences
+ */
+ if (rebased_to_ref)
+ {
+ g_autoptr(FlatpakDecomposed) eolr_decomposed = NULL;
+ g_autofree char *eolr_name = NULL;
+ const char *eolr_branch;
+
+ eolr_decomposed = flatpak_decomposed_new_from_ref (rebased_to_ref, NULL);
+
+ /* These are guarantees from FlatpakTransaction */
+ g_assert (eolr_decomposed != NULL);
+ g_assert (flatpak_decomposed_get_kind (ref) == flatpak_decomposed_get_kind (eolr_decomposed));
+
+ eolr_name = flatpak_decomposed_dup_id (eolr_decomposed);
+ eolr_branch = flatpak_decomposed_get_branch (eolr_decomposed);
+
+ if (is_pinned)
+ {
+ /* Only runtimes can be pinned */
+ g_print (_("\nInfo: (pinned) runtime %s%s%s branch %s%s%s is end-of-life, in favor of %s%s%s branch %s%s%s\n"),
+ on, ref_name, off, on, ref_branch, off, on, eolr_name, off, on, eolr_branch, off);
+ }
+ else
+ {
+ if (flatpak_decomposed_is_runtime (ref))
+ g_print (_("\nInfo: runtime %s%s%s branch %s%s%s is end-of-life, in favor of %s%s%s branch %s%s%s\n"),
+ on, ref_name, off, on, ref_branch, off, on, eolr_name, off, on, eolr_branch, off);
+ else
+ g_print (_("\nInfo: app %s%s%s branch %s%s%s is end-of-life, in favor of %s%s%s branch %s%s%s\n"),
+ on, ref_name, off, on, ref_branch, off, on, eolr_name, off, on, eolr_branch, off);
+ }
+ }
+ else if (reason)
+ {
+ if (is_pinned)
+ {
+ /* Only runtimes can be pinned */
+ g_print (_("\nInfo: (pinned) runtime %s%s%s branch %s%s%s is end-of-life, with reason:\n"),
+ on, ref_name, off, on, ref_branch, off);
+ }
+ else
+ {
+ if (flatpak_decomposed_is_runtime (ref))
+ g_print (_("\nInfo: runtime %s%s%s branch %s%s%s is end-of-life, with reason:\n"),
+ on, ref_name, off, on, ref_branch, off);
+ else
+ g_print (_("\nInfo: app %s%s%s branch %s%s%s is end-of-life, with reason:\n"),
+ on, ref_name, off, on, ref_branch, off);
+ }
+ g_print (" %s\n", reason);
+ }
+}
+
+static void
+check_current_transaction_for_dependent_apps (GPtrArray *apps,
+ FlatpakTransaction *transaction,
+ FlatpakDecomposed *ref)
+{
+ g_autoptr(FlatpakTransactionOperation) ref_op = NULL;
+ GPtrArray *related_ops;
+
+ ref_op = flatpak_transaction_get_operation_for_ref (transaction, NULL, flatpak_decomposed_get_ref (ref), NULL);
+ g_assert (ref_op != NULL);
+
+ /* Get the related ops to find any apps that use @ref as a runtime or extension */
+ related_ops = flatpak_transaction_operation_get_related_to_ops (ref_op);
+ if (related_ops == NULL)
+ return;
+
+ for (int i = 0; i < related_ops->len; i++)
+ {
+ FlatpakTransactionOperation *related_op = g_ptr_array_index (related_ops, i);
+ const char *related_op_ref = flatpak_transaction_operation_get_ref (related_op);
+ g_autoptr(FlatpakDecomposed) related_op_decomposed = flatpak_decomposed_new_from_ref (related_op_ref, NULL);
+
+ if (related_op_decomposed == NULL)
+ continue;
+ if (flatpak_decomposed_id_is_subref (related_op_decomposed))
+ continue;
+
+ /* Recurse in case @ref was a runtime extension. We need to check since a
+ * runtime can have a runtime extension in its related ops in the
+ * extra-data case, so if we recurse unconditionally it could be infinite
+ * recursion.
+ */
+ if (flatpak_decomposed_is_runtime (related_op_decomposed))
+ {
+ GKeyFile *metadata = flatpak_transaction_operation_get_metadata (ref_op);
+ if (g_key_file_has_group (metadata, FLATPAK_METADATA_GROUP_EXTENSION_OF))
+ check_current_transaction_for_dependent_apps (apps, transaction, related_op_decomposed);
+ }
+ else if (!g_ptr_array_find_with_equal_func (apps, related_op_decomposed, (GEqualFunc)flatpak_decomposed_equal, NULL))
+ g_ptr_array_add (apps, g_steal_pointer (&related_op_decomposed));
+ }
+}
+
+static GPtrArray *
+find_reverse_dep_apps (FlatpakTransaction *transaction,
+ FlatpakDir *dir,
+ FlatpakDecomposed *ref,
+ gboolean *out_is_extension)
+{
+ FlatpakCliTransaction *self = FLATPAK_CLI_TRANSACTION (transaction);
+ g_autoptr(GPtrArray) apps = NULL;
+ g_autoptr(GError) local_error = NULL;
+
+ g_assert (out_is_extension);
+
+ *out_is_extension = flatpak_dir_is_runtime_extension (dir, ref);
+ if (*out_is_extension)
+ {
+ /* Find apps which are using the ref as an extension directly or as an
+ * extension of their runtime.
+ */
+ apps = flatpak_dir_list_app_refs_with_runtime_extension (dir,
+ &self->runtime_app_map,
+ &self->extension_app_map,
+ ref, NULL, &local_error);
+ if (apps == NULL)
+ {
+ g_info ("Unable to list apps using extension %s: %s\n",
+ flatpak_decomposed_get_ref (ref), local_error->message);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* Find any apps using the runtime directly */
+ apps = flatpak_dir_list_app_refs_with_runtime (dir, &self->runtime_app_map, ref,
+ NULL, &local_error);
+ if (apps == NULL)
+ {
+ g_info ("Unable to find apps using runtime %s: %s\n",
+ flatpak_decomposed_get_ref (ref), local_error->message);
+ return NULL;
+ }
+ }
+
+ /* Also check the current transaction since it's possible the EOL ref
+ * and/or any app(s) that depend on it are not installed. It's also
+ * possible the current transaction updates one of the apps to a
+ * newer runtime but we don't handle that yet
+ * (https://github.com/flatpak/flatpak/issues/4832)
+ */
+ check_current_transaction_for_dependent_apps (apps, transaction, ref);
+
+ return g_steal_pointer (&apps);
+}
+
static gboolean
end_of_lifed_with_rebase (FlatpakTransaction *transaction,
const char *remote,
@@ -709,32 +929,24 @@ end_of_lifed_with_rebase (FlatpakTransaction *transaction,
if (action == EOL_UNDECIDED)
{
- gboolean is_pinned = flatpak_dir_ref_is_pinned (dir, flatpak_decomposed_get_ref (ref));
- g_autofree char *branch = flatpak_decomposed_dup_branch (ref);
action = EOL_IGNORE;
- if (rebased_to_ref)
- if (is_pinned)
- g_print (_("Info: (pinned) %s//%s is end-of-life, in favor of %s\n"), name, branch, rebased_to_ref);
- else
- g_print (_("Info: %s//%s is end-of-life, in favor of %s\n"), name, branch, rebased_to_ref);
- else if (reason)
- {
- if (is_pinned)
- g_print (_("Info: (pinned) %s//%s is end-of-life, with reason:\n"), name, branch);
- else
- g_print (_("Info: %s//%s is end-of-life, with reason:\n"), name, branch);
- g_print (" %s\n", reason);
- }
+ print_eol_info_message (dir, ref, name, rebased_to_ref, reason);
- if (flatpak_decomposed_is_runtime (ref))
+ if (flatpak_decomposed_is_runtime (ref) && !rebased_to_ref)
{
- g_autoptr(GPtrArray) apps = flatpak_dir_list_app_refs_with_runtime (dir, ref, NULL, NULL);
+ gboolean is_extension;
+ g_autoptr(GPtrArray) apps = find_reverse_dep_apps (transaction, dir, ref, &is_extension);
+
if (apps && apps->len > 0)
{
- g_print (_("Applications using this runtime:\n"));
+ if (is_extension)
+ g_print (_("Info: applications using this extension:\n"));
+ else
+ g_print (_("Info: applications using this runtime:\n"));
+
g_print (" ");
- for (int i = 0; i < apps->len; i++)
+ for (guint i = 0; i < apps->len; i++)
{
FlatpakDecomposed *app_ref = g_ptr_array_index (apps, i);
g_autofree char *id = flatpak_decomposed_dup_id (app_ref);
@@ -748,8 +960,9 @@ end_of_lifed_with_rebase (FlatpakTransaction *transaction,
if (rebased_to_ref && remote)
{
+ /* The context for this prompt is in print_eol_info_message() */
if (self->disable_interaction ||
- flatpak_yes_no_prompt (TRUE, _("Replace it with %s?"), rebased_to_ref))
+ flatpak_yes_no_prompt (TRUE, _("Replace?")))
{
if (self->disable_interaction)
g_print (_("Updating to rebased version\n"));
@@ -762,7 +975,7 @@ end_of_lifed_with_rebase (FlatpakTransaction *transaction,
}
else
{
- g_debug ("%s is end-of-life, using action from parent ren", name);
+ g_info ("%s is end-of-life, using action from parent ref", name);
}
/* Cache for later comparison and reuse */
@@ -951,6 +1164,14 @@ print_permissions (FlatpakCliTransaction *self,
int i, j;
int rows, cols;
int table_rows, table_cols;
+ const char *on = "";
+ const char *off = "";
+
+ if (flatpak_fancy_output ())
+ {
+ on = FLATPAK_ANSI_BOLD_ON;
+ off = FLATPAK_ANSI_BOLD_OFF;
+ }
if (metadata == NULL)
return;
@@ -991,9 +1212,9 @@ print_permissions (FlatpakCliTransaction *self,
g_print ("\n");
if (old_metadata)
- g_print (_("New %s permissions:"), flatpak_ref_get_name (rref));
+ g_print (_("New %s%s%s permissions:"), on, flatpak_ref_get_name (rref), off);
else
- g_print (_("%s permissions:"), flatpak_ref_get_name (rref));
+ g_print (_("%s%s%s permissions:"), on, flatpak_ref_get_name (rref), off);
g_print ("\n");
@@ -1079,6 +1300,10 @@ transaction_ready_pre_auth (FlatpakTransaction *transaction)
FlatpakTablePrinter *printer;
const char *op_shorthand[] = { "i", "u", "i", "r" };
+ /* These caches may no longer be valid once the transaction runs */
+ g_clear_pointer (&self->runtime_app_map, g_hash_table_unref);
+ g_clear_pointer (&self->extension_app_map, g_hash_table_unref);
+
if (ops == NULL)
return TRUE;
@@ -1323,6 +1548,12 @@ flatpak_cli_transaction_finalize (GObject *object)
g_hash_table_unref (self->eol_actions);
+ if (self->runtime_app_map)
+ g_hash_table_unref (self->runtime_app_map);
+
+ if (self->extension_app_map)
+ g_hash_table_unref (self->extension_app_map);
+
if (self->printer)
flatpak_table_printer_free (self->printer);