From fbfe6851fa6fa48bca39acbe68d2d65993092d8b Mon Sep 17 00:00:00 2001 From: Eva M Date: Thu, 16 Apr 2026 22:01:34 -0700 Subject: [PATCH 1/4] add bundle install support --- src/bz-application.c | 38 ++-- src/bz-backend-notification.txt | 2 +- src/bz-entry-group.c | 16 +- src/bz-flatpak-entry.c | 41 ++++- src/bz-flatpak-entry.h | 3 + src/bz-flatpak-instance.c | 298 +++++++++++++++++++++++++------- src/bz-full-view.blp | 10 +- src/bz-window.c | 8 +- 8 files changed, 320 insertions(+), 96 deletions(-) diff --git a/src/bz-application.c b/src/bz-application.c index a6039574..0feba7ab 100644 --- a/src/bz-application.c +++ b/src/bz-application.c @@ -1371,6 +1371,23 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) update_labels = TRUE; } break; + case BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES: + { + g_autoptr (GListModel) repos = NULL; + + repos = dex_await_object ( + bz_backend_list_repositories (BZ_BACKEND (self->flatpak), NULL), + &local_error); + + if (repos != NULL) + bz_state_info_set_repositories (self->state, repos); + else + { + g_warning ("Failed to enumerate repositories: %s", local_error->message); + g_clear_error (&local_error); + } + } + break; case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START: { const char *remote_name = NULL; @@ -1499,11 +1516,12 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) } break; case BZ_BACKEND_NOTIFICATION_KIND_ERROR: - case BZ_BACKEND_NOTIFICATION_KIND_TELL_INCOMING: - case BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY: - case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START: - case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH: case BZ_BACKEND_NOTIFICATION_KIND_EXTERNAL_CHANGE: + case BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES: + case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH: + case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START: + case BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY: + case BZ_BACKEND_NOTIFICATION_KIND_TELL_INCOMING: default: g_assert_not_reached (); }; @@ -1735,17 +1753,7 @@ open_flatpakref_fiber (OpenFlatpakrefData *data) value = dex_future_get_value (future, &local_error); if (value != NULL) - { - if (G_VALUE_HOLDS_OBJECT (value)) - { - BzEntry *entry = NULL; - - entry = g_value_get_object (value); - bz_window_show_entry (BZ_WINDOW (window), entry); - } - else - open_generic_id (self, g_value_get_string (value)); - } + open_generic_id (self, g_value_get_string (value)); else bz_show_error_for_widget (GTK_WIDGET (window), _ ("Failed to open .flatpakref"), local_error->message); diff --git a/src/bz-backend-notification.txt b/src/bz-backend-notification.txt index 3816118f..038dc6ed 100644 --- a/src/bz-backend-notification.txt +++ b/src/bz-backend-notification.txt @@ -4,7 +4,7 @@ parent-prefix=g parent-name=object author=AUTOGEN -enum=bz backend_notification_kind error tell_incoming replace_entry remote_sync_start remote_sync_finish install_done update_done remove_done external_change +enum=bz backend_notification_kind error tell_incoming replace_entry invalidate_remotes remote_sync_start remote_sync_finish install_done update_done remove_done external_change include="bz-entry.h" diff --git a/src/bz-entry-group.c b/src/bz-entry-group.c index f8fe3815..f17a74a9 100644 --- a/src/bz-entry-group.c +++ b/src/bz-entry-group.c @@ -934,11 +934,11 @@ bz_entry_group_add (BzEntryGroup *self, } } - title = bz_entry_get_title (entry); - description = bz_entry_get_description (entry); - installed_size = bz_entry_get_installed_size (entry); - is_flathub = bz_entry_get_is_flathub (entry); - is_floss = bz_entry_get_is_foss (entry); + title = bz_entry_get_title (entry); + description = bz_entry_get_description (entry); + installed_size = bz_entry_get_installed_size (entry); + is_flathub = bz_entry_get_is_flathub (entry); + is_floss = bz_entry_get_is_foss (entry); if (is_addon) // You would not see any addon when the filter is on without this. is_verified = TRUE; @@ -970,8 +970,8 @@ bz_entry_group_add (BzEntryGroup *self, gtk_string_list_remove (self->unique_ids, existing); gtk_string_list_remove (self->installed_versions, existing); } - gtk_string_list_splice (self->unique_ids, 0, 0, (const char *const[]) { unique_id, NULL }); - gtk_string_list_splice (self->installed_versions, 0, 0, (const char *const[]) { installed_version != NULL ? installed_version : "", NULL }); + gtk_string_list_splice (self->unique_ids, 0, 0, (const char *const[]){ unique_id, NULL }); + gtk_string_list_splice (self->installed_versions, 0, 0, (const char *const[]){ installed_version != NULL ? installed_version : "", NULL }); if (title != NULL) { @@ -1211,7 +1211,7 @@ installed_changed (BzEntryGroup *self, if (index != G_MAXUINT) { gtk_string_list_splice (self->installed_versions, index, 1, - (const char *const[]) { + (const char *const[]){ version != NULL ? version : "", NULL }); } diff --git a/src/bz-flatpak-entry.c b/src/bz-flatpak-entry.c index 38767140..8b465234 100644 --- a/src/bz-flatpak-entry.c +++ b/src/bz-flatpak-entry.c @@ -53,6 +53,7 @@ struct _BzFlatpakEntry char *application_command; char *runtime_name; char *addon_extension_of_ref; + char *bundle_path; BzResult *runtime_result; FlatpakRef *ref; @@ -74,6 +75,7 @@ enum PROP_USER, PROP_FLATPAK_NAME, PROP_IS_BUNDLE, + PROP_BUNDLE_PATH, PROP_FLATPAK_ID, PROP_FLATPAK_VERSION, PROP_APPLICATION_NAME, @@ -129,6 +131,9 @@ bz_flatpak_entry_get_property (GObject *object, case PROP_IS_BUNDLE: g_value_set_boolean (value, self->is_bundle); break; + case PROP_BUNDLE_PATH: + g_value_set_string (value, self->bundle_path); + break; case PROP_APPLICATION_RUNTIME: g_value_set_string (value, self->application_runtime); break; @@ -169,6 +174,7 @@ bz_flatpak_entry_set_property (GObject *object, case PROP_APPLICATION_COMMAND: case PROP_RUNTIME_NAME: case PROP_ADDON_OF_REF: + case PROP_BUNDLE_PATH: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -221,6 +227,12 @@ bz_flatpak_entry_class_init (BzFlatpakEntryClass *klass) FALSE, G_PARAM_READABLE); + props[PROP_BUNDLE_PATH] = + g_param_spec_string ( + "bundle-path", + NULL, NULL, NULL, + G_PARAM_READABLE); + props[PROP_APPLICATION_RUNTIME] = g_param_spec_string ( "application-runtime", @@ -267,7 +279,10 @@ bz_flatpak_entry_real_serialize (BzSerializable *serializable, BzFlatpakEntry *self = BZ_FLATPAK_ENTRY (serializable); g_variant_builder_add (builder, "{sv}", "user", g_variant_new_boolean (self->user)); + g_variant_builder_add (builder, "{sv}", "is-bundle", g_variant_new_boolean (self->is_bundle)); g_variant_builder_add (builder, "{sv}", "is-installed-ref", g_variant_new_boolean (self->is_installed_ref)); + if (self->bundle_path != NULL) + g_variant_builder_add (builder, "{sv}", "bundle-path", g_variant_new_string (self->bundle_path)); if (self->flatpak_name != NULL) g_variant_builder_add (builder, "{sv}", "flatpak-name", g_variant_new_string (self->flatpak_name)); if (self->flatpak_id != NULL) @@ -309,8 +324,12 @@ bz_flatpak_entry_real_deserialize (BzSerializable *serializable, if (g_strcmp0 (key, "user") == 0) self->user = g_variant_get_boolean (value); + else if (g_strcmp0 (key, "is-bundle") == 0) + self->is_bundle = g_variant_get_boolean (value); else if (g_strcmp0 (key, "is-installed-ref") == 0) self->is_installed_ref = g_variant_get_boolean (value); + else if (g_strcmp0 (key, "bundle-path") == 0) + self->bundle_path = g_variant_dup_string (value, NULL); else if (g_strcmp0 (key, "flatpak-name") == 0) self->flatpak_name = g_variant_dup_string (value, NULL); else if (g_strcmp0 (key, "flatpak-id") == 0) @@ -406,6 +425,15 @@ bz_flatpak_entry_new_for_ref (FlatpakRef *ref, self->is_installed_ref = FLATPAK_IS_INSTALLED_REF (ref); self->ref = g_object_ref (ref); + if (FLATPAK_IS_BUNDLE_REF (ref)) + { + GFile *file = NULL; + + file = flatpak_bundle_ref_get_file (FLATPAK_BUNDLE_REF (ref)); + if (file != NULL) + self->bundle_path = g_file_get_path (file); + } + key_file = g_key_file_new (); if (FLATPAK_IS_REMOTE_REF (ref)) bytes = flatpak_remote_ref_get_metadata (FLATPAK_REMOTE_REF (ref)); @@ -663,7 +691,8 @@ bz_flatpak_ref_parts_format_unique (const char *origin, return g_strdup_printf ( "FLATPAK-%s::%s::%s", user ? "USER" : "SYSTEM", - origin, fmt); + origin != NULL ? origin : "bundle", + fmt); } char * @@ -677,8 +706,6 @@ bz_flatpak_ref_format_unique (FlatpakRef *ref, if (FLATPAK_IS_REMOTE_REF (ref)) origin = flatpak_remote_ref_get_remote_name (FLATPAK_REMOTE_REF (ref)); - else if (FLATPAK_IS_BUNDLE_REF (ref)) - origin = flatpak_bundle_ref_get_origin (FLATPAK_BUNDLE_REF (ref)); else if (FLATPAK_IS_INSTALLED_REF (ref)) origin = flatpak_installed_ref_get_origin (FLATPAK_INSTALLED_REF (ref)); @@ -780,6 +807,13 @@ bz_flatpak_entry_is_installed_ref (BzFlatpakEntry *self) return self->is_installed_ref; } +const char * +bz_flatpak_entry_get_bundle_path (BzFlatpakEntry *self) +{ + g_return_val_if_fail (BZ_IS_FLATPAK_ENTRY (self), NULL); + return self->bundle_path; +} + gboolean bz_flatpak_entry_launch (BzFlatpakEntry *self, BzFlatpakInstance *flatpak, @@ -835,5 +869,6 @@ clear_entry (BzFlatpakEntry *self) g_clear_pointer (&self->application_command, g_free); g_clear_pointer (&self->runtime_name, g_free); g_clear_pointer (&self->addon_extension_of_ref, g_free); + g_clear_pointer (&self->bundle_path, g_free); g_clear_object (&self->runtime_result); } diff --git a/src/bz-flatpak-entry.h b/src/bz-flatpak-entry.h index 5c85e1c2..41aacd48 100644 --- a/src/bz-flatpak-entry.h +++ b/src/bz-flatpak-entry.h @@ -63,6 +63,9 @@ bz_flatpak_entry_is_bundle (BzFlatpakEntry *self); gboolean bz_flatpak_entry_is_installed_ref (BzFlatpakEntry *self); +const char * +bz_flatpak_entry_get_bundle_path (BzFlatpakEntry *self); + const char * bz_flatpak_entry_get_addon_extension_of_ref (BzFlatpakEntry *self); diff --git a/src/bz-flatpak-instance.c b/src/bz-flatpak-instance.c index d4fc7fb8..fa6010e6 100644 --- a/src/bz-flatpak-instance.c +++ b/src/bz-flatpak-instance.c @@ -169,11 +169,19 @@ BZ_DEFINE_DATA ( static DexFuture * retrieve_refs_for_remote_fiber (RetrieveRefsForRemoteData *data); -static void -gather_refs_update_progress (const char *status, - guint progress, - gboolean estimating, - GatherRefsData *data); +static DexFuture * +retrieve_refs_for_enumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote); + +static DexFuture * +retrieve_refs_for_noenumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote); BZ_DEFINE_DATA ( transaction, @@ -949,11 +957,15 @@ ensure_flathub_fiber (EnsureFlathubData *data) static DexFuture * load_local_ref_fiber (LoadLocalRefData *data) { - GFile *file = data->file; - gboolean result = FALSE; - g_autoptr (GError) local_error = NULL; - g_autofree char *uri = NULL; - g_autofree char *path = NULL; + g_autoptr (BzFlatpakInstance) self = NULL; + GFile *file = data->file; + GCancellable *cancellable = data->cancellable; + gboolean result = FALSE; + g_autoptr (GError) local_error = NULL; + g_autofree char *uri = NULL; + g_autofree char *path = NULL; + + bz_weak_get_or_return_reject (self, data->self); uri = g_file_get_uri (file); path = g_file_get_path (file); @@ -1022,11 +1034,15 @@ load_local_ref_fiber (LoadLocalRefData *data) else /* This is a bundle ref */ { - g_autoptr (FlatpakBundleRef) bref = NULL; - g_autoptr (BzFlatpakEntry) entry = NULL; - g_autoptr (GBytes) appstream_gz = NULL; - g_autoptr (GBytes) appstream = NULL; - g_autoptr (AsComponent) component = NULL; + g_autoptr (FlatpakBundleRef) bref = NULL; + const char *name = NULL; + const char *origin = NULL; + FlatpakInstallation *add_to_installation = NULL; + g_autoptr (FlatpakRemote) remote = NULL; + g_autoptr (BzFlatpakEntry) entry = NULL; + g_autoptr (GBytes) appstream_gz = NULL; + g_autoptr (GBytes) appstream = NULL; + g_autoptr (AsComponent) component = NULL; if (path == NULL) return dex_future_new_reject ( @@ -1044,6 +1060,126 @@ load_local_ref_fiber (LoadLocalRefData *data) path, local_error->message); + name = flatpak_ref_get_name (FLATPAK_REF (bref)); + origin = flatpak_bundle_ref_get_origin (bref); + + if (self->system != NULL) + add_to_installation = self->system; + else if (self->user != NULL) + add_to_installation = self->user; + + /* First check if we already should have the origin remote + installed */ + if (self->system != NULL) + { + g_autoptr (GPtrArray) remotes = NULL; + + remotes = flatpak_installation_list_remotes ( + self->system, NULL, NULL); + if (remotes != NULL) + { + for (guint i = 0; i < remotes->len; i++) + { + FlatpakRemote *existing = NULL; + const char *url = NULL; + + existing = g_ptr_array_index (remotes, i); + url = flatpak_remote_get_url (existing); + + if (url != NULL && + g_strcmp0 (url, origin) == 0) + { + remote = g_object_ref (existing); + add_to_installation = NULL; + break; + } + } + } + } + if (self->user != NULL) + { + g_autoptr (GPtrArray) remotes = NULL; + + remotes = flatpak_installation_list_remotes ( + self->user, NULL, NULL); + if (remotes != NULL) + { + for (guint i = 0; i < remotes->len; i++) + { + FlatpakRemote *existing = NULL; + const char *url = NULL; + + existing = g_ptr_array_index (remotes, i); + url = flatpak_remote_get_url (existing); + + if (url != NULL && + g_strcmp0 (url, origin) == 0) + { + remote = g_object_ref (existing); + add_to_installation = NULL; + break; + } + } + } + } + + if (add_to_installation != NULL) + { + g_autoptr (FlatpakRemote) config_remote = NULL; + g_autofree char *remote_name = NULL; + + /* Configure and sync the new remote */ + remote_name = g_strdup_printf ("%s-bazaar-origin", name); + + config_remote = flatpak_remote_new (remote_name); + flatpak_remote_set_url (config_remote, origin); + flatpak_remote_set_disabled (config_remote, FALSE); + flatpak_remote_set_noenumerate (config_remote, FALSE); + flatpak_remote_set_gpg_verify (config_remote, TRUE); + + result = flatpak_installation_add_remote ( + add_to_installation, config_remote, FALSE, cancellable, NULL); + if (result) + { + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES); + + send_notif_all (self, notif, TRUE); + } + + remote = flatpak_installation_get_remote_by_name ( + add_to_installation, remote_name, cancellable, NULL); + if (remote != NULL) + { + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START); + bz_backend_notification_set_remote_name (notif, remote_name); + + send_notif_all (self, notif, TRUE); + } + result = dex_await ( + retrieve_refs_for_enumerable_remote ( + self, cancellable, remote_name, add_to_installation, remote), + NULL); + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH); + bz_backend_notification_set_remote_name (notif, remote_name); + + send_notif_all (self, notif, TRUE); + } + } + } + } + appstream_gz = flatpak_bundle_ref_get_appstream (bref); if (appstream_gz != NULL) { @@ -1090,7 +1226,7 @@ load_local_ref_fiber (LoadLocalRefData *data) entry = bz_flatpak_entry_new_for_ref ( FLATPAK_REF (bref), - NULL, + remote, FALSE, component, NULL, @@ -1103,7 +1239,47 @@ load_local_ref_fiber (LoadLocalRefData *data) path, local_error->message); - return dex_future_new_for_object (entry); + { + g_autoptr (BzBackendNotification) notif = NULL; + g_autoptr (BzBackendNotification) inc_incoming = NULL; + g_autoptr (GMutexLocker) locker = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY); + bz_backend_notification_set_entry (notif, BZ_ENTRY (entry)); + + inc_incoming = bz_backend_notification_new (); + bz_backend_notification_set_kind (inc_incoming, BZ_BACKEND_NOTIFICATION_KIND_TELL_INCOMING); + bz_backend_notification_set_n_incoming (inc_incoming, 1); + + locker = g_mutex_locker_new (&self->notif_mutex); + for (guint i = 0; i < self->notif_channels->len;) + { + DexChannel *channel = NULL; + + channel = g_ptr_array_index (self->notif_channels, i); + if (dex_channel_can_send (channel)) + { + /* We need to ensure the notification has been completely + processed before returning the ID to avoid racing */ + dex_await ( + dex_channel_send ( + channel, + dex_future_new_for_object (inc_incoming)), + NULL); + dex_await ( + dex_channel_send ( + channel, + dex_future_new_for_object (notif)), + NULL); + i++; + } + else + g_ptr_array_remove_index_fast (self->notif_channels, i); + } + } + + return dex_future_new_for_string (name); } } @@ -1247,22 +1423,13 @@ retrieve_remote_refs_fiber (GatherRefsData *data) "%s", error_string->str); } -static void -gather_refs_update_progress (const char *status, - guint progress, - gboolean estimating, - GatherRefsData *data) -{ -} - static DexFuture * -retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, - const char *remote_name, - FlatpakInstallation *installation, - FlatpakRemote *remote) +retrieve_refs_for_enumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote) { - g_autoptr (BzFlatpakInstance) self = NULL; - GCancellable *cancellable = data->parent->cancellable; g_autoptr (GError) local_error = NULL; gboolean result = FALSE; g_autoptr (GFile) appstream_dir = NULL; @@ -1276,8 +1443,6 @@ retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, g_autoptr (GHashTable) component_hash = NULL; g_autoptr (GPtrArray) refs = NULL; - bz_weak_get_or_return_reject (self, data->parent->self); - g_debug ("Remote '%s' is enumerable, listing all remote refs", remote_name); result = flatpak_installation_update_remote_sync ( @@ -1296,10 +1461,7 @@ retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, result = flatpak_installation_update_appstream_full_sync ( installation, remote_name, - NULL, - (FlatpakProgressCallback) gather_refs_update_progress, - data, - NULL, + NULL, NULL, NULL, NULL, cancellable, &local_error); if (!result) @@ -1479,19 +1641,16 @@ retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, } static DexFuture * -retrieve_refs_for_noenumerable_remote (RetrieveRefsForRemoteData *data, - const char *remote_name, - FlatpakInstallation *installation, - FlatpakRemote *remote) +retrieve_refs_for_noenumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote) { - g_autoptr (BzFlatpakInstance) self = NULL; - GCancellable *cancellable = data->parent->cancellable; g_autoptr (GError) local_error = NULL; g_autoptr (GPtrArray) installed_apps = NULL; guint matched = 0; - bz_weak_get_or_return_reject (self, data->parent->self); - installed_apps = flatpak_installation_list_installed_refs_by_kind ( installation, FLATPAK_REF_KIND_APP, @@ -1628,10 +1787,12 @@ retrieve_refs_for_remote_fiber (RetrieveRefsForRemoteData *data) if (is_noenumerate) #endif ret = retrieve_refs_for_noenumerable_remote ( - data, remote_name, installation, remote); + self, data->parent->cancellable, + remote_name, installation, remote); else ret = retrieve_refs_for_enumerable_remote ( - data, remote_name, installation, remote); + self, data->parent->cancellable, + remote_name, installation, remote); { g_autoptr (BzBackendNotification) notif = NULL; @@ -1908,15 +2069,17 @@ transaction_fiber (TransactionData *data) for (guint i = 0; i < installations->len; i++) { BzFlatpakEntry *entry = NULL; + const char *bundle_path = NULL; FlatpakRef *ref = NULL; gboolean is_user = FALSE; g_autofree char *ref_fmt = NULL; g_autoptr (FlatpakTransaction) transaction = NULL; - entry = g_ptr_array_index (installations, i); - ref = bz_flatpak_entry_get_ref (entry); - is_user = bz_flatpak_entry_is_user (BZ_FLATPAK_ENTRY (entry)); - ref_fmt = flatpak_ref_format_ref (ref); + entry = g_ptr_array_index (installations, i); + bundle_path = bz_flatpak_entry_get_bundle_path (entry); + ref = bz_flatpak_entry_get_ref (entry); + is_user = bz_flatpak_entry_is_user (BZ_FLATPAK_ENTRY (entry)); + ref_fmt = flatpak_ref_format_ref (ref); if ((is_user && self->user == NULL) || (!is_user && self->system == NULL)) @@ -1945,12 +2108,25 @@ transaction_fiber (TransactionData *data) local_error->message); } - result = flatpak_transaction_add_install ( - transaction, - bz_entry_get_remote_repo_name (BZ_ENTRY (entry)), - ref_fmt, - NULL, - &local_error); + if (bundle_path != NULL) + /* Prioritize bundle installation */ + { + g_autoptr (GFile) file = NULL; + + file = g_file_new_for_path (bundle_path); + result = flatpak_transaction_add_install_bundle ( + transaction, + file, + NULL, + &local_error); + } + else + result = flatpak_transaction_add_install ( + transaction, + bz_entry_get_remote_repo_name (BZ_ENTRY (entry)), + ref_fmt, + NULL, + &local_error); if (!result) { dex_channel_close_send (channel); @@ -2436,9 +2612,11 @@ transaction_operation_done (FlatpakTransaction *transaction, g_assert_not_reached (); } - origin = flatpak_transaction_operation_get_remote (operation); - ref = flatpak_transaction_operation_get_ref (operation); - is_user = flatpak_transaction_get_installation (transaction) == self->user_interactive; + if (op_type != FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE) + origin = flatpak_transaction_operation_get_remote (operation); + ref = flatpak_transaction_operation_get_ref (operation); + is_user = flatpak_transaction_get_installation (transaction) == self->user_interactive; + unique_id = bz_flatpak_ref_parts_format_unique (origin, ref, is_user); if (notif_kind == BZ_BACKEND_NOTIFICATION_KIND_INSTALL_DONE || diff --git a/src/bz-full-view.blp b/src/bz-full-view.blp index c6ff0da5..18051fd3 100644 --- a/src/bz-full-view.blp +++ b/src/bz-full-view.blp @@ -118,11 +118,11 @@ template $BzFullView: Adw.Bin { margin-bottom: 15; spacing: 20; - Adw.Banner { - title: _("Installing .flatpak bundles is not yet supported"); - visible: bind template.ui-entry as <$BzResult>.object as <$BzFlatpakEntry>.is-bundle as ; - revealed: true; - } + // Adw.Banner { + // title: _("Installing .flatpak bundles is not yet supported"); + // visible: bind template.ui-entry as <$BzResult>.object as <$BzFlatpakEntry>.is-bundle as ; + // revealed: true; + // } Adw.Banner { title: _("This is a local preview, some details may differ from the published listing"); diff --git a/src/bz-window.c b/src/bz-window.c index bc2ace68..070b65a1 100644 --- a/src/bz-window.c +++ b/src/bz-window.c @@ -303,7 +303,7 @@ browse_flathub_cb (BzWindow *self, } static void -open_search_cb (BzWindow *self, +open_search_cb (BzWindow *self, BzSearchPage *widget) { adw_view_stack_set_visible_child_name (self->main_view_stack, "search"); @@ -443,7 +443,7 @@ action_show_group (GtkWidget *widget, { AdwDialog *dialog = NULL; - dialog =bz_addons_dialog_new_single (group); + dialog = bz_addons_dialog_new_single (group); adw_dialog_present (dialog, GTK_WIDGET (self)); } else @@ -458,7 +458,7 @@ action_addons_group (GtkWidget *widget, BzWindow *self = BZ_WINDOW (widget); const char *id = NULL; g_autoptr (BzEntryGroup) group = NULL; - AdwDialog *addons_dialog = NULL; + AdwDialog *addons_dialog = NULL; id = g_variant_get_string (parameter, NULL); group = bz_application_map_factory_convert_one ( @@ -929,7 +929,7 @@ bz_window_show_entry (BzWindow *self, g_return_if_fail (BZ_IS_ENTRY (entry)); group = bz_entry_group_new_for_single_entry (entry); - bz_window_show_group(self, group); + bz_window_show_group (self, group); } void From 00ce0bef401057724c76cc6e17e24baff2a6e6c3 Mon Sep 17 00:00:00 2001 From: Alexander Vanhee <160625516+AlexanderVanhee@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:37:06 +0200 Subject: [PATCH 2/4] Move bundle to cache folder before install --- src/bz-flatpak-instance.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/bz-flatpak-instance.c b/src/bz-flatpak-instance.c index fa6010e6..3000ba4a 100644 --- a/src/bz-flatpak-instance.c +++ b/src/bz-flatpak-instance.c @@ -2111,9 +2111,40 @@ transaction_fiber (TransactionData *data) if (bundle_path != NULL) /* Prioritize bundle installation */ { - g_autoptr (GFile) file = NULL; + g_autoptr (GFile) file = NULL; + g_autofree char *resolved_path = NULL; - file = g_file_new_for_path (bundle_path); + if (g_str_has_prefix (bundle_path, "/run/user/") && + strstr (bundle_path, "/doc/") != NULL) + { + g_autofree char *basename = NULL; + g_autofree char *staging = NULL; + g_autoptr (GFile) src = NULL; + g_autoptr (GFile) dst = NULL; + + basename = g_path_get_basename (bundle_path); + staging = g_build_filename (g_get_user_cache_dir (), "bundle-staging", NULL); + + g_mkdir_with_parents (staging, 0755); + + resolved_path = g_build_filename (staging, basename, NULL); + src = g_file_new_for_path (bundle_path); + dst = g_file_new_for_path (resolved_path); + + if (!g_file_copy (src, dst, + G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, + cancellable, NULL, NULL, &local_error)) + { + dex_channel_close_send (channel); + return dex_future_new_reject ( + BZ_FLATPAK_ERROR, + BZ_FLATPAK_ERROR_TRANSACTION_FAILURE, + "Failed to stage bundle '%s': %s", + bundle_path, local_error->message); + } + } + + file = g_file_new_for_path (resolved_path != NULL ? resolved_path : bundle_path); result = flatpak_transaction_add_install_bundle ( transaction, file, From 1d7cfc59656c6d60e8fe010b026b9a5c01d138a7 Mon Sep 17 00:00:00 2001 From: Eva M Date: Fri, 17 Apr 2026 09:00:15 -0700 Subject: [PATCH 3/4] worky? --- src/bz-application.c | 46 ++++++++--- src/bz-backend-notification.txt | 3 +- src/bz-flatpak-instance.c | 135 +++++++++++++++++--------------- 3 files changed, 109 insertions(+), 75 deletions(-) diff --git a/src/bz-application.c b/src/bz-application.c index 0feba7ab..8464f2a8 100644 --- a/src/bz-application.c +++ b/src/bz-application.c @@ -1145,7 +1145,17 @@ init_fiber (GWeakRef *wr) future = g_ptr_array_index (futures, i); value = dex_future_get_value (future, &local_error); if (value != NULL) - g_ptr_array_add (entries, g_value_dup_object (value)); + { + g_autoptr (BzEntry) entry = NULL; + + entry = g_value_dup_object (value); + if (BZ_IS_FLATPAK_ENTRY (entry) && + bz_flatpak_entry_get_bundle_path (BZ_FLATPAK_ENTRY (entry))) + /* refrain from restoring bundle entries */ + continue; + + g_ptr_array_add (entries, g_steal_pointer (&entry)); + } else { g_warning ("Unable to retrieve cached entry: %s", local_error->message); @@ -1320,6 +1330,17 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) kind = bz_backend_notification_get_kind (notif); switch (kind) { + case BZ_BACKEND_NOTIFICATION_KIND_PRESENT_ID: + { + const char *id = NULL; + + id = bz_backend_notification_get_generic_id (notif); + if (id == NULL) + break; + + open_generic_id (self, id); + } + break; case BZ_BACKEND_NOTIFICATION_KIND_ERROR: { const char *error = NULL; @@ -1518,6 +1539,7 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) case BZ_BACKEND_NOTIFICATION_KIND_ERROR: case BZ_BACKEND_NOTIFICATION_KIND_EXTERNAL_CHANGE: case BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES: + case BZ_BACKEND_NOTIFICATION_KIND_PRESENT_ID: case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH: case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START: case BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY: @@ -1738,7 +1760,6 @@ open_flatpakref_fiber (OpenFlatpakrefData *data) GFile *file = data->file; g_autoptr (GError) local_error = NULL; g_autoptr (DexFuture) future = NULL; - GtkWindow *window = NULL; const GValue *value = NULL; bz_weak_get_or_return_reject (self, data->self); @@ -1747,15 +1768,20 @@ open_flatpakref_fiber (OpenFlatpakrefData *data) future = bz_backend_load_local_package (BZ_BACKEND (self->flatpak), file, NULL); dex_await (dex_ref (future), NULL); - window = gtk_application_get_active_window (GTK_APPLICATION (self)); - if (window == NULL) - window = new_window (self); - value = dex_future_get_value (future, &local_error); - if (value != NULL) - open_generic_id (self, g_value_get_string (value)); - else - bz_show_error_for_widget (GTK_WIDGET (window), _ ("Failed to open .flatpakref"), local_error->message); + if (value == NULL) + { + GtkWindow *window = NULL; + + window = gtk_application_get_active_window (GTK_APPLICATION (self)); + if (window == NULL) + window = new_window (self); + + bz_show_error_for_widget ( + GTK_WIDGET (window), + _ ("Failed to open file"), + local_error->message); + } return dex_future_new_true (); } diff --git a/src/bz-backend-notification.txt b/src/bz-backend-notification.txt index 038dc6ed..50b32a77 100644 --- a/src/bz-backend-notification.txt +++ b/src/bz-backend-notification.txt @@ -4,7 +4,7 @@ parent-prefix=g parent-name=object author=AUTOGEN -enum=bz backend_notification_kind error tell_incoming replace_entry invalidate_remotes remote_sync_start remote_sync_finish install_done update_done remove_done external_change +enum=bz backend_notification_kind error tell_incoming replace_entry invalidate_remotes remote_sync_start remote_sync_finish install_done update_done remove_done external_change present_id include="bz-entry.h" @@ -14,4 +14,5 @@ property=n_incoming int G_TYPE_INT int property=entry BzEntry BZ_TYPE_ENTRY object property=version char G_TYPE_STRING string property=remote_name char G_TYPE_STRING string +property=generic_id char G_TYPE_STRING string property=unique_id char G_TYPE_STRING string diff --git a/src/bz-flatpak-instance.c b/src/bz-flatpak-instance.c index 3000ba4a..540f5d6a 100644 --- a/src/bz-flatpak-instance.c +++ b/src/bz-flatpak-instance.c @@ -1029,12 +1029,23 @@ load_local_ref_fiber (LoadLocalRefData *data) "Failed to load locate \"Name\" key in flatpakref '%s': %s", uri, local_error->message); + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_PRESENT_ID); + bz_backend_notification_set_generic_id (notif, name); + + send_notif_all (self, notif, TRUE); + } + return dex_future_new_take_string (g_steal_pointer (&name)); } else /* This is a bundle ref */ { g_autoptr (FlatpakBundleRef) bref = NULL; + g_autoptr (GFile) resolved_file = NULL; const char *name = NULL; const char *origin = NULL; FlatpakInstallation *add_to_installation = NULL; @@ -1051,7 +1062,46 @@ load_local_ref_fiber (LoadLocalRefData *data) "Cannot load '%s' as a flatpak bundle: URI is not a local file", uri); - bref = flatpak_bundle_ref_new (file, &local_error); +#ifdef SANDBOXED_LIBFLATPAK + { + g_autofree char *basename = NULL; + g_autofree char *module_dir = NULL; + g_autofree char *staging = NULL; + g_autofree char *dest = NULL; + g_autoptr (GSubprocess) subprocess = NULL; + + basename = g_path_get_basename (path); + module_dir = bz_dup_module_dir (); + staging = g_build_filename (module_dir, "bundle-staging", NULL); + g_mkdir_with_parents (staging, 0755); + dest = g_build_filename (staging, basename, NULL); + + subprocess = g_subprocess_new ( + G_SUBPROCESS_FLAGS_STDERR_PIPE, + &local_error, + "flatpak-spawn", + "--host", + "cp", + path, + dest, + NULL); + result = dex_await ( + dex_subprocess_wait_check (subprocess), + &local_error); + if (!result) + return dex_future_new_reject ( + BZ_FLATPAK_ERROR, + BZ_FLATPAK_ERROR_IO_MISBEHAVIOR, + "Failed to copy bundle from %s to %s : %s", + path, dest, local_error->message); + + resolved_file = g_file_new_for_path (dest); + } +#else + resolved_file = g_object_ref (file); +#endif + + bref = flatpak_bundle_ref_new (resolved_file, &local_error); if (bref == NULL) return dex_future_new_reject ( BZ_FLATPAK_ERROR, @@ -1240,43 +1290,31 @@ load_local_ref_fiber (LoadLocalRefData *data) local_error->message); { - g_autoptr (BzBackendNotification) notif = NULL; - g_autoptr (BzBackendNotification) inc_incoming = NULL; - g_autoptr (GMutexLocker) locker = NULL; + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_TELL_INCOMING); + bz_backend_notification_set_n_incoming (notif, 1); + + send_notif_all (self, notif, TRUE); + } + { + g_autoptr (BzBackendNotification) notif = NULL; notif = bz_backend_notification_new (); bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY); bz_backend_notification_set_entry (notif, BZ_ENTRY (entry)); - inc_incoming = bz_backend_notification_new (); - bz_backend_notification_set_kind (inc_incoming, BZ_BACKEND_NOTIFICATION_KIND_TELL_INCOMING); - bz_backend_notification_set_n_incoming (inc_incoming, 1); + send_notif_all (self, notif, TRUE); + } + { + g_autoptr (BzBackendNotification) notif = NULL; - locker = g_mutex_locker_new (&self->notif_mutex); - for (guint i = 0; i < self->notif_channels->len;) - { - DexChannel *channel = NULL; + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_PRESENT_ID); + bz_backend_notification_set_generic_id (notif, bz_entry_get_id (BZ_ENTRY (entry))); - channel = g_ptr_array_index (self->notif_channels, i); - if (dex_channel_can_send (channel)) - { - /* We need to ensure the notification has been completely - processed before returning the ID to avoid racing */ - dex_await ( - dex_channel_send ( - channel, - dex_future_new_for_object (inc_incoming)), - NULL); - dex_await ( - dex_channel_send ( - channel, - dex_future_new_for_object (notif)), - NULL); - i++; - } - else - g_ptr_array_remove_index_fast (self->notif_channels, i); - } + send_notif_all (self, notif, TRUE); } return dex_future_new_for_string (name); @@ -2111,40 +2149,9 @@ transaction_fiber (TransactionData *data) if (bundle_path != NULL) /* Prioritize bundle installation */ { - g_autoptr (GFile) file = NULL; - g_autofree char *resolved_path = NULL; - - if (g_str_has_prefix (bundle_path, "/run/user/") && - strstr (bundle_path, "/doc/") != NULL) - { - g_autofree char *basename = NULL; - g_autofree char *staging = NULL; - g_autoptr (GFile) src = NULL; - g_autoptr (GFile) dst = NULL; - - basename = g_path_get_basename (bundle_path); - staging = g_build_filename (g_get_user_cache_dir (), "bundle-staging", NULL); - - g_mkdir_with_parents (staging, 0755); - - resolved_path = g_build_filename (staging, basename, NULL); - src = g_file_new_for_path (bundle_path); - dst = g_file_new_for_path (resolved_path); - - if (!g_file_copy (src, dst, - G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, - cancellable, NULL, NULL, &local_error)) - { - dex_channel_close_send (channel); - return dex_future_new_reject ( - BZ_FLATPAK_ERROR, - BZ_FLATPAK_ERROR_TRANSACTION_FAILURE, - "Failed to stage bundle '%s': %s", - bundle_path, local_error->message); - } - } + g_autoptr (GFile) file = NULL; - file = g_file_new_for_path (resolved_path != NULL ? resolved_path : bundle_path); + file = g_file_new_for_path (bundle_path); result = flatpak_transaction_add_install_bundle ( transaction, file, From ee46e6b44bd7fe1c2dcb2b34dcd405311fbc61be Mon Sep 17 00:00:00 2001 From: Eva M Date: Fri, 17 Apr 2026 09:17:58 -0700 Subject: [PATCH 4/4] revert to using `g_file_copy` --- src/bz-flatpak-instance.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/bz-flatpak-instance.c b/src/bz-flatpak-instance.c index 540f5d6a..2da46400 100644 --- a/src/bz-flatpak-instance.c +++ b/src/bz-flatpak-instance.c @@ -1064,11 +1064,10 @@ load_local_ref_fiber (LoadLocalRefData *data) #ifdef SANDBOXED_LIBFLATPAK { - g_autofree char *basename = NULL; - g_autofree char *module_dir = NULL; - g_autofree char *staging = NULL; - g_autofree char *dest = NULL; - g_autoptr (GSubprocess) subprocess = NULL; + g_autofree char *basename = NULL; + g_autofree char *module_dir = NULL; + g_autofree char *staging = NULL; + g_autofree char *dest = NULL; basename = g_path_get_basename (path); module_dir = bz_dup_module_dir (); @@ -1076,17 +1075,12 @@ load_local_ref_fiber (LoadLocalRefData *data) g_mkdir_with_parents (staging, 0755); dest = g_build_filename (staging, basename, NULL); - subprocess = g_subprocess_new ( - G_SUBPROCESS_FLAGS_STDERR_PIPE, - &local_error, - "flatpak-spawn", - "--host", - "cp", - path, - dest, - NULL); - result = dex_await ( - dex_subprocess_wait_check (subprocess), + resolved_file = g_file_new_for_path (dest); + + result = g_file_copy ( + file, resolved_file, + G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, + NULL, NULL, NULL, &local_error); if (!result) return dex_future_new_reject ( @@ -1094,8 +1088,6 @@ load_local_ref_fiber (LoadLocalRefData *data) BZ_FLATPAK_ERROR_IO_MISBEHAVIOR, "Failed to copy bundle from %s to %s : %s", path, dest, local_error->message); - - resolved_file = g_file_new_for_path (dest); } #else resolved_file = g_object_ref (file);