From 61f9d19eaeea4e92eeac930a9a14fc367053064c Mon Sep 17 00:00:00 2001 From: Umang Jain Date: Thu, 21 Nov 2019 12:18:55 +0530 Subject: [PATCH] installation: Return refs as updatable if related extensions are missing While updating, if the related extension is missing on the installation of an installed ref (could be an app or runtime), FlatpakTransaction tends to "repair" the ref by automatically downloading the related extension again and restoring the overall functionality of the ref. The related extension concerned that are the ones associated with `should-download` to TRUE only. Hence, teach the libflatpak API to do that same, so that clients like gnome-software can mark those refs as updatable, if their related extensions is missing. --- common/flatpak-dir-private.h | 4 ++ common/flatpak-dir.c | 35 +++++++++++++++++ common/flatpak-installation.c | 29 +++++++++++++- tests/testlibrary.c | 71 +++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h index a54bb762..0f39f8c4 100644 --- a/common/flatpak-dir-private.h +++ b/common/flatpak-dir-private.h @@ -902,6 +902,10 @@ GPtrArray * flatpak_dir_find_remote_related_for_metadata (FlatpakDir *se GKeyFile *metakey, GCancellable *cancellable, GError **error); +gboolean flatpak_dir_check_installed_ref_missing_related_ref (FlatpakDir *self, + FlatpakRemoteState *state, + const gchar *full_ref, + GCancellable *cancellable); GPtrArray * flatpak_dir_find_remote_related (FlatpakDir *dir, FlatpakRemoteState *state, const char *ref, diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index f1cc0268..c577b428 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -14034,6 +14034,41 @@ flatpak_dir_find_remote_related_for_metadata (FlatpakDir *self, return g_steal_pointer (&related); } +gboolean +flatpak_dir_check_installed_ref_missing_related_ref (FlatpakDir *self, + FlatpakRemoteState *state, + const gchar *full_ref, + GCancellable *cancellable) +{ + g_autoptr(GPtrArray) remote_related_refs = NULL; + g_autoptr(GError) local_error = NULL; + guint j; + + remote_related_refs = flatpak_dir_find_remote_related (self, state, full_ref, + cancellable, &local_error); + if (remote_related_refs == NULL) + { + g_warning ("Unable to get remote related refs for %s: %s", full_ref, local_error->message); + return FALSE; + } + + for (j = 0; j < remote_related_refs->len; j++) + { + FlatpakRelated *rel = g_ptr_array_index (remote_related_refs, j); + g_autoptr(GFile) deploy = NULL; + + if (!rel->download || flatpak_dir_ref_is_masked (self, rel->ref)) + continue; + + deploy = flatpak_dir_get_if_deployed (self, rel->ref, NULL, cancellable); + /* If the related extension ref was meant to be auto-installed but was not found to be + * deployed, return TRUE. It will be pulled in via a FlatpakTransaction's update-op again. */ + if (rel->download && deploy == NULL) + return TRUE; + } + + return FALSE; +} GPtrArray * flatpak_dir_find_remote_related (FlatpakDir *self, diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c index d6935b1d..b3136c8a 100644 --- a/common/flatpak-installation.c +++ b/common/flatpak-installation.c @@ -1002,6 +1002,11 @@ _ostree_collection_ref_free0 (OstreeCollectionRef *ref) * it can have local updates available that has not been deployed. Look * at commit vs latest_commit on installed apps for this. * + * This also checks if any of #FlatpakInstalledRef has a missing #FlatpakRelatedRef + * (which has `should-download` set to %TRUE). If so, it adds the ref to the + * returning #GPtrArray to pull in the #FlatpakRelatedRef again via an update + * operation in #FlatpakTransaction. + * * Returns: (transfer container) (element-type FlatpakInstalledRef): a GPtrArray of * #FlatpakInstalledRef instances, or %NULL on error */ @@ -1079,6 +1084,7 @@ flatpak_installation_list_installed_refs_for_update (FlatpakInstallation *self, for (i = 0; i < installed->len; i++) { + g_autoptr(FlatpakRemoteState) state = NULL; FlatpakInstalledRef *installed_ref = g_ptr_array_index (installed, i); const char *remote_name = flatpak_installed_ref_get_origin (installed_ref); g_autofree char *full_ref = flatpak_ref_format_ref (FLATPAK_REF (installed_ref)); @@ -1092,7 +1098,28 @@ flatpak_installation_list_installed_refs_for_update (FlatpakInstallation *self, /* Note: local_commit may be NULL here */ if (remote_commit != NULL && g_strcmp0 (remote_commit, local_commit) != 0) - g_ptr_array_add (updates, g_object_ref (installed_ref)); + { + g_ptr_array_add (updates, g_object_ref (installed_ref)); + + /* Don't check further, as we already added the installed_ref to @updates. */ + continue; + } + + /* Check if all "should-download" related refs for the ref are installed. + * If not, add the ref in @updates array so that it can be installed via + * FlatpakTransaction's update-op. + * + * This makes sure that the ref (maybe an app or runtime) remains in usable + * state and fixes itself through an update. + */ + state = flatpak_dir_get_remote_state_optional (dir, remote_name, FALSE, cancellable, error); + if (state == NULL) + continue; + + if (flatpak_dir_check_installed_ref_missing_related_ref (dir, state, full_ref, cancellable)) + { + g_ptr_array_add (updates, g_object_ref (installed_ref)); + } } collection_refs = g_ptr_array_new_with_free_func ((GDestroyNotify) _ostree_collection_ref_free0); diff --git a/tests/testlibrary.c b/tests/testlibrary.c index 3d503290..a0989886 100644 --- a/tests/testlibrary.c +++ b/tests/testlibrary.c @@ -1073,6 +1073,76 @@ test_list_remote_refs (void) } } +static void +test_update_related_refs (void) +{ + g_autoptr(FlatpakInstallation) inst = NULL; + g_autoptr(FlatpakTransaction) transaction = NULL; + g_autoptr(GPtrArray) updatable_refs = NULL; + g_autoptr(GPtrArray) related_refs = NULL; + g_autoptr(GError) error = NULL; + FlatpakInstalledRef *iref = NULL; + g_autoptr(FlatpakInstalledRef) runtime_ref = NULL; + gboolean res; + g_autofree char *app = NULL; + + app = g_strdup_printf ("app/org.test.Hello/%s/master", + flatpak_get_default_arch ()); + + inst = flatpak_installation_new_user (NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (inst); + + empty_installation (inst); + + /* Install a runtime */ + runtime_ref = flatpak_installation_install (inst, + repo_name, + FLATPAK_REF_KIND_RUNTIME, + "org.test.Platform", + NULL, "master", NULL, NULL, NULL, + &error); + g_assert_no_error (error); + g_assert (FLATPAK_IS_INSTALLED_REF (runtime_ref)); + + iref = flatpak_installation_install (inst, + repo_name, + FLATPAK_REF_KIND_APP, + "org.test.Hello", + NULL, "master", NULL, NULL, NULL, + &error); + g_assert_no_error (error); + g_assert (FLATPAK_IS_INSTALLED_REF (iref)); + iref = NULL; + + /* We expect no installed related refs (i.e. org.test.Hello.Locale) at this point */ + related_refs = flatpak_installation_list_installed_related_refs_sync (inst, repo_name, app, NULL, &error); + g_assert_cmpint (related_refs->len, ==, 0); + + updatable_refs = flatpak_installation_list_installed_refs_for_update (inst, NULL, &error); + g_assert_cmpint (updatable_refs->len, ==, 1); + iref = g_ptr_array_index (updatable_refs, 0); + g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (iref)), ==, "org.test.Hello"); + + /* Prepare an update transaction to update org.test.Hello. The missing related .Locale + extension should automatically be installed with it. */ + transaction = flatpak_transaction_new_for_installation (inst, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (transaction); + + res = flatpak_transaction_add_update (transaction, app, NULL, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + res = flatpak_transaction_run (transaction, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + iref = flatpak_installation_get_installed_ref (inst, FLATPAK_REF_KIND_RUNTIME, "org.test.Hello.Locale", NULL, NULL, NULL, &error); + g_assert_nonnull (iref); + g_assert_no_error (error); +} + static void test_list_remote_related_refs (void) { @@ -3947,6 +4017,7 @@ main (int argc, char *argv[]) g_test_add_func ("/library/install-bundle", test_install_bundle); g_test_add_func ("/library/install-flatpakref", test_install_flatpakref); g_test_add_func ("/library/list-installed-related-refs", test_list_installed_related_refs); + g_test_add_func ("/library/update-related-refs", test_update_related_refs); g_test_add_func ("/library/no-deploy", test_no_deploy); g_test_add_func ("/library/bad-remote-name", test_bad_remote_name); g_test_add_func ("/library/transaction-no-runtime", test_transaction_no_runtime);