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);