From 2c9dde7b621515f832d57866492a3dccbe67dc48 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 30 Aug 2024 11:32:32 +0200 Subject: [PATCH 01/44] Update to 47.rc --- gnome-software.spec | 5 ++++- sources | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 37f4d6c..d9c30f5 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,7 +25,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 47~beta +Version: 47~rc Release: 1%{?dist} Summary: A software center for GNOME @@ -286,6 +286,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Fri Aug 30 2024 Milan Crha - 47~rc-1 +- Update to 47.rc + * Fri Aug 02 2024 Milan Crha - 47~beta-1 - Update to 47.beta - Build with DKMS/akmods plugin in Fedora diff --git a/sources b/sources index becf8db..d9246ad 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-47.beta.tar.xz) = e9e0daa489ac302d82901f689c553a67725b18c200d4d5c4b9d9bad648db47c2d51067bf3b2200258a73a8ce5e5dfb9bc05d4c389dd5353735842a0f1c82f964 +SHA512 (gnome-software-47.rc.tar.xz) = 6e86d964ce634e1a19910227fce554ed45652f047977dbcefe5d2f564273b1d6ef00a4c7f4bd84103b176efe13daf4c1f01ab0049e1dc751a22b4ff4fae5110f From 399c9361bb329921a20ca40db893c12d5f16d911 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 13 Sep 2024 09:54:36 +0200 Subject: [PATCH 02/44] Update to 47.0 --- gnome-software.spec | 8 +++++--- sources | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index d9c30f5..16e66c7 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,7 +25,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 47~rc +Version: 47.0 Release: 1%{?dist} Summary: A software center for GNOME @@ -83,6 +83,7 @@ Requires: flatpak%{?_isa} >= %{flatpak_version} Requires: flatpak-libs%{?_isa} >= %{flatpak_version} Requires: fwupd%{?_isa} >= %{fwupd_version} Requires: glib2%{?_isa} >= %{glib2_version} +Requires: gnome-app-list # gnome-menus is needed for app folder .directory entries Requires: gnome-menus%{?_isa} Requires: gsettings-desktop-schemas%{?_isa} @@ -252,8 +253,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %if %{with webapps} %{_datadir}/swcatalog/xml/gnome-pwa-list-foss.xml %endif -%{_datadir}/swcatalog/xml/org.gnome.Software.Curated.xml -%{_datadir}/swcatalog/xml/org.gnome.Software.Featured.xml %{_datadir}/dbus-1/services/org.freedesktop.PackageKit.service %{_datadir}/dbus-1/services/org.gnome.Software.service %{_datadir}/gnome-shell/search-providers/org.gnome.Software-search-provider.ini @@ -286,6 +285,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Fri Sep 13 2024 Milan Crha - 47.0-1 +- Update to 47.0 + * Fri Aug 30 2024 Milan Crha - 47~rc-1 - Update to 47.rc diff --git a/sources b/sources index d9246ad..ed97419 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-47.rc.tar.xz) = 6e86d964ce634e1a19910227fce554ed45652f047977dbcefe5d2f564273b1d6ef00a4c7f4bd84103b176efe13daf4c1f01ab0049e1dc751a22b4ff4fae5110f +SHA512 (gnome-software-47.0.tar.xz) = 0aac8e2f9803fbdef9e07c798d3171eb6267090fc1ffcbe1e4888e2499a50c28ec4a817ec82205f1eeb4e3220f11920ff045b817b692c8b1f8e5edbdbd82b6b9 From c60a0c56916812752505c72b9f11e4ea9629b2de Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Mon, 16 Sep 2024 23:06:52 -0400 Subject: [PATCH 03/44] Fix ELN build With gnome-app-list split out from gnome-software, only the webapps-related data is installed into swcatalog, and therefore those directories are not created when webapps are disabled (as in RHEL). While c10s does bundle a list file and therefore creates these directories, it is too early to know what this will look like for RHEL 11. --- gnome-software.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gnome-software.spec b/gnome-software.spec index 16e66c7..89b3eb2 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -83,7 +83,9 @@ Requires: flatpak%{?_isa} >= %{flatpak_version} Requires: flatpak-libs%{?_isa} >= %{flatpak_version} Requires: fwupd%{?_isa} >= %{fwupd_version} Requires: glib2%{?_isa} >= %{glib2_version} +%if !0%{?rhel} Requires: gnome-app-list +%endif # gnome-menus is needed for app folder .directory entries Requires: gnome-menus%{?_isa} Requires: gsettings-desktop-schemas%{?_isa} @@ -248,9 +250,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_provenance.so %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_repos.so %{_sysconfdir}/xdg/autostart/org.gnome.Software.desktop +%if %{with webapps} %dir %{_datadir}/swcatalog %dir %{_datadir}/swcatalog/xml -%if %{with webapps} %{_datadir}/swcatalog/xml/gnome-pwa-list-foss.xml %endif %{_datadir}/dbus-1/services/org.freedesktop.PackageKit.service From 287e223c6ea7d1f6a23c930c5728f4492c869e00 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Thu, 19 Sep 2024 13:00:50 +0200 Subject: [PATCH 04/44] Resolves: #2312882 (dkms: Fix callback user data in a reload() function) --- ...lback-user-data-in-a-reload-function.patch | 30 +++++++++++++++++++ gnome-software.spec | 7 ++++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 0001-dkms-Fix-callback-user-data-in-a-reload-function.patch diff --git a/0001-dkms-Fix-callback-user-data-in-a-reload-function.patch b/0001-dkms-Fix-callback-user-data-in-a-reload-function.patch new file mode 100644 index 0000000..e44003e --- /dev/null +++ b/0001-dkms-Fix-callback-user-data-in-a-reload-function.patch @@ -0,0 +1,30 @@ +From 6b29a361465fb9b70c3a4cd66615e200bef65cd6 Mon Sep 17 00:00:00 2001 +Date: Wed, 18 Sep 2024 07:16:42 +0200 +Subject: [PATCH] dkms: Fix callback user data in a reload() function + +The gs_dkms_got_secureboot_state_cb() expects a GTask as its user data, +but the gs_plugin_dkms_reload() passed a GsPlugin instead. That could +cause a crash. + +Reported downstream at: +https://bugzilla.redhat.com/show_bug.cgi?id=2312882 +--- + plugins/dkms/gs-plugin-dkms.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/plugins/dkms/gs-plugin-dkms.c b/plugins/dkms/gs-plugin-dkms.c +index f7971334f..eb423e08d 100644 +--- a/plugins/dkms/gs-plugin-dkms.c ++++ b/plugins/dkms/gs-plugin-dkms.c +@@ -83,7 +83,7 @@ gs_plugin_dkms_reload (GsPlugin *plugin) + + /* mokutil was not installed probably; the reload can be called when some + app/package had been installed, thus re-try to check Secure Boot state */ +- gs_dkms_get_secureboot_state_async (NULL, gs_dkms_got_secureboot_state_cb, g_object_ref (plugin)); ++ gs_dkms_get_secureboot_state_async (NULL, gs_dkms_got_secureboot_state_cb, g_steal_pointer (&task)); + } + } + +-- +2.45.2 + diff --git a/gnome-software.spec b/gnome-software.spec index 89b3eb2..6493449 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -26,13 +26,15 @@ Name: gnome-software Version: 47.0 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later URL: https://apps.gnome.org/Software Source0: https://download.gnome.org/sources/gnome-software/47/%{name}-%{tarball_version}.tar.xz +Patch: 0001-dkms-Fix-callback-user-data-in-a-reload-function.patch + # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 %if 0%{?fedora} || 0%{?rhel} >= 10 @@ -287,6 +289,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Thu Sep 19 2024 Milan Crha - 47.0-2 +- Resolves: #2312882 (dkms: Fix callback user data in a reload() function) + * Fri Sep 13 2024 Milan Crha - 47.0-1 - Update to 47.0 From 8459c63d07257e0d29400db904cb772033d4026b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 4 Oct 2024 16:47:40 +0100 Subject: [PATCH 05/44] Rebuild against libfwupd.so.3 --- ...not-use-API-removed-from-fwupd-2.0.x.patch | 45 +++++++++++++++++++ gnome-software.spec | 6 ++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch diff --git a/0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch b/0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch new file mode 100644 index 0000000..78bd0b9 --- /dev/null +++ b/0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch @@ -0,0 +1,45 @@ +From bdfe5c691a1c3302cf61da0781b290c67f88ba6b Mon Sep 17 00:00:00 2001 +From: Richard Hughes +Date: Tue, 1 Oct 2024 12:24:09 +0100 +Subject: [PATCH 14/14] fwupd: Do not use API removed from fwupd 2.0.x + +All the systemd-offline logic was unused, and so removed. +--- + plugins/fwupd/gs-fwupd-app.c | 5 ----- + plugins/fwupd/gs-plugin-fwupd.c | 4 ---- + 2 files changed, 9 deletions(-) + +diff --git a/plugins/fwupd/gs-fwupd-app.c b/plugins/fwupd/gs-fwupd-app.c +index bad97420a..e4376b265 100644 +--- a/plugins/fwupd/gs-fwupd-app.c ++++ b/plugins/fwupd/gs-fwupd-app.c +@@ -117,11 +117,6 @@ gs_fwupd_app_set_from_device (GsApp *app, + ) + gs_app_set_state (app, GS_APP_STATE_UPDATABLE_LIVE); + +- /* only can be applied in systemd-offline */ +- if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) +- gs_app_set_metadata (app, "fwupd::OnlyOffline", ""); +- +- + /* reboot required to apply update */ + if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + gs_app_add_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT); +diff --git a/plugins/fwupd/gs-plugin-fwupd.c b/plugins/fwupd/gs-plugin-fwupd.c +index fd6049055..8e3d8ab2c 100644 +--- a/plugins/fwupd/gs-plugin-fwupd.c ++++ b/plugins/fwupd/gs-plugin-fwupd.c +@@ -1430,10 +1430,6 @@ gs_plugin_fwupd_install_async (GsPluginFwupd *self, + * https://github.com/fwupd/fwupd/issues/5522. */ + g_set_object (&self->app_current, app); + +- /* only offline supported */ +- if (gs_app_get_metadata_item (app, "fwupd::OnlyOffline") != NULL) +- install_flags |= FWUPD_INSTALL_FLAG_OFFLINE; +- + gs_app_set_state (app, GS_APP_STATE_INSTALLING); + + fwupd_client_install_async (self->client, data->device_id, +-- +2.46.2 + diff --git a/gnome-software.spec b/gnome-software.spec index 6493449..06e77c9 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -26,7 +26,7 @@ Name: gnome-software Version: 47.0 -Release: 2%{?dist} +Release: 3%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -34,6 +34,7 @@ URL: https://apps.gnome.org/Software Source0: https://download.gnome.org/sources/gnome-software/47/%{name}-%{tarball_version}.tar.xz Patch: 0001-dkms-Fix-callback-user-data-in-a-reload-function.patch +Patch: 0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 @@ -289,6 +290,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Fri Oct 04 2024 Richard Hughes - 47.0-3 +- Rebuild against fwupd 2.0.0 + * Thu Sep 19 2024 Milan Crha - 47.0-2 - Resolves: #2312882 (dkms: Fix callback user data in a reload() function) From 8bf3c4e72093b33e50e2f733699ae1708675b1a2 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Thu, 10 Oct 2024 14:58:22 +0200 Subject: [PATCH 06/44] Update to 47.1 --- ...lback-user-data-in-a-reload-function.patch | 30 ------------- ...not-use-API-removed-from-fwupd-2.0.x.patch | 45 ------------------- gnome-software.spec | 11 +++-- sources | 2 +- 4 files changed, 6 insertions(+), 82 deletions(-) delete mode 100644 0001-dkms-Fix-callback-user-data-in-a-reload-function.patch delete mode 100644 0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch diff --git a/0001-dkms-Fix-callback-user-data-in-a-reload-function.patch b/0001-dkms-Fix-callback-user-data-in-a-reload-function.patch deleted file mode 100644 index e44003e..0000000 --- a/0001-dkms-Fix-callback-user-data-in-a-reload-function.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 6b29a361465fb9b70c3a4cd66615e200bef65cd6 Mon Sep 17 00:00:00 2001 -Date: Wed, 18 Sep 2024 07:16:42 +0200 -Subject: [PATCH] dkms: Fix callback user data in a reload() function - -The gs_dkms_got_secureboot_state_cb() expects a GTask as its user data, -but the gs_plugin_dkms_reload() passed a GsPlugin instead. That could -cause a crash. - -Reported downstream at: -https://bugzilla.redhat.com/show_bug.cgi?id=2312882 ---- - plugins/dkms/gs-plugin-dkms.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/plugins/dkms/gs-plugin-dkms.c b/plugins/dkms/gs-plugin-dkms.c -index f7971334f..eb423e08d 100644 ---- a/plugins/dkms/gs-plugin-dkms.c -+++ b/plugins/dkms/gs-plugin-dkms.c -@@ -83,7 +83,7 @@ gs_plugin_dkms_reload (GsPlugin *plugin) - - /* mokutil was not installed probably; the reload can be called when some - app/package had been installed, thus re-try to check Secure Boot state */ -- gs_dkms_get_secureboot_state_async (NULL, gs_dkms_got_secureboot_state_cb, g_object_ref (plugin)); -+ gs_dkms_get_secureboot_state_async (NULL, gs_dkms_got_secureboot_state_cb, g_steal_pointer (&task)); - } - } - --- -2.45.2 - diff --git a/0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch b/0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch deleted file mode 100644 index 78bd0b9..0000000 --- a/0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch +++ /dev/null @@ -1,45 +0,0 @@ -From bdfe5c691a1c3302cf61da0781b290c67f88ba6b Mon Sep 17 00:00:00 2001 -From: Richard Hughes -Date: Tue, 1 Oct 2024 12:24:09 +0100 -Subject: [PATCH 14/14] fwupd: Do not use API removed from fwupd 2.0.x - -All the systemd-offline logic was unused, and so removed. ---- - plugins/fwupd/gs-fwupd-app.c | 5 ----- - plugins/fwupd/gs-plugin-fwupd.c | 4 ---- - 2 files changed, 9 deletions(-) - -diff --git a/plugins/fwupd/gs-fwupd-app.c b/plugins/fwupd/gs-fwupd-app.c -index bad97420a..e4376b265 100644 ---- a/plugins/fwupd/gs-fwupd-app.c -+++ b/plugins/fwupd/gs-fwupd-app.c -@@ -117,11 +117,6 @@ gs_fwupd_app_set_from_device (GsApp *app, - ) - gs_app_set_state (app, GS_APP_STATE_UPDATABLE_LIVE); - -- /* only can be applied in systemd-offline */ -- if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) -- gs_app_set_metadata (app, "fwupd::OnlyOffline", ""); -- -- - /* reboot required to apply update */ - if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) - gs_app_add_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT); -diff --git a/plugins/fwupd/gs-plugin-fwupd.c b/plugins/fwupd/gs-plugin-fwupd.c -index fd6049055..8e3d8ab2c 100644 ---- a/plugins/fwupd/gs-plugin-fwupd.c -+++ b/plugins/fwupd/gs-plugin-fwupd.c -@@ -1430,10 +1430,6 @@ gs_plugin_fwupd_install_async (GsPluginFwupd *self, - * https://github.com/fwupd/fwupd/issues/5522. */ - g_set_object (&self->app_current, app); - -- /* only offline supported */ -- if (gs_app_get_metadata_item (app, "fwupd::OnlyOffline") != NULL) -- install_flags |= FWUPD_INSTALL_FLAG_OFFLINE; -- - gs_app_set_state (app, GS_APP_STATE_INSTALLING); - - fwupd_client_install_async (self->client, data->device_id, --- -2.46.2 - diff --git a/gnome-software.spec b/gnome-software.spec index 06e77c9..849bd92 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,17 +25,14 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 47.0 -Release: 3%{?dist} +Version: 47.1 +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later URL: https://apps.gnome.org/Software Source0: https://download.gnome.org/sources/gnome-software/47/%{name}-%{tarball_version}.tar.xz -Patch: 0001-dkms-Fix-callback-user-data-in-a-reload-function.patch -Patch: 0001-fwupd-Do-not-use-API-removed-from-fwupd-2.0.x.patch - # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 %if 0%{?fedora} || 0%{?rhel} >= 10 @@ -143,7 +140,6 @@ This package includes the rpm-ostree backend. %build %meson \ - -Dsoup2=false \ -Dsnap=false \ %if %{with malcontent} -Dmalcontent=true \ @@ -290,6 +286,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Thu Oct 10 2024 Milan Crha - 47.1-1 +- Update to 47.1 + * Fri Oct 04 2024 Richard Hughes - 47.0-3 - Rebuild against fwupd 2.0.0 diff --git a/sources b/sources index ed97419..ee2d17f 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-47.0.tar.xz) = 0aac8e2f9803fbdef9e07c798d3171eb6267090fc1ffcbe1e4888e2499a50c28ec4a817ec82205f1eeb4e3220f11920ff045b817b692c8b1f8e5edbdbd82b6b9 +SHA512 (gnome-software-47.1.tar.xz) = 18c500ef0487f9303097f939e11d2841df87d609d05484e3d4419ce9a27f6156ec4aabeca61191724d557b5603ff7857095ed09fc065b6064d0b92286bc19748 From 79f3e3a0c8c6cde3880c1f5b6cf39c00e4c758fd Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 25 Nov 2024 14:36:36 +0100 Subject: [PATCH 07/44] Update to 47.2 --- gnome-software.spec | 7 ++++++- sources | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 849bd92..9693dd6 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,7 +25,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 47.1 +Version: 47.2 Release: 1%{?dist} Summary: A software center for GNOME @@ -89,8 +89,10 @@ Requires: gnome-app-list # gnome-menus is needed for app folder .directory entries Requires: gnome-menus%{?_isa} Requires: gsettings-desktop-schemas%{?_isa} +Requires: gtk4 >= %{gtk4_version} Requires: json-glib%{?_isa} >= %{json_glib_version} Requires: iso-codes +Requires: libadwaita >= %{libadwaita_version} # librsvg2 is needed for gdk-pixbuf svg loader Requires: librsvg2%{?_isa} Requires: libxmlb%{?_isa} >= %{libxmlb_version} @@ -286,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Mon Nov 25 2024 Milan Crha - 47.2-1 +- Update to 47.2 + * Thu Oct 10 2024 Milan Crha - 47.1-1 - Update to 47.1 diff --git a/sources b/sources index ee2d17f..4b9ac1c 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-47.1.tar.xz) = 18c500ef0487f9303097f939e11d2841df87d609d05484e3d4419ce9a27f6156ec4aabeca61191724d557b5603ff7857095ed09fc065b6064d0b92286bc19748 +SHA512 (gnome-software-47.2.tar.xz) = 60de4a3c3237bfdc0dbbefdeccb00c97af7332a36c6beb5610fb6a416508e8b4712fd007e395ad283a3f9cb8d3e0f3a56c134ff28b3f22953a448188a1005cb6 From 0dd97896b5b14544057ab9c8c6629bcb9bde6364 Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Thu, 5 Dec 2024 16:04:14 -0500 Subject: [PATCH 08/44] Rebuild for fwupd 2.0 The ELN build of fwupd 2.0, which included an ABI version change, was delayed until now. --- gnome-software.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gnome-software.spec b/gnome-software.spec index 9693dd6..d744445 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -26,7 +26,7 @@ Name: gnome-software Version: 47.2 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -288,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Thu Dec 05 2024 Yaakov Selkowitz - 47.2-2 +- Rebuild for fwupd 2.0 + * Mon Nov 25 2024 Milan Crha - 47.2-1 - Update to 47.2 From 6263c5e777f454dff0a865042bf767484847552e Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 9 Dec 2024 10:57:46 +0100 Subject: [PATCH 09/44] Resolves: #2272232 (Crash under gs_appstream_gather_merge_data()) --- ...under-gs_appstream_gather_merge_data.patch | 1468 +++++++++++++++++ gnome-software.spec | 7 +- 2 files changed, 1474 insertions(+), 1 deletion(-) create mode 100644 0001-crash-under-gs_appstream_gather_merge_data.patch diff --git a/0001-crash-under-gs_appstream_gather_merge_data.patch b/0001-crash-under-gs_appstream_gather_merge_data.patch new file mode 100644 index 0000000..b52cefd --- /dev/null +++ b/0001-crash-under-gs_appstream_gather_merge_data.patch @@ -0,0 +1,1468 @@ +From f1140fd98666fbdb35dfe13a6b3f58fedcb1ad0b Mon Sep 17 00:00:00 2001 +Date: Mon, 29 Apr 2024 17:37:57 +0200 +Subject: [PATCH 1/3] gs-plugin-appstream: Simplify XbSilo locking + +Instead of holding RW lock on the internal silo variable, simply return +a reference to it and work with it like with an immutable object. That +simplifies locking and makes the code cleaner. It can also make the code +quicker, in some cases. +--- + plugins/core/gs-plugin-appstream.c | 194 ++++++++++++++++------------- + 1 file changed, 107 insertions(+), 87 deletions(-) + +diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c +index 7ccbac12a5..5749a175f3 100644 +--- a/plugins/core/gs-plugin-appstream.c ++++ b/plugins/core/gs-plugin-appstream.c +@@ -36,7 +36,7 @@ struct _GsPluginAppstream + GsWorkerThread *worker; /* (owned) */ + + XbSilo *silo; +- GRWLock silo_lock; ++ GMutex silo_lock; + gchar *silo_filename; + GHashTable *silo_installed_by_desktopid; + GHashTable *silo_installed_by_id; +@@ -65,7 +65,7 @@ gs_plugin_appstream_dispose (GObject *object) + g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); + g_clear_pointer (&self->silo_installed_by_id, g_hash_table_unref); + g_clear_object (&self->settings); +- g_rw_lock_clear (&self->silo_lock); ++ g_mutex_clear (&self->silo_lock); + g_clear_object (&self->worker); + g_clear_pointer (&self->file_monitors, g_ptr_array_unref); + +@@ -77,9 +77,7 @@ gs_plugin_appstream_init (GsPluginAppstream *self) + { + GApplication *application = g_application_get_default (); + +- /* XbSilo needs external locking as we destroy the silo and build a new +- * one when something changes */ +- g_rw_lock_init (&self->silo_lock); ++ g_mutex_init (&self->silo_lock); + + /* require settings */ + self->settings = g_settings_new ("org.gnome.software"); +@@ -530,10 +528,13 @@ gs_add_appstream_metainfo_location (GPtrArray *locations, const gchar *root) + g_build_filename (root, "appdata", NULL)); + } + +-static gboolean +-gs_plugin_appstream_check_silo (GsPluginAppstream *self, +- GCancellable *cancellable, +- GError **error) ++static XbSilo * ++gs_plugin_appstream_ref_silo (GsPluginAppstream *self, ++ gchar **out_silo_filename, ++ GHashTable **out_silo_installed_by_desktopid, ++ GHashTable **out_silo_installed_by_id, ++ GCancellable *cancellable, ++ GError **error) + { + const gchar *test_xml; + g_autofree gchar *blobfn = NULL; +@@ -541,21 +542,25 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + g_autoptr(XbNode) n = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) installed = NULL; +- g_autoptr(GRWLockReaderLocker) reader_locker = NULL; +- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; ++ g_autoptr(GMutexLocker) locker = NULL; + g_autoptr(GPtrArray) parent_appdata = g_ptr_array_new_with_free_func (g_free); + g_autoptr(GPtrArray) parent_appstream = NULL; + g_autoptr(GMainContext) old_thread_default = NULL; + +- reader_locker = g_rw_lock_reader_locker_new (&self->silo_lock); ++ locker = g_mutex_locker_new (&self->silo_lock); + /* everything is okay */ + if (self->silo != NULL && xb_silo_is_valid (self->silo) && +- g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) +- return TRUE; +- g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); ++ g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) { ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL) ++ *out_silo_installed_by_desktopid = self->silo_installed_by_desktopid ? g_hash_table_ref (self->silo_installed_by_desktopid) : NULL; ++ if (out_silo_installed_by_id != NULL) ++ *out_silo_installed_by_id = self->silo_installed_by_id ? g_hash_table_ref (self->silo_installed_by_id) : NULL; ++ return g_object_ref (self->silo); ++ } + + /* drat! silo needs regenerating */ +- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); + reload: + g_clear_object (&self->silo); + g_clear_pointer (&self->silo_filename, g_free); +@@ -594,7 +599,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!xb_builder_source_load_xml (source, test_xml, + XB_BUILDER_SOURCE_FLAG_NONE, + error)) +- return FALSE; ++ return NULL; + fixup1 = xb_builder_fixup_new ("AddOriginKeywords", + gs_plugin_appstream_add_origin_keyword_cb, + self, NULL); +@@ -640,7 +645,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!gs_plugin_appstream_load_appstream (self, builder, fn, cancellable, error)) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + } + for (guint i = 0; i < parent_appdata->len; i++) { +@@ -648,7 +653,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!gs_plugin_appstream_load_appdata (self, builder, fn, cancellable, error)) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + } + for (guint i = 0; i < parent_desktop->len; i++) { +@@ -657,7 +662,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!gs_appstream_load_desktop_files (builder, dir, NULL, &file_monitor, cancellable, error)) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + gs_plugin_appstream_maybe_store_file_monitor (self, file_monitor); + } +@@ -678,7 +683,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + GS_UTILS_CACHE_FLAG_CREATE_DIRECTORY, + error); + if (blobfn == NULL) +- return FALSE; ++ return NULL; + file = g_file_new_for_path (blobfn); + g_debug ("ensuring %s", blobfn); + +@@ -696,7 +701,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (self->silo == NULL) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + + if (old_thread_default != NULL) +@@ -717,7 +722,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + GS_PLUGIN_ERROR, + GS_PLUGIN_ERROR_NOT_SUPPORTED, + "No AppStream data found"); +- return FALSE; ++ return NULL; + } + + g_clear_object (&n); +@@ -768,7 +773,13 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + } + + /* success */ +- return TRUE; ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL) ++ *out_silo_installed_by_desktopid = self->silo_installed_by_desktopid ? g_hash_table_ref (self->silo_installed_by_desktopid) : NULL; ++ if (out_silo_installed_by_id != NULL) ++ *out_silo_installed_by_id = self->silo_installed_by_id ? g_hash_table_ref (self->silo_installed_by_id) : NULL; ++ return g_object_ref (self->silo); + } + + static void +@@ -776,7 +787,6 @@ gs_plugin_appstream_reload (GsPlugin *plugin) + { + GsPluginAppstream *self; + g_autoptr(GsAppList) list = NULL; +- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; + guint sz; + + g_return_if_fail (GS_IS_PLUGIN_APPSTREAM (plugin)); +@@ -790,9 +800,8 @@ gs_plugin_appstream_reload (GsPlugin *plugin) + } + + self = GS_PLUGIN_APPSTREAM (plugin); +- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); +- if (self->silo != NULL) +- xb_silo_invalidate (self->silo); ++ /* simpler than getting the silo and setting it invalid */ ++ g_atomic_int_inc (&self->file_monitor_stamp); + } + + static gint +@@ -834,11 +843,13 @@ setup_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) + g_task_return_error (task, g_steal_pointer (&local_error)); + else + g_task_return_boolean (task, TRUE); +@@ -910,21 +921,21 @@ url_to_app_thread_cb (GTask *task, + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); + GsPluginUrlToAppData *data = task_data; + g_autoptr(GsAppList) list = NULL; +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); + list = gs_app_list_new (); + +- if (gs_appstream_url_to_app (GS_PLUGIN (self), self->silo, list, data->url, cancellable, &local_error)) ++ if (gs_appstream_url_to_app (GS_PLUGIN (self), silo, list, data->url, cancellable, &local_error)) + g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); + else + g_task_return_error (task, g_steal_pointer (&local_error)); +@@ -1006,17 +1017,14 @@ gs_plugin_appstream_set_compulsory_quirk (GsApp *app, XbNode *component) + static gboolean + gs_plugin_appstream_refine_state (GsPluginAppstream *self, + GsApp *app, ++ GHashTable *silo_installed_by_id, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; +- + /* Ignore apps with no ID */ +- if (gs_app_get_id (app) == NULL) ++ if (gs_app_get_id (app) == NULL || silo_installed_by_id == NULL) + return TRUE; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- if (g_hash_table_contains (self->silo_installed_by_id, gs_app_get_id (app))) ++ if (g_hash_table_contains (silo_installed_by_id, gs_app_get_id (app))) + gs_app_set_state (app, GS_APP_STATE_INSTALLED); + return TRUE; + } +@@ -1027,20 +1035,21 @@ gs_plugin_refine_from_id (GsPluginAppstream *self, + GsPluginRefineFlags flags, + GHashTable *apps_by_id, + GHashTable *apps_by_origin_and_id, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + gboolean *found, + GError **error) + { + const gchar *id, *origin; + GPtrArray *components; +- g_autoptr(GRWLockReaderLocker) locker = NULL; + + /* not enough info to find */ + id = gs_app_get_id (app); + if (id == NULL) + return TRUE; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + origin = gs_app_get_origin_appstream (app); + + /* look in AppStream then fall back to AppData */ +@@ -1056,15 +1065,15 @@ gs_plugin_refine_from_id (GsPluginAppstream *self, + + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); +- if (!gs_appstream_refine_app (GS_PLUGIN (self), app, self->silo, component, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) ++ if (!gs_appstream_refine_app (GS_PLUGIN (self), app, silo, component, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->default_scope, error)) + return FALSE; + gs_plugin_appstream_set_compulsory_quirk (app, component); + } + + /* if an installed desktop or appdata file exists set to installed */ + if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) { +- if (!gs_plugin_appstream_refine_state (self, app, error)) ++ if (!gs_plugin_appstream_refine_state (self, app, silo_installed_by_id, error)) + return FALSE; + } + +@@ -1077,6 +1086,10 @@ static gboolean + gs_plugin_refine_from_pkgname (GsPluginAppstream *self, + GsApp *app, + GsPluginRefineFlags flags, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + GError **error) + { + GPtrArray *sources = gs_app_get_sources (app); +@@ -1089,33 +1102,30 @@ gs_plugin_refine_from_pkgname (GsPluginAppstream *self, + /* find all apps when matching any prefixes */ + for (guint j = 0; j < sources->len; j++) { + const gchar *pkgname = g_ptr_array_index (sources, j); +- g_autoptr(GRWLockReaderLocker) locker = NULL; + g_autoptr(GString) xpath = g_string_new (NULL); + g_autoptr(XbNode) component = NULL; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + /* prefer actual apps and then fallback to anything else */ + xb_string_append_union (xpath, "components/component[@type='desktop-application']/pkgname[text()='%s']/..", pkgname); + xb_string_append_union (xpath, "components/component[@type='console-application']/pkgname[text()='%s']/..", pkgname); + xb_string_append_union (xpath, "components/component[@type='web-application']/pkgname[text()='%s']/..", pkgname); + xb_string_append_union (xpath, "components/component/pkgname[text()='%s']/..", pkgname); +- component = xb_silo_query_first (self->silo, xpath->str, &error_local); ++ component = xb_silo_query_first (silo, xpath->str, &error_local); + if (component == NULL) { + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + continue; + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } +- if (!gs_appstream_refine_app (GS_PLUGIN (self), app, self->silo, component, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) ++ if (!gs_appstream_refine_app (GS_PLUGIN (self), app, silo, component, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->default_scope, error)) + return FALSE; + gs_plugin_appstream_set_compulsory_quirk (app, component); + } + + /* if an installed desktop or appdata file exists set to installed */ + if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) { +- if (!gs_plugin_appstream_refine_state (self, app, error)) ++ if (!gs_plugin_appstream_refine_state (self, app, silo_installed_by_id, error)) + return FALSE; + } + +@@ -1153,6 +1163,10 @@ static gboolean refine_wildcard (GsPluginAppstream *self, + GsAppList *list, + GsPluginRefineFlags refine_flags, + GHashTable *apps_by_id, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + GCancellable *cancellable, + GError **error); + +@@ -1171,13 +1185,17 @@ refine_thread_cb (GTask *task, + g_autoptr(GHashTable) apps_by_id = NULL; + g_autoptr(GHashTable) apps_by_origin_and_id = NULL; + g_autoptr(GPtrArray) components = NULL; +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autofree gchar *silo_filename = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autoptr(GHashTable) silo_installed_by_id = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, &silo_filename, &silo_installed_by_desktopid, &silo_installed_by_id, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1185,9 +1203,7 @@ refine_thread_cb (GTask *task, + apps_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); + apps_by_origin_and_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- components = xb_silo_query (self->silo, "components/component/id", 0, NULL); ++ components = xb_silo_query (silo, "components/component/id", 0, NULL); + for (guint i = 0; components != NULL && i < components->len; i++) { + XbNode *node = g_ptr_array_index (components, i); + g_autoptr(XbNode) component_node = xb_node_get_parent (node); +@@ -1234,7 +1250,7 @@ refine_thread_cb (GTask *task, + } + + g_clear_pointer (&components, g_ptr_array_unref); +- components = xb_silo_query (self->silo, "component/id", 0, NULL); ++ components = xb_silo_query (silo, "component/id", 0, NULL); + for (guint i = 0; components != NULL && i < components->len; i++) { + XbNode *node = g_ptr_array_index (components, i); + g_autoptr(XbNode) component_node = xb_node_get_parent (node); +@@ -1262,12 +1278,14 @@ refine_thread_cb (GTask *task, + continue; + + /* find by ID then fall back to package name */ +- if (!gs_plugin_refine_from_id (self, app, flags, apps_by_id, apps_by_origin_and_id, &found, &local_error)) { ++ if (!gs_plugin_refine_from_id (self, app, flags, apps_by_id, apps_by_origin_and_id, silo, silo_filename, ++ silo_installed_by_desktopid, silo_installed_by_id, &found, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + if (!found) { +- if (!gs_plugin_refine_from_pkgname (self, app, flags, &local_error)) { ++ if (!gs_plugin_refine_from_pkgname (self, app, flags, silo, silo_filename, ++ silo_installed_by_desktopid, silo_installed_by_id, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1286,7 +1304,8 @@ refine_thread_cb (GTask *task, + GsApp *app = gs_app_list_index (app_list, j); + + if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && +- !refine_wildcard (self, app, list, flags, apps_by_id, cancellable, &local_error)) { ++ !refine_wildcard (self, app, list, flags, apps_by_id, silo, silo_filename, ++ silo_installed_by_desktopid, silo_installed_by_id,cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1311,20 +1330,21 @@ refine_wildcard (GsPluginAppstream *self, + GsAppList *list, + GsPluginRefineFlags refine_flags, + GHashTable *apps_by_id, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + GCancellable *cancellable, + GError **error) + { + const gchar *id; + GPtrArray *components; +- g_autoptr(GRWLockReaderLocker) locker = NULL; + + /* not enough info to find */ + id = gs_app_get_id (app); + if (id == NULL) + return TRUE; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + components = g_hash_table_lookup (apps_by_id, id); + if (components == NULL) + return TRUE; +@@ -1333,20 +1353,20 @@ refine_wildcard (GsPluginAppstream *self, + g_autoptr(GsApp) new = NULL; + + /* new app */ +- new = gs_appstream_create_app (GS_PLUGIN (self), self->silo, component, self->silo_filename ? self->silo_filename : "", ++ new = gs_appstream_create_app (GS_PLUGIN (self), silo, component, silo_filename ? silo_filename : "", + self->default_scope, error); + if (new == NULL) + return FALSE; + gs_app_set_scope (new, AS_COMPONENT_SCOPE_SYSTEM); + gs_app_subsume_metadata (new, app); +- if (!gs_appstream_refine_app (GS_PLUGIN (self), new, self->silo, component, refine_flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) ++ if (!gs_appstream_refine_app (GS_PLUGIN (self), new, silo, component, refine_flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->default_scope, error)) + return FALSE; + gs_plugin_appstream_set_compulsory_quirk (new, component); + + /* if an installed desktop or appdata file exists set to installed */ + if (gs_app_get_state (new) == GS_APP_STATE_UNKNOWN) { +- if (!gs_plugin_appstream_refine_state (self, new, error)) ++ if (!gs_plugin_appstream_refine_state (self, new, silo_installed_by_id, error)) + return FALSE; + } + +@@ -1398,21 +1418,20 @@ refine_categories_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + GsPluginRefineCategoriesData *data = task_data; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- if (!gs_appstream_refine_category_sizes (self->silo, data->list, cancellable, &local_error)) { ++ if (!gs_appstream_refine_category_sizes (silo, data->list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1462,7 +1481,7 @@ list_apps_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GsAppList) list = gs_app_list_new (); + GsPluginListAppsData *data = task_data; + GDateTime *released_since = NULL; +@@ -1516,65 +1535,64 @@ list_apps_thread_cb (GTask *task, + } + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + if (released_since != NULL && +- !gs_appstream_add_recent (GS_PLUGIN (self), self->silo, list, age_secs, ++ !gs_appstream_add_recent (GS_PLUGIN (self), silo, list, age_secs, + cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (is_curated != GS_APP_QUERY_TRISTATE_UNSET && +- !gs_appstream_add_popular (self->silo, list, cancellable, &local_error)) { ++ !gs_appstream_add_popular (silo, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (is_featured != GS_APP_QUERY_TRISTATE_UNSET && +- !gs_appstream_add_featured (self->silo, list, cancellable, &local_error)) { ++ !gs_appstream_add_featured (silo, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (category != NULL && +- !gs_appstream_add_category_apps (GS_PLUGIN (self), self->silo, category, list, cancellable, &local_error)) { ++ !gs_appstream_add_category_apps (GS_PLUGIN (self), silo, category, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (is_installed == GS_APP_QUERY_TRISTATE_TRUE && +- !gs_appstream_add_installed (GS_PLUGIN (self), self->silo, list, cancellable, &local_error)) { ++ !gs_appstream_add_installed (GS_PLUGIN (self), silo, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (deployment_featured != NULL && +- !gs_appstream_add_deployment_featured (self->silo, deployment_featured, list, ++ !gs_appstream_add_deployment_featured (silo, deployment_featured, list, + cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (developers != NULL && +- !gs_appstream_search_developer_apps (GS_PLUGIN (self), self->silo, developers, list, cancellable, &local_error)) { ++ !gs_appstream_search_developer_apps (GS_PLUGIN (self), silo, developers, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (keywords != NULL && +- !gs_appstream_search (GS_PLUGIN (self), self->silo, keywords, list, cancellable, &local_error)) { ++ !gs_appstream_search (GS_PLUGIN (self), silo, keywords, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (alternate_of != NULL && +- !gs_appstream_add_alternates (self->silo, alternate_of, list, cancellable, &local_error)) { ++ !gs_appstream_add_alternates (silo, alternate_of, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1623,12 +1641,14 @@ refresh_metadata_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* Checking the silo will refresh it if needed. */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) + g_task_return_error (task, g_steal_pointer (&local_error)); + else + g_task_return_boolean (task, TRUE); +-- +GitLab + + +From cf04c32b5fb1fd267096fad8dd11a22429ad788a Mon Sep 17 00:00:00 2001 +Date: Mon, 29 Apr 2024 17:45:08 +0200 +Subject: [PATCH 2/3] gs-plugin-appstream: Rename file_monitor_stamp to + silo_change_stamp + +Since it's used also in the gs_plugin_appstream_reload() and works +like a change indicator, name it appropriately to avoid confusion. +--- + plugins/core/gs-plugin-appstream.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c +index 5749a175f3..99fe5a1432 100644 +--- a/plugins/core/gs-plugin-appstream.c ++++ b/plugins/core/gs-plugin-appstream.c +@@ -46,8 +46,8 @@ struct _GsPluginAppstream + GPtrArray *file_monitors; /* (owned) (element-type GFileMonitor) */ + /* The stamps help to avoid locking the silo lock in the main thread + and also to detect changes while loading other appstream data. */ +- gint file_monitor_stamp; /* the file monitor stamp, increased on every file monitor change */ +- gint file_monitor_stamp_current; /* the currently known file monitor stamp, checked for changes */ ++ gint silo_change_stamp; /* the silo change stamp, increased on every silo change */ ++ gint silo_change_stamp_current; /* the currently known silo change stamp, checked for changes */ + }; + + G_DEFINE_TYPE (GsPluginAppstream, gs_plugin_appstream, GS_TYPE_PLUGIN) +@@ -224,7 +224,7 @@ gs_plugin_appstream_file_monitor_changed_cb (GFileMonitor *monitor, + gpointer user_data) + { + GsPluginAppstream *self = user_data; +- g_atomic_int_inc (&self->file_monitor_stamp); ++ g_atomic_int_inc (&self->silo_change_stamp); + } + + static void +@@ -550,7 +550,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, + locker = g_mutex_locker_new (&self->silo_lock); + /* everything is okay */ + if (self->silo != NULL && xb_silo_is_valid (self->silo) && +- g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) { ++ g_atomic_int_get (&self->silo_change_stamp_current) == g_atomic_int_get (&self->silo_change_stamp)) { + if (out_silo_filename != NULL) + *out_silo_filename = g_strdup (self->silo_filename); + if (out_silo_installed_by_desktopid != NULL) +@@ -568,7 +568,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, + g_clear_pointer (&blobfn, g_free); + self->default_scope = AS_COMPONENT_SCOPE_UNKNOWN; + g_ptr_array_set_size (self->file_monitors, 0); +- g_atomic_int_set (&self->file_monitor_stamp_current, g_atomic_int_get (&self->file_monitor_stamp)); ++ g_atomic_int_set (&self->silo_change_stamp_current, g_atomic_int_get (&self->silo_change_stamp)); + + /* FIXME: https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1422 */ + old_thread_default = g_main_context_ref_thread_default (); +@@ -707,7 +707,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); + +- if (g_atomic_int_get (&self->file_monitor_stamp_current) != g_atomic_int_get (&self->file_monitor_stamp)) { ++ if (g_atomic_int_get (&self->silo_change_stamp_current) != g_atomic_int_get (&self->silo_change_stamp)) { + g_ptr_array_set_size (parent_appdata, 0); + g_ptr_array_set_size (parent_appstream, 0); + g_debug ("appstream: File monitors reported change while loading appstream data, reloading..."); +@@ -800,8 +800,8 @@ gs_plugin_appstream_reload (GsPlugin *plugin) + } + + self = GS_PLUGIN_APPSTREAM (plugin); +- /* simpler than getting the silo and setting it invalid */ +- g_atomic_int_inc (&self->file_monitor_stamp); ++ /* Invalidate the reference to the current silo */ ++ g_atomic_int_inc (&self->silo_change_stamp); + } + + static gint +-- +GitLab + + +From 59b301b8a878f3e81359909a8d4107715eb60a35 Mon Sep 17 00:00:00 2001 +Date: Tue, 30 Apr 2024 13:13:36 +0200 +Subject: [PATCH 3/3] flatpak: Simplify XbSilo locking + +Instead of holding RW lock on the internal silo variable, simply return +a reference to it and work with it like with an immutable object. That +simplifies locking and makes the code cleaner. It can also make the code +quicker, in some cases. +--- + plugins/flatpak/gs-flatpak.c | 307 +++++++++++++++++++---------------- + 1 file changed, 163 insertions(+), 144 deletions(-) + +diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c +index 2e175fd08e..9893cf88b7 100644 +--- a/plugins/flatpak/gs-flatpak.c ++++ b/plugins/flatpak/gs-flatpak.c +@@ -54,9 +54,11 @@ struct _GsFlatpak { + AsComponentScope scope; + GsPlugin *plugin; + XbSilo *silo; +- GRWLock silo_lock; ++ GMutex silo_lock; + gchar *silo_filename; + GHashTable *silo_installed_by_desktopid; ++ gint silo_change_stamp; ++ gint silo_change_stamp_current; + gchar *id; + guint changed_id; + GHashTable *app_silos; +@@ -654,10 +656,7 @@ gs_flatpak_create_source (GsFlatpak *self, FlatpakRemote *xremote) + static void + gs_flatpak_invalidate_silo (GsFlatpak *self) + { +- g_rw_lock_writer_lock (&self->silo_lock); +- if (self->silo != NULL) +- xb_silo_invalidate (self->silo); +- g_rw_lock_writer_unlock (&self->silo_lock); ++ g_atomic_int_inc (&self->silo_change_stamp); + } + + static void +@@ -1085,32 +1084,39 @@ gs_flatpak_rescan_installed (GsFlatpak *self, + g_debug ("Failed to read flatpak .desktop files in %s: %s", path, error_local->message); + } + +-static gboolean +-gs_flatpak_rescan_appstream_store (GsFlatpak *self, +- gboolean interactive, +- GCancellable *cancellable, +- GError **error) ++static XbSilo * ++gs_flatpak_ref_silo (GsFlatpak *self, ++ gboolean interactive, ++ gchar **out_silo_filename, ++ GHashTable **out_silo_installed_by_desktopid, ++ GCancellable *cancellable, ++ GError **error) + { + g_autofree gchar *blobfn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) xremotes = NULL; + g_autoptr(GPtrArray) desktop_paths = NULL; +- g_autoptr(GRWLockReaderLocker) reader_locker = NULL; +- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; ++ g_autoptr(GMutexLocker) locker = NULL; + g_autoptr(XbBuilder) builder = NULL; + g_autoptr(GMainContext) old_thread_default = NULL; + +- reader_locker = g_rw_lock_reader_locker_new (&self->silo_lock); ++ locker = g_mutex_locker_new (&self->silo_lock); + /* everything is okay */ +- if (self->silo != NULL && xb_silo_is_valid (self->silo)) +- return TRUE; +- g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); ++ if (self->silo != NULL && xb_silo_is_valid (self->silo) && ++ g_atomic_int_get (&self->silo_change_stamp_current) == g_atomic_int_get (&self->silo_change_stamp)) { ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL && self->silo_installed_by_desktopid) ++ *out_silo_installed_by_desktopid = g_hash_table_ref (self->silo_installed_by_desktopid); ++ return g_object_ref (self->silo); ++ } + + /* drat! silo needs regenerating */ +- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); ++ reload: + g_clear_object (&self->silo); + g_clear_pointer (&self->silo_filename, g_free); + g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); ++ g_atomic_int_set (&self->silo_change_stamp_current, g_atomic_int_get (&self->silo_change_stamp)); + + /* FIXME: https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1422 */ + old_thread_default = g_main_context_ref_thread_default (); +@@ -1138,7 +1144,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + error); + if (xremotes == NULL) { + gs_flatpak_error_convert (error); +- return FALSE; ++ return NULL; + } + for (guint i = 0; i < xremotes->len; i++) { + g_autoptr(GError) error_local = NULL; +@@ -1152,7 +1158,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + flatpak_remote_get_name (xremote), error_local->message); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + gs_flatpak_error_convert (error); +- return FALSE; ++ return NULL; + } + } + } +@@ -1176,7 +1182,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + GS_UTILS_CACHE_FLAG_CREATE_DIRECTORY, + error); + if (blobfn == NULL) +- return FALSE; ++ return NULL; + file = g_file_new_for_path (blobfn); + g_debug ("ensuring %s", blobfn); + +@@ -1195,6 +1201,17 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); + ++ if (g_atomic_int_get (&self->silo_change_stamp_current) != g_atomic_int_get (&self->silo_change_stamp)) { ++ g_clear_pointer (&blobfn, g_free); ++ g_clear_pointer (&xremotes, g_ptr_array_unref); ++ g_clear_pointer (&desktop_paths, g_ptr_array_unref); ++ g_clear_pointer (&old_thread_default, g_main_context_unref); ++ g_clear_object (&file); ++ g_clear_object (&builder); ++ g_debug ("flatpak: Reported change while loading appstream data, reloading..."); ++ goto reload; ++ } ++ + if (self->silo != NULL) { + g_autoptr(GPtrArray) installed = NULL; + g_autoptr(XbNode) info_filename = NULL; +@@ -1218,66 +1235,46 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + info_filename = xb_silo_query_first (self->silo, "/info/filename", NULL); + if (info_filename != NULL) + self->silo_filename = g_strdup (xb_node_get_text (info_filename)); ++ ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL && self->silo_installed_by_desktopid) ++ *out_silo_installed_by_desktopid = g_hash_table_ref (self->silo_installed_by_desktopid); ++ return g_object_ref (self->silo); + } + +- /* success */ +- return self->silo != NULL; ++ return NULL; + } + + static gboolean + gs_flatpak_rescan_app_data (GsFlatpak *self, + gboolean interactive, ++ XbSilo **out_silo, ++ gchar **out_silo_filename, ++ GHashTable **out_silo_installed_by_desktopid, + GCancellable *cancellable, + GError **error) + { ++ g_autoptr(XbSilo) silo = NULL; ++ + if (self->requires_full_rescan) { + gboolean res = gs_flatpak_refresh (self, 60, interactive, cancellable, error); +- if (res) ++ if (res) { + self->requires_full_rescan = FALSE; +- else ++ } else { + gs_flatpak_internal_data_changed (self); +- return res; ++ return res; ++ } + } + +- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { ++ silo = gs_flatpak_ref_silo (self, interactive, out_silo_filename, out_silo_installed_by_desktopid, cancellable, error); ++ if (silo == NULL) { + gs_flatpak_internal_data_changed (self); + return FALSE; + } + +- return TRUE; +-} +- +-/* Returns with a read lock held on @self->silo_lock on success. +- The *locker should be NULL when being called. */ +-static gboolean +-ensure_flatpak_silo_with_locker (GsFlatpak *self, +- GRWLockReaderLocker **locker, +- gboolean interactive, +- GCancellable *cancellable, +- GError **error) +-{ +- /* should not hold the lock when called */ +- g_return_val_if_fail (*locker == NULL, FALSE); +- +- /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) +- return FALSE; +- +- *locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- while (self->silo == NULL) { +- g_clear_pointer (locker, g_rw_lock_reader_locker_free); +- +- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { +- gs_flatpak_internal_data_changed (self); +- return FALSE; +- } +- +- /* At this point either rescan_appstream_store() returned an error or it successfully +- * initialised self->silo. There is the possibility that another thread will invalidate +- * the silo before we regain the lock. If so, we’ll have to rescan again. */ +- *locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- } ++ if (out_silo != NULL) ++ *out_silo = g_steal_pointer (&silo); + + return TRUE; + } +@@ -1412,6 +1409,7 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, + { + gboolean ret; + g_autoptr(GPtrArray) xremotes = NULL; ++ g_autoptr(XbSilo) silo = NULL; + + /* get remotes */ + xremotes = flatpak_installation_list_remotes (gs_flatpak_get_installation (self, interactive), +@@ -1498,7 +1496,8 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, + } + + /* ensure the AppStream silo is up to date */ +- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { ++ silo = gs_flatpak_ref_silo (self, interactive, NULL, NULL, cancellable, error); ++ if (silo == NULL) { + gs_flatpak_internal_data_changed (self); + return FALSE; + } +@@ -1646,7 +1645,7 @@ gs_flatpak_add_sources (GsFlatpak *self, + FlatpakInstallation *installation = gs_flatpak_get_installation (self, interactive); + + /* refresh */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) + return FALSE; + + /* get installed apps and runtimes */ +@@ -2080,7 +2079,7 @@ gs_flatpak_add_updates (GsFlatpak *self, + FlatpakInstallation *installation = gs_flatpak_get_installation (self, interactive); + + /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) + return FALSE; + + /* get all the updatable apps and runtimes */ +@@ -2390,10 +2389,8 @@ gs_flatpak_create_fake_ref (GsApp *app, GError **error) + return xref; + } + +-/* the _unlocked() version doesn't call gs_flatpak_rescan_app_data, +- * in order to avoid taking the writer lock on self->silo_lock */ + static gboolean +-gs_flatpak_refine_app_state_unlocked (GsFlatpak *self, ++gs_flatpak_refine_app_state_internal (GsFlatpak *self, + GsApp *app, + gboolean interactive, + gboolean force_state_update, +@@ -2505,10 +2502,10 @@ gs_flatpak_refine_app_state (GsFlatpak *self, + GError **error) + { + /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_flatpak_refine_app_state_unlocked (self, app, interactive, force_state_update, cancellable, error); ++ return gs_flatpak_refine_app_state_internal (self, app, interactive, force_state_update, cancellable, error); + } + + static GsApp * +@@ -2594,7 +2591,7 @@ gs_flatpak_create_runtime (GsFlatpak *self, + gs_flatpak_app_set_ref_name (app, split[0]); + gs_flatpak_app_set_ref_arch (app, split[1]); + +- if (!gs_flatpak_refine_app_state_unlocked (self, app, interactive, FALSE, NULL, &local_error)) ++ if (!gs_flatpak_refine_app_state_internal (self, app, interactive, FALSE, NULL, &local_error)) + g_debug ("Failed to refine state for runtime '%s': %s", gs_app_get_unique_id (app), local_error->message); + + /* save in the cache */ +@@ -2984,7 +2981,7 @@ gs_plugin_refine_item_size (GsFlatpak *self, + + /* is the app_runtime already installed? */ + app_runtime = gs_app_get_runtime (app); +- if (!gs_flatpak_refine_app_state_unlocked (self, ++ if (!gs_flatpak_refine_app_state_internal (self, + app_runtime, + interactive, + FALSE, +@@ -3096,6 +3093,8 @@ gs_flatpak_refine_appstream_from_bytes (GsFlatpak *self, + GBytes *appstream_gz, + GsPluginRefineFlags flags, + gboolean interactive, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, + GCancellable *cancellable, + GError **error) + { +@@ -3229,8 +3228,8 @@ gs_flatpak_refine_appstream_from_bytes (GsFlatpak *self, + } + + /* copy details from AppStream to app */ +- if (!gs_appstream_refine_app (self->plugin, app, silo, component_node, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->scope, error)) ++ if (!gs_appstream_refine_app (self->plugin, app, silo, component_node, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->scope, error)) + return FALSE; + + if (gs_app_get_origin (app)) +@@ -3324,6 +3323,8 @@ static gboolean + gs_flatpak_refine_appstream (GsFlatpak *self, + GsApp *app, + XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, + GsPluginRefineFlags flags, + GHashTable *components_by_bundle, + gboolean interactive, +@@ -3410,11 +3411,12 @@ gs_flatpak_refine_appstream (GsFlatpak *self, + appstream_gz, + flags, + interactive, ++ silo_filename, silo_installed_by_desktopid, + cancellable, error); + } + +- if (!gs_appstream_refine_app (self->plugin, app, silo, component, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->scope, error)) ++ if (!gs_appstream_refine_app (self->plugin, app, silo, component, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->scope, error)) + return FALSE; + + /* use the default release as the version number */ +@@ -3423,13 +3425,15 @@ gs_flatpak_refine_appstream (GsFlatpak *self, + } + + static gboolean +-gs_flatpak_refine_app_unlocked (GsFlatpak *self, ++gs_flatpak_refine_app_internal (GsFlatpak *self, + GsApp *app, + GsPluginRefineFlags flags, + gboolean interactive, + gboolean force_state_update, + GHashTable *components_by_bundle, +- GRWLockReaderLocker **locker, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, + GCancellable *cancellable, + GError **error) + { +@@ -3440,13 +3444,9 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, + if (gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_FLATPAK) + return TRUE; + +- g_clear_pointer (locker, g_rw_lock_reader_locker_free); +- +- if (!ensure_flatpak_silo_with_locker (self, locker, interactive, cancellable, error)) +- return FALSE; +- + /* always do AppStream properties */ +- if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, components_by_bundle, interactive, cancellable, error)) ++ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, ++ flags, components_by_bundle, interactive, cancellable, error)) + return FALSE; + + /* AppStream sets the source to appname/arch/branch */ +@@ -3456,7 +3456,7 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, + } + + /* check the installed state */ +- if (!gs_flatpak_refine_app_state_unlocked (self, app, interactive, force_state_update, cancellable, error)) { ++ if (!gs_flatpak_refine_app_state_internal (self, app, interactive, force_state_update, cancellable, error)) { + g_prefix_error (error, "failed to get state: "); + return FALSE; + } +@@ -3473,7 +3473,8 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, + + /* if the state was changed, perhaps set the version from the release */ + if (old_state != gs_app_get_state (app)) { +- if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, components_by_bundle, interactive, cancellable, error)) ++ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, ++ flags, components_by_bundle, interactive, cancellable, error)) + return FALSE; + } + +@@ -3567,11 +3568,16 @@ gs_flatpak_refine_addons (GsFlatpak *self, + gboolean interactive, + GCancellable *cancellable) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autofree gchar *silo_filename = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; + g_autoptr(GsAppList) addons = NULL; + g_autoptr(GString) errors = NULL; + guint ii, sz; + ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, &silo_filename, &silo_installed_by_desktopid, cancellable, NULL)) ++ return; ++ + addons = gs_app_dup_addons (parent_app); + sz = addons ? gs_app_list_length (addons) : 0; + +@@ -3582,7 +3588,8 @@ gs_flatpak_refine_addons (GsFlatpak *self, + if (state != gs_app_get_state (addon)) + continue; + +- if (!gs_flatpak_refine_app_unlocked (self, addon, flags, interactive, TRUE, NULL, &locker, cancellable, &local_error)) { ++ if (!gs_flatpak_refine_app_internal (self, addon, flags, interactive, TRUE, NULL, silo, silo_filename, ++ silo_installed_by_desktopid, cancellable, &local_error)) { + if (errors) + g_string_append_c (errors, '\n'); + else +@@ -3614,13 +3621,16 @@ gs_flatpak_refine_app (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autofree gchar *silo_filename = NULL; + + /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, &silo_filename, &silo_installed_by_desktopid, cancellable, error)) + return FALSE; + +- return gs_flatpak_refine_app_unlocked (self, app, flags, interactive, force_state_update, NULL, &locker, cancellable, error); ++ return gs_flatpak_refine_app_internal (self, app, flags, interactive, force_state_update, NULL, ++ silo, silo_filename, silo_installed_by_desktopid, cancellable, error); + } + + gboolean +@@ -3634,7 +3644,9 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + const gchar *id; + GPtrArray* components = NULL; + g_autoptr(GError) error_local = NULL; +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autofree gchar *silo_filename = NULL; + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); + +@@ -3643,7 +3655,8 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + if (id == NULL) + return TRUE; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); ++ if (silo == NULL) + return FALSE; + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardQuerySilo, "Flatpak (query silo)", NULL); +@@ -3653,7 +3666,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + } else { + g_autoptr(GPtrArray) components_with_id = NULL; + *inout_components_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); +- components_with_id = xb_silo_query (self->silo, "components/component/id", 0, &error_local); ++ components_with_id = xb_silo_query (silo, "components/component/id", 0, &error_local); + if (components_with_id == NULL) { + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + return TRUE; +@@ -3686,7 +3699,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + g_autoptr(GPtrArray) bundles = NULL; + + *inout_components_by_bundle = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); +- bundles = xb_silo_query (self->silo, "/components/component/bundle[@type='flatpak']", 0, NULL); ++ bundles = xb_silo_query (silo, "/components/component/bundle[@type='flatpak']", 0, NULL); + for (guint b = 0; bundles != NULL && b < bundles->len; b++) { + XbNode *bundle_node = g_ptr_array_index (bundles, b); + g_autoptr(XbNode) component_node = xb_node_get_parent (bundle_node); +@@ -3709,7 +3722,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + g_autoptr(GsApp) new = NULL; + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardCreateAppstreamApp, "Flatpak (create Appstream app)", NULL); +- new = gs_appstream_create_app (self->plugin, self->silo, component, self->silo_filename ? self->silo_filename : "", ++ new = gs_appstream_create_app (self->plugin, silo, component, silo_filename ? silo_filename : "", + self->scope, error); + GS_PROFILER_END_SCOPED (FlatpakRefineWildcardCreateAppstreamApp); + +@@ -3761,7 +3774,8 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + g_debug ("Failed to get ref info for '%s' from wildcard '%s', skipping it...", gs_app_get_id (new), id); + } else { + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardRefineNewApp, "Flatpak (refine new app)", NULL); +- if (!gs_flatpak_refine_app_unlocked (self, new, refine_flags, interactive, FALSE, *inout_components_by_bundle, &locker, cancellable, error)) ++ if (!gs_flatpak_refine_app_internal (self, new, refine_flags, interactive, FALSE, *inout_components_by_bundle, ++ silo, silo_filename, silo_installed_by_desktopid, cancellable, error)) + return FALSE; + GS_PROFILER_END_SCOPED (FlatpakRefineWildcardRefineNewApp); + +@@ -3894,10 +3908,18 @@ gs_flatpak_file_to_app_bundle (GsFlatpak *self, + /* load AppStream */ + appstream_gz = flatpak_bundle_ref_get_appstream (xref_bundle); + if (appstream_gz != NULL) { ++ g_autofree gchar *silo_filename = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autoptr(XbSilo) tmp_silo = NULL; ++ ++ tmp_silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); ++ if (tmp_silo == NULL) ++ return NULL; + if (!gs_flatpak_refine_appstream_from_bytes (self, app, NULL, NULL, + appstream_gz, + GS_PLUGIN_REFINE_FLAGS_REQUIRE_ID, + interactive, ++ silo_filename, silo_installed_by_desktopid, + cancellable, error)) + return NULL; + } else { +@@ -3995,6 +4017,9 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self, + g_autoptr(GsApp) app = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbSilo) silo = NULL; ++ g_autoptr(XbSilo) tmp_silo = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autofree gchar *silo_filename = NULL; + g_autofree gchar *origin_url = NULL; + g_autofree gchar *ref_comment = NULL; + g_autofree gchar *ref_description = NULL; +@@ -4274,8 +4299,12 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self, + g_debug ("showing AppStream data: %s", xml); + } + ++ tmp_silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); ++ if (tmp_silo == NULL) ++ return NULL; ++ + /* get extra AppStream data if available */ +- if (!gs_flatpak_refine_appstream (self, app, silo, ++ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, + GS_PLUGIN_REFINE_FLAGS_MASK, + NULL, + interactive, +@@ -4296,17 +4325,16 @@ gs_flatpak_search (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; + g_autoptr(GMutexLocker) app_silo_locker = NULL; + g_autoptr(GPtrArray) silos_to_remove = g_ptr_array_new (); ++ g_autoptr(XbSilo) silo = NULL; + GHashTableIter iter; + gpointer key, value; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_search (self->plugin, self->silo, values, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_search (self->plugin, silo, values, list_tmp, cancellable, error)) + return FALSE; + + gs_flatpak_ensure_remote_title (self, interactive, cancellable); +@@ -4353,8 +4381,8 @@ gs_flatpak_search (GsFlatpak *self, + } + + for (guint i = 0; i < silos_to_remove->len; i++) { +- const char *silo = g_ptr_array_index (silos_to_remove, i); +- g_hash_table_remove (self->app_silos, silo); ++ const char *app_ref = g_ptr_array_index (silos_to_remove, i); ++ g_hash_table_remove (self->app_silos, app_ref); + } + + return TRUE; +@@ -4369,17 +4397,16 @@ gs_flatpak_search_developer_apps (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; + g_autoptr(GMutexLocker) app_silo_locker = NULL; + g_autoptr(GPtrArray) silos_to_remove = g_ptr_array_new (); ++ g_autoptr(XbSilo) silo = NULL; + GHashTableIter iter; + gpointer key, value; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_search_developer_apps (self->plugin, self->silo, values, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_search_developer_apps (self->plugin, silo, values, list_tmp, cancellable, error)) + return FALSE; + + gs_flatpak_ensure_remote_title (self, interactive, cancellable); +@@ -4426,8 +4453,8 @@ gs_flatpak_search_developer_apps (GsFlatpak *self, + } + + for (guint i = 0; i < silos_to_remove->len; i++) { +- const char *silo = g_ptr_array_index (silos_to_remove, i); +- g_hash_table_remove (self->app_silos, silo); ++ const char *app_ref = g_ptr_array_index (silos_to_remove, i); ++ g_hash_table_remove (self->app_silos, app_ref); + } + + return TRUE; +@@ -4441,14 +4468,12 @@ gs_flatpak_add_category_apps (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_appstream_add_category_apps (self->plugin, self->silo, +- category, list, +- cancellable, error); ++ return gs_appstream_add_category_apps (self->plugin, silo, category, list, cancellable, error); + } + + gboolean +@@ -4458,12 +4483,12 @@ gs_flatpak_refine_category_sizes (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_appstream_refine_category_sizes (self->silo, list, cancellable, error); ++ return gs_appstream_refine_category_sizes (silo, list, cancellable, error); + } + + gboolean +@@ -4474,13 +4499,12 @@ gs_flatpak_add_popular (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_popular (self->silo, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_add_popular (silo, list_tmp, cancellable, error)) + return FALSE; + + gs_app_list_add_list (list, list_tmp); +@@ -4496,13 +4520,12 @@ gs_flatpak_add_featured (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_featured (self->silo, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_add_featured (silo, list_tmp, cancellable, error)) + return FALSE; + + gs_app_list_add_list (list, list_tmp); +@@ -4518,12 +4541,12 @@ gs_flatpak_add_deployment_featured (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_appstream_add_deployment_featured (self->silo, deployments, list, cancellable, error); ++ return gs_appstream_add_deployment_featured (silo, deployments, list, cancellable, error); + } + + gboolean +@@ -4535,13 +4558,12 @@ gs_flatpak_add_alternates (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_alternates (self->silo, app, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_add_alternates (silo, app, list_tmp, cancellable, error)) + return FALSE; + + gs_app_list_add_list (list, list_tmp); +@@ -4558,13 +4580,12 @@ gs_flatpak_add_recent (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_recent (self->plugin, self->silo, list_tmp, age, +- cancellable, error)) ++ if (!gs_appstream_add_recent (self->plugin, silo, list_tmp, age, cancellable, error)) + return FALSE; + + gs_flatpak_claim_app_list (self, list_tmp, interactive); +@@ -4582,12 +4603,12 @@ gs_flatpak_url_to_app (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_url_to_app (self->plugin, self->silo, list_tmp, url, cancellable, error)) ++ if (!gs_appstream_url_to_app (self->plugin, silo, list_tmp, url, cancellable, error)) + return FALSE; + + gs_flatpak_claim_app_list (self, list_tmp, interactive); +@@ -4638,10 +4659,8 @@ gs_flatpak_finalize (GObject *object) + g_signal_handler_disconnect (self->monitor, self->changed_id); + self->changed_id = 0; + } +- if (self->silo != NULL) +- g_object_unref (self->silo); +- if (self->monitor != NULL) +- g_object_unref (self->monitor); ++ g_clear_object (&self->silo); ++ g_clear_object (&self->monitor); + g_clear_pointer (&self->silo_filename, g_free); + g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); + +@@ -4654,7 +4673,7 @@ gs_flatpak_finalize (GObject *object) + g_object_unref (self->plugin); + g_hash_table_unref (self->broken_remotes); + g_mutex_clear (&self->broken_remotes_mutex); +- g_rw_lock_clear (&self->silo_lock); ++ g_mutex_clear (&self->silo_lock); + g_hash_table_unref (self->app_silos); + g_mutex_clear (&self->app_silos_mutex); + g_clear_pointer (&self->remote_title, g_hash_table_unref); +@@ -4675,7 +4694,7 @@ gs_flatpak_init (GsFlatpak *self) + { + /* XbSilo needs external locking as we destroy the silo and build a new + * one when something changes */ +- g_rw_lock_init (&self->silo_lock); ++ g_mutex_init (&self->silo_lock); + + g_mutex_init (&self->installed_refs_mutex); + self->installed_refs = NULL; +-- +GitLab + diff --git a/gnome-software.spec b/gnome-software.spec index d744445..5b8b2b4 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -26,13 +26,15 @@ Name: gnome-software Version: 47.2 -Release: 2%{?dist} +Release: 3%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later URL: https://apps.gnome.org/Software Source0: https://download.gnome.org/sources/gnome-software/47/%{name}-%{tarball_version}.tar.xz +Patch01: 0001-crash-under-gs_appstream_gather_merge_data.patch + # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 %if 0%{?fedora} || 0%{?rhel} >= 10 @@ -288,6 +290,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Mon Dec 09 2024 Milan Crha - 47.2-3 +- Resolves: #2272232 (Crash under gs_appstream_gather_merge_data()) + * Thu Dec 05 2024 Yaakov Selkowitz - 47.2-2 - Rebuild for fwupd 2.0 From 27805dcecbe0166032de3430d33fcca2ecec26af Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 7 Jan 2025 08:57:23 +0100 Subject: [PATCH 10/44] Update to 48.alpha2 --- ...under-gs_appstream_gather_merge_data.patch | 1468 ----------------- gnome-software.spec | 13 +- sources | 2 +- 3 files changed, 8 insertions(+), 1475 deletions(-) delete mode 100644 0001-crash-under-gs_appstream_gather_merge_data.patch diff --git a/0001-crash-under-gs_appstream_gather_merge_data.patch b/0001-crash-under-gs_appstream_gather_merge_data.patch deleted file mode 100644 index b52cefd..0000000 --- a/0001-crash-under-gs_appstream_gather_merge_data.patch +++ /dev/null @@ -1,1468 +0,0 @@ -From f1140fd98666fbdb35dfe13a6b3f58fedcb1ad0b Mon Sep 17 00:00:00 2001 -Date: Mon, 29 Apr 2024 17:37:57 +0200 -Subject: [PATCH 1/3] gs-plugin-appstream: Simplify XbSilo locking - -Instead of holding RW lock on the internal silo variable, simply return -a reference to it and work with it like with an immutable object. That -simplifies locking and makes the code cleaner. It can also make the code -quicker, in some cases. ---- - plugins/core/gs-plugin-appstream.c | 194 ++++++++++++++++------------- - 1 file changed, 107 insertions(+), 87 deletions(-) - -diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c -index 7ccbac12a5..5749a175f3 100644 ---- a/plugins/core/gs-plugin-appstream.c -+++ b/plugins/core/gs-plugin-appstream.c -@@ -36,7 +36,7 @@ struct _GsPluginAppstream - GsWorkerThread *worker; /* (owned) */ - - XbSilo *silo; -- GRWLock silo_lock; -+ GMutex silo_lock; - gchar *silo_filename; - GHashTable *silo_installed_by_desktopid; - GHashTable *silo_installed_by_id; -@@ -65,7 +65,7 @@ gs_plugin_appstream_dispose (GObject *object) - g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); - g_clear_pointer (&self->silo_installed_by_id, g_hash_table_unref); - g_clear_object (&self->settings); -- g_rw_lock_clear (&self->silo_lock); -+ g_mutex_clear (&self->silo_lock); - g_clear_object (&self->worker); - g_clear_pointer (&self->file_monitors, g_ptr_array_unref); - -@@ -77,9 +77,7 @@ gs_plugin_appstream_init (GsPluginAppstream *self) - { - GApplication *application = g_application_get_default (); - -- /* XbSilo needs external locking as we destroy the silo and build a new -- * one when something changes */ -- g_rw_lock_init (&self->silo_lock); -+ g_mutex_init (&self->silo_lock); - - /* require settings */ - self->settings = g_settings_new ("org.gnome.software"); -@@ -530,10 +528,13 @@ gs_add_appstream_metainfo_location (GPtrArray *locations, const gchar *root) - g_build_filename (root, "appdata", NULL)); - } - --static gboolean --gs_plugin_appstream_check_silo (GsPluginAppstream *self, -- GCancellable *cancellable, -- GError **error) -+static XbSilo * -+gs_plugin_appstream_ref_silo (GsPluginAppstream *self, -+ gchar **out_silo_filename, -+ GHashTable **out_silo_installed_by_desktopid, -+ GHashTable **out_silo_installed_by_id, -+ GCancellable *cancellable, -+ GError **error) - { - const gchar *test_xml; - g_autofree gchar *blobfn = NULL; -@@ -541,21 +542,25 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - g_autoptr(XbNode) n = NULL; - g_autoptr(GFile) file = NULL; - g_autoptr(GPtrArray) installed = NULL; -- g_autoptr(GRWLockReaderLocker) reader_locker = NULL; -- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; -+ g_autoptr(GMutexLocker) locker = NULL; - g_autoptr(GPtrArray) parent_appdata = g_ptr_array_new_with_free_func (g_free); - g_autoptr(GPtrArray) parent_appstream = NULL; - g_autoptr(GMainContext) old_thread_default = NULL; - -- reader_locker = g_rw_lock_reader_locker_new (&self->silo_lock); -+ locker = g_mutex_locker_new (&self->silo_lock); - /* everything is okay */ - if (self->silo != NULL && xb_silo_is_valid (self->silo) && -- g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) -- return TRUE; -- g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); -+ g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) { -+ if (out_silo_filename != NULL) -+ *out_silo_filename = g_strdup (self->silo_filename); -+ if (out_silo_installed_by_desktopid != NULL) -+ *out_silo_installed_by_desktopid = self->silo_installed_by_desktopid ? g_hash_table_ref (self->silo_installed_by_desktopid) : NULL; -+ if (out_silo_installed_by_id != NULL) -+ *out_silo_installed_by_id = self->silo_installed_by_id ? g_hash_table_ref (self->silo_installed_by_id) : NULL; -+ return g_object_ref (self->silo); -+ } - - /* drat! silo needs regenerating */ -- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); - reload: - g_clear_object (&self->silo); - g_clear_pointer (&self->silo_filename, g_free); -@@ -594,7 +599,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - if (!xb_builder_source_load_xml (source, test_xml, - XB_BUILDER_SOURCE_FLAG_NONE, - error)) -- return FALSE; -+ return NULL; - fixup1 = xb_builder_fixup_new ("AddOriginKeywords", - gs_plugin_appstream_add_origin_keyword_cb, - self, NULL); -@@ -640,7 +645,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - if (!gs_plugin_appstream_load_appstream (self, builder, fn, cancellable, error)) { - if (old_thread_default != NULL) - g_main_context_push_thread_default (old_thread_default); -- return FALSE; -+ return NULL; - } - } - for (guint i = 0; i < parent_appdata->len; i++) { -@@ -648,7 +653,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - if (!gs_plugin_appstream_load_appdata (self, builder, fn, cancellable, error)) { - if (old_thread_default != NULL) - g_main_context_push_thread_default (old_thread_default); -- return FALSE; -+ return NULL; - } - } - for (guint i = 0; i < parent_desktop->len; i++) { -@@ -657,7 +662,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - if (!gs_appstream_load_desktop_files (builder, dir, NULL, &file_monitor, cancellable, error)) { - if (old_thread_default != NULL) - g_main_context_push_thread_default (old_thread_default); -- return FALSE; -+ return NULL; - } - gs_plugin_appstream_maybe_store_file_monitor (self, file_monitor); - } -@@ -678,7 +683,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - GS_UTILS_CACHE_FLAG_CREATE_DIRECTORY, - error); - if (blobfn == NULL) -- return FALSE; -+ return NULL; - file = g_file_new_for_path (blobfn); - g_debug ("ensuring %s", blobfn); - -@@ -696,7 +701,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - if (self->silo == NULL) { - if (old_thread_default != NULL) - g_main_context_push_thread_default (old_thread_default); -- return FALSE; -+ return NULL; - } - - if (old_thread_default != NULL) -@@ -717,7 +722,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - GS_PLUGIN_ERROR, - GS_PLUGIN_ERROR_NOT_SUPPORTED, - "No AppStream data found"); -- return FALSE; -+ return NULL; - } - - g_clear_object (&n); -@@ -768,7 +773,13 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, - } - - /* success */ -- return TRUE; -+ if (out_silo_filename != NULL) -+ *out_silo_filename = g_strdup (self->silo_filename); -+ if (out_silo_installed_by_desktopid != NULL) -+ *out_silo_installed_by_desktopid = self->silo_installed_by_desktopid ? g_hash_table_ref (self->silo_installed_by_desktopid) : NULL; -+ if (out_silo_installed_by_id != NULL) -+ *out_silo_installed_by_id = self->silo_installed_by_id ? g_hash_table_ref (self->silo_installed_by_id) : NULL; -+ return g_object_ref (self->silo); - } - - static void -@@ -776,7 +787,6 @@ gs_plugin_appstream_reload (GsPlugin *plugin) - { - GsPluginAppstream *self; - g_autoptr(GsAppList) list = NULL; -- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; - guint sz; - - g_return_if_fail (GS_IS_PLUGIN_APPSTREAM (plugin)); -@@ -790,9 +800,8 @@ gs_plugin_appstream_reload (GsPlugin *plugin) - } - - self = GS_PLUGIN_APPSTREAM (plugin); -- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); -- if (self->silo != NULL) -- xb_silo_invalidate (self->silo); -+ /* simpler than getting the silo and setting it invalid */ -+ g_atomic_int_inc (&self->file_monitor_stamp); - } - - static gint -@@ -834,11 +843,13 @@ setup_thread_cb (GTask *task, - GCancellable *cancellable) - { - GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); -+ g_autoptr(XbSilo) silo = NULL; - g_autoptr(GError) local_error = NULL; - - assert_in_worker (self); - -- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) -+ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); -+ if (silo == NULL) - g_task_return_error (task, g_steal_pointer (&local_error)); - else - g_task_return_boolean (task, TRUE); -@@ -910,21 +921,21 @@ url_to_app_thread_cb (GTask *task, - GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); - GsPluginUrlToAppData *data = task_data; - g_autoptr(GsAppList) list = NULL; -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - g_autoptr(GError) local_error = NULL; - - assert_in_worker (self); - - /* check silo is valid */ -- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { -+ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); -+ if (silo == NULL) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); - list = gs_app_list_new (); - -- if (gs_appstream_url_to_app (GS_PLUGIN (self), self->silo, list, data->url, cancellable, &local_error)) -+ if (gs_appstream_url_to_app (GS_PLUGIN (self), silo, list, data->url, cancellable, &local_error)) - g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); - else - g_task_return_error (task, g_steal_pointer (&local_error)); -@@ -1006,17 +1017,14 @@ gs_plugin_appstream_set_compulsory_quirk (GsApp *app, XbNode *component) - static gboolean - gs_plugin_appstream_refine_state (GsPluginAppstream *self, - GsApp *app, -+ GHashTable *silo_installed_by_id, - GError **error) - { -- g_autoptr(GRWLockReaderLocker) locker = NULL; -- - /* Ignore apps with no ID */ -- if (gs_app_get_id (app) == NULL) -+ if (gs_app_get_id (app) == NULL || silo_installed_by_id == NULL) - return TRUE; - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- -- if (g_hash_table_contains (self->silo_installed_by_id, gs_app_get_id (app))) -+ if (g_hash_table_contains (silo_installed_by_id, gs_app_get_id (app))) - gs_app_set_state (app, GS_APP_STATE_INSTALLED); - return TRUE; - } -@@ -1027,20 +1035,21 @@ gs_plugin_refine_from_id (GsPluginAppstream *self, - GsPluginRefineFlags flags, - GHashTable *apps_by_id, - GHashTable *apps_by_origin_and_id, -+ XbSilo *silo, -+ const gchar *silo_filename, -+ GHashTable *silo_installed_by_desktopid, -+ GHashTable *silo_installed_by_id, - gboolean *found, - GError **error) - { - const gchar *id, *origin; - GPtrArray *components; -- g_autoptr(GRWLockReaderLocker) locker = NULL; - - /* not enough info to find */ - id = gs_app_get_id (app); - if (id == NULL) - return TRUE; - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- - origin = gs_app_get_origin_appstream (app); - - /* look in AppStream then fall back to AppData */ -@@ -1056,15 +1065,15 @@ gs_plugin_refine_from_id (GsPluginAppstream *self, - - for (guint i = 0; i < components->len; i++) { - XbNode *component = g_ptr_array_index (components, i); -- if (!gs_appstream_refine_app (GS_PLUGIN (self), app, self->silo, component, flags, self->silo_installed_by_desktopid, -- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) -+ if (!gs_appstream_refine_app (GS_PLUGIN (self), app, silo, component, flags, silo_installed_by_desktopid, -+ silo_filename ? silo_filename : "", self->default_scope, error)) - return FALSE; - gs_plugin_appstream_set_compulsory_quirk (app, component); - } - - /* if an installed desktop or appdata file exists set to installed */ - if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) { -- if (!gs_plugin_appstream_refine_state (self, app, error)) -+ if (!gs_plugin_appstream_refine_state (self, app, silo_installed_by_id, error)) - return FALSE; - } - -@@ -1077,6 +1086,10 @@ static gboolean - gs_plugin_refine_from_pkgname (GsPluginAppstream *self, - GsApp *app, - GsPluginRefineFlags flags, -+ XbSilo *silo, -+ const gchar *silo_filename, -+ GHashTable *silo_installed_by_desktopid, -+ GHashTable *silo_installed_by_id, - GError **error) - { - GPtrArray *sources = gs_app_get_sources (app); -@@ -1089,33 +1102,30 @@ gs_plugin_refine_from_pkgname (GsPluginAppstream *self, - /* find all apps when matching any prefixes */ - for (guint j = 0; j < sources->len; j++) { - const gchar *pkgname = g_ptr_array_index (sources, j); -- g_autoptr(GRWLockReaderLocker) locker = NULL; - g_autoptr(GString) xpath = g_string_new (NULL); - g_autoptr(XbNode) component = NULL; - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- - /* prefer actual apps and then fallback to anything else */ - xb_string_append_union (xpath, "components/component[@type='desktop-application']/pkgname[text()='%s']/..", pkgname); - xb_string_append_union (xpath, "components/component[@type='console-application']/pkgname[text()='%s']/..", pkgname); - xb_string_append_union (xpath, "components/component[@type='web-application']/pkgname[text()='%s']/..", pkgname); - xb_string_append_union (xpath, "components/component/pkgname[text()='%s']/..", pkgname); -- component = xb_silo_query_first (self->silo, xpath->str, &error_local); -+ component = xb_silo_query_first (silo, xpath->str, &error_local); - if (component == NULL) { - if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - continue; - g_propagate_error (error, g_steal_pointer (&error_local)); - return FALSE; - } -- if (!gs_appstream_refine_app (GS_PLUGIN (self), app, self->silo, component, flags, self->silo_installed_by_desktopid, -- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) -+ if (!gs_appstream_refine_app (GS_PLUGIN (self), app, silo, component, flags, silo_installed_by_desktopid, -+ silo_filename ? silo_filename : "", self->default_scope, error)) - return FALSE; - gs_plugin_appstream_set_compulsory_quirk (app, component); - } - - /* if an installed desktop or appdata file exists set to installed */ - if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) { -- if (!gs_plugin_appstream_refine_state (self, app, error)) -+ if (!gs_plugin_appstream_refine_state (self, app, silo_installed_by_id, error)) - return FALSE; - } - -@@ -1153,6 +1163,10 @@ static gboolean refine_wildcard (GsPluginAppstream *self, - GsAppList *list, - GsPluginRefineFlags refine_flags, - GHashTable *apps_by_id, -+ XbSilo *silo, -+ const gchar *silo_filename, -+ GHashTable *silo_installed_by_desktopid, -+ GHashTable *silo_installed_by_id, - GCancellable *cancellable, - GError **error); - -@@ -1171,13 +1185,17 @@ refine_thread_cb (GTask *task, - g_autoptr(GHashTable) apps_by_id = NULL; - g_autoptr(GHashTable) apps_by_origin_and_id = NULL; - g_autoptr(GPtrArray) components = NULL; -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; -+ g_autofree gchar *silo_filename = NULL; -+ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; -+ g_autoptr(GHashTable) silo_installed_by_id = NULL; - g_autoptr(GError) local_error = NULL; - - assert_in_worker (self); - - /* check silo is valid */ -- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { -+ silo = gs_plugin_appstream_ref_silo (self, &silo_filename, &silo_installed_by_desktopid, &silo_installed_by_id, cancellable, &local_error); -+ if (silo == NULL) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } -@@ -1185,9 +1203,7 @@ refine_thread_cb (GTask *task, - apps_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); - apps_by_origin_and_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- -- components = xb_silo_query (self->silo, "components/component/id", 0, NULL); -+ components = xb_silo_query (silo, "components/component/id", 0, NULL); - for (guint i = 0; components != NULL && i < components->len; i++) { - XbNode *node = g_ptr_array_index (components, i); - g_autoptr(XbNode) component_node = xb_node_get_parent (node); -@@ -1234,7 +1250,7 @@ refine_thread_cb (GTask *task, - } - - g_clear_pointer (&components, g_ptr_array_unref); -- components = xb_silo_query (self->silo, "component/id", 0, NULL); -+ components = xb_silo_query (silo, "component/id", 0, NULL); - for (guint i = 0; components != NULL && i < components->len; i++) { - XbNode *node = g_ptr_array_index (components, i); - g_autoptr(XbNode) component_node = xb_node_get_parent (node); -@@ -1262,12 +1278,14 @@ refine_thread_cb (GTask *task, - continue; - - /* find by ID then fall back to package name */ -- if (!gs_plugin_refine_from_id (self, app, flags, apps_by_id, apps_by_origin_and_id, &found, &local_error)) { -+ if (!gs_plugin_refine_from_id (self, app, flags, apps_by_id, apps_by_origin_and_id, silo, silo_filename, -+ silo_installed_by_desktopid, silo_installed_by_id, &found, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - if (!found) { -- if (!gs_plugin_refine_from_pkgname (self, app, flags, &local_error)) { -+ if (!gs_plugin_refine_from_pkgname (self, app, flags, silo, silo_filename, -+ silo_installed_by_desktopid, silo_installed_by_id, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } -@@ -1286,7 +1304,8 @@ refine_thread_cb (GTask *task, - GsApp *app = gs_app_list_index (app_list, j); - - if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && -- !refine_wildcard (self, app, list, flags, apps_by_id, cancellable, &local_error)) { -+ !refine_wildcard (self, app, list, flags, apps_by_id, silo, silo_filename, -+ silo_installed_by_desktopid, silo_installed_by_id,cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } -@@ -1311,20 +1330,21 @@ refine_wildcard (GsPluginAppstream *self, - GsAppList *list, - GsPluginRefineFlags refine_flags, - GHashTable *apps_by_id, -+ XbSilo *silo, -+ const gchar *silo_filename, -+ GHashTable *silo_installed_by_desktopid, -+ GHashTable *silo_installed_by_id, - GCancellable *cancellable, - GError **error) - { - const gchar *id; - GPtrArray *components; -- g_autoptr(GRWLockReaderLocker) locker = NULL; - - /* not enough info to find */ - id = gs_app_get_id (app); - if (id == NULL) - return TRUE; - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- - components = g_hash_table_lookup (apps_by_id, id); - if (components == NULL) - return TRUE; -@@ -1333,20 +1353,20 @@ refine_wildcard (GsPluginAppstream *self, - g_autoptr(GsApp) new = NULL; - - /* new app */ -- new = gs_appstream_create_app (GS_PLUGIN (self), self->silo, component, self->silo_filename ? self->silo_filename : "", -+ new = gs_appstream_create_app (GS_PLUGIN (self), silo, component, silo_filename ? silo_filename : "", - self->default_scope, error); - if (new == NULL) - return FALSE; - gs_app_set_scope (new, AS_COMPONENT_SCOPE_SYSTEM); - gs_app_subsume_metadata (new, app); -- if (!gs_appstream_refine_app (GS_PLUGIN (self), new, self->silo, component, refine_flags, self->silo_installed_by_desktopid, -- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) -+ if (!gs_appstream_refine_app (GS_PLUGIN (self), new, silo, component, refine_flags, silo_installed_by_desktopid, -+ silo_filename ? silo_filename : "", self->default_scope, error)) - return FALSE; - gs_plugin_appstream_set_compulsory_quirk (new, component); - - /* if an installed desktop or appdata file exists set to installed */ - if (gs_app_get_state (new) == GS_APP_STATE_UNKNOWN) { -- if (!gs_plugin_appstream_refine_state (self, new, error)) -+ if (!gs_plugin_appstream_refine_state (self, new, silo_installed_by_id, error)) - return FALSE; - } - -@@ -1398,21 +1418,20 @@ refine_categories_thread_cb (GTask *task, - GCancellable *cancellable) - { - GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - GsPluginRefineCategoriesData *data = task_data; - g_autoptr(GError) local_error = NULL; - - assert_in_worker (self); - - /* check silo is valid */ -- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { -+ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); -+ if (silo == NULL) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- -- if (!gs_appstream_refine_category_sizes (self->silo, data->list, cancellable, &local_error)) { -+ if (!gs_appstream_refine_category_sizes (silo, data->list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } -@@ -1462,7 +1481,7 @@ list_apps_thread_cb (GTask *task, - GCancellable *cancellable) - { - GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - g_autoptr(GsAppList) list = gs_app_list_new (); - GsPluginListAppsData *data = task_data; - GDateTime *released_since = NULL; -@@ -1516,65 +1535,64 @@ list_apps_thread_cb (GTask *task, - } - - /* check silo is valid */ -- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { -+ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); -+ if (silo == NULL) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - -- locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- - if (released_since != NULL && -- !gs_appstream_add_recent (GS_PLUGIN (self), self->silo, list, age_secs, -+ !gs_appstream_add_recent (GS_PLUGIN (self), silo, list, age_secs, - cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (is_curated != GS_APP_QUERY_TRISTATE_UNSET && -- !gs_appstream_add_popular (self->silo, list, cancellable, &local_error)) { -+ !gs_appstream_add_popular (silo, list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (is_featured != GS_APP_QUERY_TRISTATE_UNSET && -- !gs_appstream_add_featured (self->silo, list, cancellable, &local_error)) { -+ !gs_appstream_add_featured (silo, list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (category != NULL && -- !gs_appstream_add_category_apps (GS_PLUGIN (self), self->silo, category, list, cancellable, &local_error)) { -+ !gs_appstream_add_category_apps (GS_PLUGIN (self), silo, category, list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (is_installed == GS_APP_QUERY_TRISTATE_TRUE && -- !gs_appstream_add_installed (GS_PLUGIN (self), self->silo, list, cancellable, &local_error)) { -+ !gs_appstream_add_installed (GS_PLUGIN (self), silo, list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (deployment_featured != NULL && -- !gs_appstream_add_deployment_featured (self->silo, deployment_featured, list, -+ !gs_appstream_add_deployment_featured (silo, deployment_featured, list, - cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (developers != NULL && -- !gs_appstream_search_developer_apps (GS_PLUGIN (self), self->silo, developers, list, cancellable, &local_error)) { -+ !gs_appstream_search_developer_apps (GS_PLUGIN (self), silo, developers, list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (keywords != NULL && -- !gs_appstream_search (GS_PLUGIN (self), self->silo, keywords, list, cancellable, &local_error)) { -+ !gs_appstream_search (GS_PLUGIN (self), silo, keywords, list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } - - if (alternate_of != NULL && -- !gs_appstream_add_alternates (self->silo, alternate_of, list, cancellable, &local_error)) { -+ !gs_appstream_add_alternates (silo, alternate_of, list, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } -@@ -1623,12 +1641,14 @@ refresh_metadata_thread_cb (GTask *task, - GCancellable *cancellable) - { - GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); -+ g_autoptr(XbSilo) silo = NULL; - g_autoptr(GError) local_error = NULL; - - assert_in_worker (self); - - /* Checking the silo will refresh it if needed. */ -- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) -+ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); -+ if (silo == NULL) - g_task_return_error (task, g_steal_pointer (&local_error)); - else - g_task_return_boolean (task, TRUE); --- -GitLab - - -From cf04c32b5fb1fd267096fad8dd11a22429ad788a Mon Sep 17 00:00:00 2001 -Date: Mon, 29 Apr 2024 17:45:08 +0200 -Subject: [PATCH 2/3] gs-plugin-appstream: Rename file_monitor_stamp to - silo_change_stamp - -Since it's used also in the gs_plugin_appstream_reload() and works -like a change indicator, name it appropriately to avoid confusion. ---- - plugins/core/gs-plugin-appstream.c | 16 ++++++++-------- - 1 file changed, 8 insertions(+), 8 deletions(-) - -diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c -index 5749a175f3..99fe5a1432 100644 ---- a/plugins/core/gs-plugin-appstream.c -+++ b/plugins/core/gs-plugin-appstream.c -@@ -46,8 +46,8 @@ struct _GsPluginAppstream - GPtrArray *file_monitors; /* (owned) (element-type GFileMonitor) */ - /* The stamps help to avoid locking the silo lock in the main thread - and also to detect changes while loading other appstream data. */ -- gint file_monitor_stamp; /* the file monitor stamp, increased on every file monitor change */ -- gint file_monitor_stamp_current; /* the currently known file monitor stamp, checked for changes */ -+ gint silo_change_stamp; /* the silo change stamp, increased on every silo change */ -+ gint silo_change_stamp_current; /* the currently known silo change stamp, checked for changes */ - }; - - G_DEFINE_TYPE (GsPluginAppstream, gs_plugin_appstream, GS_TYPE_PLUGIN) -@@ -224,7 +224,7 @@ gs_plugin_appstream_file_monitor_changed_cb (GFileMonitor *monitor, - gpointer user_data) - { - GsPluginAppstream *self = user_data; -- g_atomic_int_inc (&self->file_monitor_stamp); -+ g_atomic_int_inc (&self->silo_change_stamp); - } - - static void -@@ -550,7 +550,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, - locker = g_mutex_locker_new (&self->silo_lock); - /* everything is okay */ - if (self->silo != NULL && xb_silo_is_valid (self->silo) && -- g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) { -+ g_atomic_int_get (&self->silo_change_stamp_current) == g_atomic_int_get (&self->silo_change_stamp)) { - if (out_silo_filename != NULL) - *out_silo_filename = g_strdup (self->silo_filename); - if (out_silo_installed_by_desktopid != NULL) -@@ -568,7 +568,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, - g_clear_pointer (&blobfn, g_free); - self->default_scope = AS_COMPONENT_SCOPE_UNKNOWN; - g_ptr_array_set_size (self->file_monitors, 0); -- g_atomic_int_set (&self->file_monitor_stamp_current, g_atomic_int_get (&self->file_monitor_stamp)); -+ g_atomic_int_set (&self->silo_change_stamp_current, g_atomic_int_get (&self->silo_change_stamp)); - - /* FIXME: https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1422 */ - old_thread_default = g_main_context_ref_thread_default (); -@@ -707,7 +707,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, - if (old_thread_default != NULL) - g_main_context_push_thread_default (old_thread_default); - -- if (g_atomic_int_get (&self->file_monitor_stamp_current) != g_atomic_int_get (&self->file_monitor_stamp)) { -+ if (g_atomic_int_get (&self->silo_change_stamp_current) != g_atomic_int_get (&self->silo_change_stamp)) { - g_ptr_array_set_size (parent_appdata, 0); - g_ptr_array_set_size (parent_appstream, 0); - g_debug ("appstream: File monitors reported change while loading appstream data, reloading..."); -@@ -800,8 +800,8 @@ gs_plugin_appstream_reload (GsPlugin *plugin) - } - - self = GS_PLUGIN_APPSTREAM (plugin); -- /* simpler than getting the silo and setting it invalid */ -- g_atomic_int_inc (&self->file_monitor_stamp); -+ /* Invalidate the reference to the current silo */ -+ g_atomic_int_inc (&self->silo_change_stamp); - } - - static gint --- -GitLab - - -From 59b301b8a878f3e81359909a8d4107715eb60a35 Mon Sep 17 00:00:00 2001 -Date: Tue, 30 Apr 2024 13:13:36 +0200 -Subject: [PATCH 3/3] flatpak: Simplify XbSilo locking - -Instead of holding RW lock on the internal silo variable, simply return -a reference to it and work with it like with an immutable object. That -simplifies locking and makes the code cleaner. It can also make the code -quicker, in some cases. ---- - plugins/flatpak/gs-flatpak.c | 307 +++++++++++++++++++---------------- - 1 file changed, 163 insertions(+), 144 deletions(-) - -diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c -index 2e175fd08e..9893cf88b7 100644 ---- a/plugins/flatpak/gs-flatpak.c -+++ b/plugins/flatpak/gs-flatpak.c -@@ -54,9 +54,11 @@ struct _GsFlatpak { - AsComponentScope scope; - GsPlugin *plugin; - XbSilo *silo; -- GRWLock silo_lock; -+ GMutex silo_lock; - gchar *silo_filename; - GHashTable *silo_installed_by_desktopid; -+ gint silo_change_stamp; -+ gint silo_change_stamp_current; - gchar *id; - guint changed_id; - GHashTable *app_silos; -@@ -654,10 +656,7 @@ gs_flatpak_create_source (GsFlatpak *self, FlatpakRemote *xremote) - static void - gs_flatpak_invalidate_silo (GsFlatpak *self) - { -- g_rw_lock_writer_lock (&self->silo_lock); -- if (self->silo != NULL) -- xb_silo_invalidate (self->silo); -- g_rw_lock_writer_unlock (&self->silo_lock); -+ g_atomic_int_inc (&self->silo_change_stamp); - } - - static void -@@ -1085,32 +1084,39 @@ gs_flatpak_rescan_installed (GsFlatpak *self, - g_debug ("Failed to read flatpak .desktop files in %s: %s", path, error_local->message); - } - --static gboolean --gs_flatpak_rescan_appstream_store (GsFlatpak *self, -- gboolean interactive, -- GCancellable *cancellable, -- GError **error) -+static XbSilo * -+gs_flatpak_ref_silo (GsFlatpak *self, -+ gboolean interactive, -+ gchar **out_silo_filename, -+ GHashTable **out_silo_installed_by_desktopid, -+ GCancellable *cancellable, -+ GError **error) - { - g_autofree gchar *blobfn = NULL; - g_autoptr(GFile) file = NULL; - g_autoptr(GPtrArray) xremotes = NULL; - g_autoptr(GPtrArray) desktop_paths = NULL; -- g_autoptr(GRWLockReaderLocker) reader_locker = NULL; -- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; -+ g_autoptr(GMutexLocker) locker = NULL; - g_autoptr(XbBuilder) builder = NULL; - g_autoptr(GMainContext) old_thread_default = NULL; - -- reader_locker = g_rw_lock_reader_locker_new (&self->silo_lock); -+ locker = g_mutex_locker_new (&self->silo_lock); - /* everything is okay */ -- if (self->silo != NULL && xb_silo_is_valid (self->silo)) -- return TRUE; -- g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); -+ if (self->silo != NULL && xb_silo_is_valid (self->silo) && -+ g_atomic_int_get (&self->silo_change_stamp_current) == g_atomic_int_get (&self->silo_change_stamp)) { -+ if (out_silo_filename != NULL) -+ *out_silo_filename = g_strdup (self->silo_filename); -+ if (out_silo_installed_by_desktopid != NULL && self->silo_installed_by_desktopid) -+ *out_silo_installed_by_desktopid = g_hash_table_ref (self->silo_installed_by_desktopid); -+ return g_object_ref (self->silo); -+ } - - /* drat! silo needs regenerating */ -- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); -+ reload: - g_clear_object (&self->silo); - g_clear_pointer (&self->silo_filename, g_free); - g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); -+ g_atomic_int_set (&self->silo_change_stamp_current, g_atomic_int_get (&self->silo_change_stamp)); - - /* FIXME: https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1422 */ - old_thread_default = g_main_context_ref_thread_default (); -@@ -1138,7 +1144,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, - error); - if (xremotes == NULL) { - gs_flatpak_error_convert (error); -- return FALSE; -+ return NULL; - } - for (guint i = 0; i < xremotes->len; i++) { - g_autoptr(GError) error_local = NULL; -@@ -1152,7 +1158,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, - flatpak_remote_get_name (xremote), error_local->message); - if (g_cancellable_set_error_if_cancelled (cancellable, error)) { - gs_flatpak_error_convert (error); -- return FALSE; -+ return NULL; - } - } - } -@@ -1176,7 +1182,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, - GS_UTILS_CACHE_FLAG_CREATE_DIRECTORY, - error); - if (blobfn == NULL) -- return FALSE; -+ return NULL; - file = g_file_new_for_path (blobfn); - g_debug ("ensuring %s", blobfn); - -@@ -1195,6 +1201,17 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, - if (old_thread_default != NULL) - g_main_context_push_thread_default (old_thread_default); - -+ if (g_atomic_int_get (&self->silo_change_stamp_current) != g_atomic_int_get (&self->silo_change_stamp)) { -+ g_clear_pointer (&blobfn, g_free); -+ g_clear_pointer (&xremotes, g_ptr_array_unref); -+ g_clear_pointer (&desktop_paths, g_ptr_array_unref); -+ g_clear_pointer (&old_thread_default, g_main_context_unref); -+ g_clear_object (&file); -+ g_clear_object (&builder); -+ g_debug ("flatpak: Reported change while loading appstream data, reloading..."); -+ goto reload; -+ } -+ - if (self->silo != NULL) { - g_autoptr(GPtrArray) installed = NULL; - g_autoptr(XbNode) info_filename = NULL; -@@ -1218,66 +1235,46 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, - info_filename = xb_silo_query_first (self->silo, "/info/filename", NULL); - if (info_filename != NULL) - self->silo_filename = g_strdup (xb_node_get_text (info_filename)); -+ -+ if (out_silo_filename != NULL) -+ *out_silo_filename = g_strdup (self->silo_filename); -+ if (out_silo_installed_by_desktopid != NULL && self->silo_installed_by_desktopid) -+ *out_silo_installed_by_desktopid = g_hash_table_ref (self->silo_installed_by_desktopid); -+ return g_object_ref (self->silo); - } - -- /* success */ -- return self->silo != NULL; -+ return NULL; - } - - static gboolean - gs_flatpak_rescan_app_data (GsFlatpak *self, - gboolean interactive, -+ XbSilo **out_silo, -+ gchar **out_silo_filename, -+ GHashTable **out_silo_installed_by_desktopid, - GCancellable *cancellable, - GError **error) - { -+ g_autoptr(XbSilo) silo = NULL; -+ - if (self->requires_full_rescan) { - gboolean res = gs_flatpak_refresh (self, 60, interactive, cancellable, error); -- if (res) -+ if (res) { - self->requires_full_rescan = FALSE; -- else -+ } else { - gs_flatpak_internal_data_changed (self); -- return res; -+ return res; -+ } - } - -- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { -+ silo = gs_flatpak_ref_silo (self, interactive, out_silo_filename, out_silo_installed_by_desktopid, cancellable, error); -+ if (silo == NULL) { - gs_flatpak_internal_data_changed (self); - return FALSE; - } - -- return TRUE; --} -- --/* Returns with a read lock held on @self->silo_lock on success. -- The *locker should be NULL when being called. */ --static gboolean --ensure_flatpak_silo_with_locker (GsFlatpak *self, -- GRWLockReaderLocker **locker, -- gboolean interactive, -- GCancellable *cancellable, -- GError **error) --{ -- /* should not hold the lock when called */ -- g_return_val_if_fail (*locker == NULL, FALSE); -- -- /* ensure valid */ -- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) -- return FALSE; -- -- *locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- -- while (self->silo == NULL) { -- g_clear_pointer (locker, g_rw_lock_reader_locker_free); -- -- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { -- gs_flatpak_internal_data_changed (self); -- return FALSE; -- } -- -- /* At this point either rescan_appstream_store() returned an error or it successfully -- * initialised self->silo. There is the possibility that another thread will invalidate -- * the silo before we regain the lock. If so, we’ll have to rescan again. */ -- *locker = g_rw_lock_reader_locker_new (&self->silo_lock); -- } -+ if (out_silo != NULL) -+ *out_silo = g_steal_pointer (&silo); - - return TRUE; - } -@@ -1412,6 +1409,7 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, - { - gboolean ret; - g_autoptr(GPtrArray) xremotes = NULL; -+ g_autoptr(XbSilo) silo = NULL; - - /* get remotes */ - xremotes = flatpak_installation_list_remotes (gs_flatpak_get_installation (self, interactive), -@@ -1498,7 +1496,8 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, - } - - /* ensure the AppStream silo is up to date */ -- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { -+ silo = gs_flatpak_ref_silo (self, interactive, NULL, NULL, cancellable, error); -+ if (silo == NULL) { - gs_flatpak_internal_data_changed (self); - return FALSE; - } -@@ -1646,7 +1645,7 @@ gs_flatpak_add_sources (GsFlatpak *self, - FlatpakInstallation *installation = gs_flatpak_get_installation (self, interactive); - - /* refresh */ -- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) - return FALSE; - - /* get installed apps and runtimes */ -@@ -2080,7 +2079,7 @@ gs_flatpak_add_updates (GsFlatpak *self, - FlatpakInstallation *installation = gs_flatpak_get_installation (self, interactive); - - /* ensure valid */ -- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) - return FALSE; - - /* get all the updatable apps and runtimes */ -@@ -2390,10 +2389,8 @@ gs_flatpak_create_fake_ref (GsApp *app, GError **error) - return xref; - } - --/* the _unlocked() version doesn't call gs_flatpak_rescan_app_data, -- * in order to avoid taking the writer lock on self->silo_lock */ - static gboolean --gs_flatpak_refine_app_state_unlocked (GsFlatpak *self, -+gs_flatpak_refine_app_state_internal (GsFlatpak *self, - GsApp *app, - gboolean interactive, - gboolean force_state_update, -@@ -2505,10 +2502,10 @@ gs_flatpak_refine_app_state (GsFlatpak *self, - GError **error) - { - /* ensure valid */ -- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) - return FALSE; - -- return gs_flatpak_refine_app_state_unlocked (self, app, interactive, force_state_update, cancellable, error); -+ return gs_flatpak_refine_app_state_internal (self, app, interactive, force_state_update, cancellable, error); - } - - static GsApp * -@@ -2594,7 +2591,7 @@ gs_flatpak_create_runtime (GsFlatpak *self, - gs_flatpak_app_set_ref_name (app, split[0]); - gs_flatpak_app_set_ref_arch (app, split[1]); - -- if (!gs_flatpak_refine_app_state_unlocked (self, app, interactive, FALSE, NULL, &local_error)) -+ if (!gs_flatpak_refine_app_state_internal (self, app, interactive, FALSE, NULL, &local_error)) - g_debug ("Failed to refine state for runtime '%s': %s", gs_app_get_unique_id (app), local_error->message); - - /* save in the cache */ -@@ -2984,7 +2981,7 @@ gs_plugin_refine_item_size (GsFlatpak *self, - - /* is the app_runtime already installed? */ - app_runtime = gs_app_get_runtime (app); -- if (!gs_flatpak_refine_app_state_unlocked (self, -+ if (!gs_flatpak_refine_app_state_internal (self, - app_runtime, - interactive, - FALSE, -@@ -3096,6 +3093,8 @@ gs_flatpak_refine_appstream_from_bytes (GsFlatpak *self, - GBytes *appstream_gz, - GsPluginRefineFlags flags, - gboolean interactive, -+ const gchar *silo_filename, -+ GHashTable *silo_installed_by_desktopid, - GCancellable *cancellable, - GError **error) - { -@@ -3229,8 +3228,8 @@ gs_flatpak_refine_appstream_from_bytes (GsFlatpak *self, - } - - /* copy details from AppStream to app */ -- if (!gs_appstream_refine_app (self->plugin, app, silo, component_node, flags, self->silo_installed_by_desktopid, -- self->silo_filename ? self->silo_filename : "", self->scope, error)) -+ if (!gs_appstream_refine_app (self->plugin, app, silo, component_node, flags, silo_installed_by_desktopid, -+ silo_filename ? silo_filename : "", self->scope, error)) - return FALSE; - - if (gs_app_get_origin (app)) -@@ -3324,6 +3323,8 @@ static gboolean - gs_flatpak_refine_appstream (GsFlatpak *self, - GsApp *app, - XbSilo *silo, -+ const gchar *silo_filename, -+ GHashTable *silo_installed_by_desktopid, - GsPluginRefineFlags flags, - GHashTable *components_by_bundle, - gboolean interactive, -@@ -3410,11 +3411,12 @@ gs_flatpak_refine_appstream (GsFlatpak *self, - appstream_gz, - flags, - interactive, -+ silo_filename, silo_installed_by_desktopid, - cancellable, error); - } - -- if (!gs_appstream_refine_app (self->plugin, app, silo, component, flags, self->silo_installed_by_desktopid, -- self->silo_filename ? self->silo_filename : "", self->scope, error)) -+ if (!gs_appstream_refine_app (self->plugin, app, silo, component, flags, silo_installed_by_desktopid, -+ silo_filename ? silo_filename : "", self->scope, error)) - return FALSE; - - /* use the default release as the version number */ -@@ -3423,13 +3425,15 @@ gs_flatpak_refine_appstream (GsFlatpak *self, - } - - static gboolean --gs_flatpak_refine_app_unlocked (GsFlatpak *self, -+gs_flatpak_refine_app_internal (GsFlatpak *self, - GsApp *app, - GsPluginRefineFlags flags, - gboolean interactive, - gboolean force_state_update, - GHashTable *components_by_bundle, -- GRWLockReaderLocker **locker, -+ XbSilo *silo, -+ const gchar *silo_filename, -+ GHashTable *silo_installed_by_desktopid, - GCancellable *cancellable, - GError **error) - { -@@ -3440,13 +3444,9 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, - if (gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_FLATPAK) - return TRUE; - -- g_clear_pointer (locker, g_rw_lock_reader_locker_free); -- -- if (!ensure_flatpak_silo_with_locker (self, locker, interactive, cancellable, error)) -- return FALSE; -- - /* always do AppStream properties */ -- if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, components_by_bundle, interactive, cancellable, error)) -+ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, -+ flags, components_by_bundle, interactive, cancellable, error)) - return FALSE; - - /* AppStream sets the source to appname/arch/branch */ -@@ -3456,7 +3456,7 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, - } - - /* check the installed state */ -- if (!gs_flatpak_refine_app_state_unlocked (self, app, interactive, force_state_update, cancellable, error)) { -+ if (!gs_flatpak_refine_app_state_internal (self, app, interactive, force_state_update, cancellable, error)) { - g_prefix_error (error, "failed to get state: "); - return FALSE; - } -@@ -3473,7 +3473,8 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, - - /* if the state was changed, perhaps set the version from the release */ - if (old_state != gs_app_get_state (app)) { -- if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, components_by_bundle, interactive, cancellable, error)) -+ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, -+ flags, components_by_bundle, interactive, cancellable, error)) - return FALSE; - } - -@@ -3567,11 +3568,16 @@ gs_flatpak_refine_addons (GsFlatpak *self, - gboolean interactive, - GCancellable *cancellable) - { -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; -+ g_autofree gchar *silo_filename = NULL; -+ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; - g_autoptr(GsAppList) addons = NULL; - g_autoptr(GString) errors = NULL; - guint ii, sz; - -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, &silo_filename, &silo_installed_by_desktopid, cancellable, NULL)) -+ return; -+ - addons = gs_app_dup_addons (parent_app); - sz = addons ? gs_app_list_length (addons) : 0; - -@@ -3582,7 +3588,8 @@ gs_flatpak_refine_addons (GsFlatpak *self, - if (state != gs_app_get_state (addon)) - continue; - -- if (!gs_flatpak_refine_app_unlocked (self, addon, flags, interactive, TRUE, NULL, &locker, cancellable, &local_error)) { -+ if (!gs_flatpak_refine_app_internal (self, addon, flags, interactive, TRUE, NULL, silo, silo_filename, -+ silo_installed_by_desktopid, cancellable, &local_error)) { - if (errors) - g_string_append_c (errors, '\n'); - else -@@ -3614,13 +3621,16 @@ gs_flatpak_refine_app (GsFlatpak *self, - GCancellable *cancellable, - GError **error) - { -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; -+ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; -+ g_autofree gchar *silo_filename = NULL; - - /* ensure valid */ -- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, &silo_filename, &silo_installed_by_desktopid, cancellable, error)) - return FALSE; - -- return gs_flatpak_refine_app_unlocked (self, app, flags, interactive, force_state_update, NULL, &locker, cancellable, error); -+ return gs_flatpak_refine_app_internal (self, app, flags, interactive, force_state_update, NULL, -+ silo, silo_filename, silo_installed_by_desktopid, cancellable, error); - } - - gboolean -@@ -3634,7 +3644,9 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - const gchar *id; - GPtrArray* components = NULL; - g_autoptr(GError) error_local = NULL; -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; -+ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; -+ g_autofree gchar *silo_filename = NULL; - - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); - -@@ -3643,7 +3655,8 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - if (id == NULL) - return TRUE; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); -+ if (silo == NULL) - return FALSE; - - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardQuerySilo, "Flatpak (query silo)", NULL); -@@ -3653,7 +3666,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - } else { - g_autoptr(GPtrArray) components_with_id = NULL; - *inout_components_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); -- components_with_id = xb_silo_query (self->silo, "components/component/id", 0, &error_local); -+ components_with_id = xb_silo_query (silo, "components/component/id", 0, &error_local); - if (components_with_id == NULL) { - if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - return TRUE; -@@ -3686,7 +3699,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - g_autoptr(GPtrArray) bundles = NULL; - - *inout_components_by_bundle = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); -- bundles = xb_silo_query (self->silo, "/components/component/bundle[@type='flatpak']", 0, NULL); -+ bundles = xb_silo_query (silo, "/components/component/bundle[@type='flatpak']", 0, NULL); - for (guint b = 0; bundles != NULL && b < bundles->len; b++) { - XbNode *bundle_node = g_ptr_array_index (bundles, b); - g_autoptr(XbNode) component_node = xb_node_get_parent (bundle_node); -@@ -3709,7 +3722,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - g_autoptr(GsApp) new = NULL; - - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardCreateAppstreamApp, "Flatpak (create Appstream app)", NULL); -- new = gs_appstream_create_app (self->plugin, self->silo, component, self->silo_filename ? self->silo_filename : "", -+ new = gs_appstream_create_app (self->plugin, silo, component, silo_filename ? silo_filename : "", - self->scope, error); - GS_PROFILER_END_SCOPED (FlatpakRefineWildcardCreateAppstreamApp); - -@@ -3761,7 +3774,8 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - g_debug ("Failed to get ref info for '%s' from wildcard '%s', skipping it...", gs_app_get_id (new), id); - } else { - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardRefineNewApp, "Flatpak (refine new app)", NULL); -- if (!gs_flatpak_refine_app_unlocked (self, new, refine_flags, interactive, FALSE, *inout_components_by_bundle, &locker, cancellable, error)) -+ if (!gs_flatpak_refine_app_internal (self, new, refine_flags, interactive, FALSE, *inout_components_by_bundle, -+ silo, silo_filename, silo_installed_by_desktopid, cancellable, error)) - return FALSE; - GS_PROFILER_END_SCOPED (FlatpakRefineWildcardRefineNewApp); - -@@ -3894,10 +3908,18 @@ gs_flatpak_file_to_app_bundle (GsFlatpak *self, - /* load AppStream */ - appstream_gz = flatpak_bundle_ref_get_appstream (xref_bundle); - if (appstream_gz != NULL) { -+ g_autofree gchar *silo_filename = NULL; -+ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; -+ g_autoptr(XbSilo) tmp_silo = NULL; -+ -+ tmp_silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); -+ if (tmp_silo == NULL) -+ return NULL; - if (!gs_flatpak_refine_appstream_from_bytes (self, app, NULL, NULL, - appstream_gz, - GS_PLUGIN_REFINE_FLAGS_REQUIRE_ID, - interactive, -+ silo_filename, silo_installed_by_desktopid, - cancellable, error)) - return NULL; - } else { -@@ -3995,6 +4017,9 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self, - g_autoptr(GsApp) app = NULL; - g_autoptr(XbBuilder) builder = xb_builder_new (); - g_autoptr(XbSilo) silo = NULL; -+ g_autoptr(XbSilo) tmp_silo = NULL; -+ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; -+ g_autofree gchar *silo_filename = NULL; - g_autofree gchar *origin_url = NULL; - g_autofree gchar *ref_comment = NULL; - g_autofree gchar *ref_description = NULL; -@@ -4274,8 +4299,12 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self, - g_debug ("showing AppStream data: %s", xml); - } - -+ tmp_silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); -+ if (tmp_silo == NULL) -+ return NULL; -+ - /* get extra AppStream data if available */ -- if (!gs_flatpak_refine_appstream (self, app, silo, -+ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, - GS_PLUGIN_REFINE_FLAGS_MASK, - NULL, - interactive, -@@ -4296,17 +4325,16 @@ gs_flatpak_search (GsFlatpak *self, - GError **error) - { - g_autoptr(GsAppList) list_tmp = gs_app_list_new (); -- g_autoptr(GRWLockReaderLocker) locker = NULL; - g_autoptr(GMutexLocker) app_silo_locker = NULL; - g_autoptr(GPtrArray) silos_to_remove = g_ptr_array_new (); -+ g_autoptr(XbSilo) silo = NULL; - GHashTableIter iter; - gpointer key, value; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- if (!gs_appstream_search (self->plugin, self->silo, values, list_tmp, -- cancellable, error)) -+ if (!gs_appstream_search (self->plugin, silo, values, list_tmp, cancellable, error)) - return FALSE; - - gs_flatpak_ensure_remote_title (self, interactive, cancellable); -@@ -4353,8 +4381,8 @@ gs_flatpak_search (GsFlatpak *self, - } - - for (guint i = 0; i < silos_to_remove->len; i++) { -- const char *silo = g_ptr_array_index (silos_to_remove, i); -- g_hash_table_remove (self->app_silos, silo); -+ const char *app_ref = g_ptr_array_index (silos_to_remove, i); -+ g_hash_table_remove (self->app_silos, app_ref); - } - - return TRUE; -@@ -4369,17 +4397,16 @@ gs_flatpak_search_developer_apps (GsFlatpak *self, - GError **error) - { - g_autoptr(GsAppList) list_tmp = gs_app_list_new (); -- g_autoptr(GRWLockReaderLocker) locker = NULL; - g_autoptr(GMutexLocker) app_silo_locker = NULL; - g_autoptr(GPtrArray) silos_to_remove = g_ptr_array_new (); -+ g_autoptr(XbSilo) silo = NULL; - GHashTableIter iter; - gpointer key, value; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- if (!gs_appstream_search_developer_apps (self->plugin, self->silo, values, list_tmp, -- cancellable, error)) -+ if (!gs_appstream_search_developer_apps (self->plugin, silo, values, list_tmp, cancellable, error)) - return FALSE; - - gs_flatpak_ensure_remote_title (self, interactive, cancellable); -@@ -4426,8 +4453,8 @@ gs_flatpak_search_developer_apps (GsFlatpak *self, - } - - for (guint i = 0; i < silos_to_remove->len; i++) { -- const char *silo = g_ptr_array_index (silos_to_remove, i); -- g_hash_table_remove (self->app_silos, silo); -+ const char *app_ref = g_ptr_array_index (silos_to_remove, i); -+ g_hash_table_remove (self->app_silos, app_ref); - } - - return TRUE; -@@ -4441,14 +4468,12 @@ gs_flatpak_add_category_apps (GsFlatpak *self, - GCancellable *cancellable, - GError **error) - { -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- return gs_appstream_add_category_apps (self->plugin, self->silo, -- category, list, -- cancellable, error); -+ return gs_appstream_add_category_apps (self->plugin, silo, category, list, cancellable, error); - } - - gboolean -@@ -4458,12 +4483,12 @@ gs_flatpak_refine_category_sizes (GsFlatpak *self, - GCancellable *cancellable, - GError **error) - { -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- return gs_appstream_refine_category_sizes (self->silo, list, cancellable, error); -+ return gs_appstream_refine_category_sizes (silo, list, cancellable, error); - } - - gboolean -@@ -4474,13 +4499,12 @@ gs_flatpak_add_popular (GsFlatpak *self, - GError **error) - { - g_autoptr(GsAppList) list_tmp = gs_app_list_new (); -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- if (!gs_appstream_add_popular (self->silo, list_tmp, -- cancellable, error)) -+ if (!gs_appstream_add_popular (silo, list_tmp, cancellable, error)) - return FALSE; - - gs_app_list_add_list (list, list_tmp); -@@ -4496,13 +4520,12 @@ gs_flatpak_add_featured (GsFlatpak *self, - GError **error) - { - g_autoptr(GsAppList) list_tmp = gs_app_list_new (); -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- if (!gs_appstream_add_featured (self->silo, list_tmp, -- cancellable, error)) -+ if (!gs_appstream_add_featured (silo, list_tmp, cancellable, error)) - return FALSE; - - gs_app_list_add_list (list, list_tmp); -@@ -4518,12 +4541,12 @@ gs_flatpak_add_deployment_featured (GsFlatpak *self, - GCancellable *cancellable, - GError **error) - { -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- return gs_appstream_add_deployment_featured (self->silo, deployments, list, cancellable, error); -+ return gs_appstream_add_deployment_featured (silo, deployments, list, cancellable, error); - } - - gboolean -@@ -4535,13 +4558,12 @@ gs_flatpak_add_alternates (GsFlatpak *self, - GError **error) - { - g_autoptr(GsAppList) list_tmp = gs_app_list_new (); -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- if (!gs_appstream_add_alternates (self->silo, app, list_tmp, -- cancellable, error)) -+ if (!gs_appstream_add_alternates (silo, app, list_tmp, cancellable, error)) - return FALSE; - - gs_app_list_add_list (list, list_tmp); -@@ -4558,13 +4580,12 @@ gs_flatpak_add_recent (GsFlatpak *self, - GError **error) - { - g_autoptr(GsAppList) list_tmp = gs_app_list_new (); -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- if (!gs_appstream_add_recent (self->plugin, self->silo, list_tmp, age, -- cancellable, error)) -+ if (!gs_appstream_add_recent (self->plugin, silo, list_tmp, age, cancellable, error)) - return FALSE; - - gs_flatpak_claim_app_list (self, list_tmp, interactive); -@@ -4582,12 +4603,12 @@ gs_flatpak_url_to_app (GsFlatpak *self, - GError **error) - { - g_autoptr(GsAppList) list_tmp = gs_app_list_new (); -- g_autoptr(GRWLockReaderLocker) locker = NULL; -+ g_autoptr(XbSilo) silo = NULL; - -- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) -+ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) - return FALSE; - -- if (!gs_appstream_url_to_app (self->plugin, self->silo, list_tmp, url, cancellable, error)) -+ if (!gs_appstream_url_to_app (self->plugin, silo, list_tmp, url, cancellable, error)) - return FALSE; - - gs_flatpak_claim_app_list (self, list_tmp, interactive); -@@ -4638,10 +4659,8 @@ gs_flatpak_finalize (GObject *object) - g_signal_handler_disconnect (self->monitor, self->changed_id); - self->changed_id = 0; - } -- if (self->silo != NULL) -- g_object_unref (self->silo); -- if (self->monitor != NULL) -- g_object_unref (self->monitor); -+ g_clear_object (&self->silo); -+ g_clear_object (&self->monitor); - g_clear_pointer (&self->silo_filename, g_free); - g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); - -@@ -4654,7 +4673,7 @@ gs_flatpak_finalize (GObject *object) - g_object_unref (self->plugin); - g_hash_table_unref (self->broken_remotes); - g_mutex_clear (&self->broken_remotes_mutex); -- g_rw_lock_clear (&self->silo_lock); -+ g_mutex_clear (&self->silo_lock); - g_hash_table_unref (self->app_silos); - g_mutex_clear (&self->app_silos_mutex); - g_clear_pointer (&self->remote_title, g_hash_table_unref); -@@ -4675,7 +4694,7 @@ gs_flatpak_init (GsFlatpak *self) - { - /* XbSilo needs external locking as we destroy the silo and build a new - * one when something changes */ -- g_rw_lock_init (&self->silo_lock); -+ g_mutex_init (&self->silo_lock); - - g_mutex_init (&self->installed_refs_mutex); - self->installed_refs = NULL; --- -GitLab - diff --git a/gnome-software.spec b/gnome-software.spec index 5b8b2b4..708b3f5 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -18,22 +18,20 @@ %bcond dkms %[!0%{?rhel}] # this is not a library version -%define gs_plugin_version 21 +%define gs_plugin_version 22 %global tarball_version %%(echo %{version} | tr '~' '.') %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 47.2 -Release: 3%{?dist} +Version: 48~alpha2 +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later URL: https://apps.gnome.org/Software -Source0: https://download.gnome.org/sources/gnome-software/47/%{name}-%{tarball_version}.tar.xz - -Patch01: 0001-crash-under-gs_appstream_gather_merge_data.patch +Source0: https://download.gnome.org/sources/gnome-software/48/%{name}-%{tarball_version}.tar.xz # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 @@ -290,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Tue Jan 07 2025 Milan Crha - 48~alpha2-1 +- Update to 48.alpha2 + * Mon Dec 09 2024 Milan Crha - 47.2-3 - Resolves: #2272232 (Crash under gs_appstream_gather_merge_data()) diff --git a/sources b/sources index 4b9ac1c..c1aa6d8 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-47.2.tar.xz) = 60de4a3c3237bfdc0dbbefdeccb00c97af7332a36c6beb5610fb6a416508e8b4712fd007e395ad283a3f9cb8d3e0f3a56c134ff28b3f22953a448188a1005cb6 +SHA512 (gnome-software-48.alpha2.tar.xz) = 89471657b9fe10b621b15916ced35e24aff133cdb003b57535393a47eac99bef392442cce466a6a8519997cc845f0aa4a4416b79cc3bb8e10b72da17bbfdb0b6 From 0589468f3c6d5d31a4ca5fba6a0c8e5c4c8956bd Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 14 Jan 2025 18:05:07 +0100 Subject: [PATCH 11/44] Update to 48.alpha3 --- gnome-software.spec | 5 ++++- sources | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 708b3f5..a45bc57 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,7 +25,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 48~alpha2 +Version: 48~alpha3 Release: 1%{?dist} Summary: A software center for GNOME @@ -288,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Tue Jan 14 2025 Milan Crha - 48~alpha3-1 +- Update to 48.alpha3 + * Tue Jan 07 2025 Milan Crha - 48~alpha2-1 - Update to 48.alpha2 diff --git a/sources b/sources index c1aa6d8..4aa7442 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-48.alpha2.tar.xz) = 89471657b9fe10b621b15916ced35e24aff133cdb003b57535393a47eac99bef392442cce466a6a8519997cc845f0aa4a4416b79cc3bb8e10b72da17bbfdb0b6 +SHA512 (gnome-software-48.alpha3.tar.xz) = 8b05264b77c667687b1f98e9a325b6e8829328387984830bb697f1fa91ef0eef3743bbf30c0440596b655195dcc825a4d1ec56a5d2d9b2d2d87adc3118fc91f2 From 1f728b9f4b847dfbb5a02d36092e4aef0366c999 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 16 Jan 2025 22:56:23 +0000 Subject: [PATCH 12/44] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild --- gnome-software.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gnome-software.spec b/gnome-software.spec index a45bc57..02e5395 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -26,7 +26,7 @@ Name: gnome-software Version: 48~alpha3 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -288,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Thu Jan 16 2025 Fedora Release Engineering - 48~alpha3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + * Tue Jan 14 2025 Milan Crha - 48~alpha3-1 - Update to 48.alpha3 From 539e242884a19daed280a1b30eee235b9f94668a Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 3 Feb 2025 09:00:10 +0100 Subject: [PATCH 13/44] Update to 48.beta --- gnome-software.spec | 7 +++++-- sources | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 02e5395..26aca7c 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,8 +25,8 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 48~alpha3 -Release: 2%{?dist} +Version: 48~beta +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -288,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Mon Feb 03 2025 Milan Crha - 48~beta-1 +- Update to 48.beta + * Thu Jan 16 2025 Fedora Release Engineering - 48~alpha3-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild diff --git a/sources b/sources index 4aa7442..c07dbaa 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-48.alpha3.tar.xz) = 8b05264b77c667687b1f98e9a325b6e8829328387984830bb697f1fa91ef0eef3743bbf30c0440596b655195dcc825a4d1ec56a5d2d9b2d2d87adc3118fc91f2 +SHA512 (gnome-software-48.beta.tar.xz) = 7f6744bea6e1adb136f9bccd619c9753fa94d5f00a19a3a9e8ef2806b1ea572345abe7462b20ba66e86c9a77a25a0c6cd94ffafd6607f8712e15ed66ebe3dec4 From f46e9e0cb86e66161e17be26e8bda2fbcbc05286 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 3 Mar 2025 09:38:54 +0100 Subject: [PATCH 14/44] Update to 48.rc --- gnome-software.spec | 19 +++++++++++-------- sources | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 26aca7c..3f2562c 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -1,11 +1,11 @@ -%global appstream_version 0.14.0 -%global flatpak_version 1.9.1 -%global fwupd_version 1.5.6 -%global glib2_version 2.70.0 -%global gtk4_version 4.14.0 +%global appstream_version 0.16.4 +%global flatpak_version 1.14.1 +%global fwupd_version 1.6.2 +%global glib2_version 2.76.0 +%global gtk4_version 4.16.0 %global json_glib_version 1.6.0 -%global libadwaita_version 1.6~alpha -%global libxmlb_version 0.1.7 +%global libadwaita_version 1.6.0 +%global libxmlb_version 0.3.4 %global packagekit_version 1.2.5 # Disable WebApps for RHEL builds @@ -25,7 +25,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 48~beta +Version: 48~rc Release: 1%{?dist} Summary: A software center for GNOME @@ -288,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Mon Mar 03 2025 Milan Crha - 48~rc-1 +- Update to 48.rc + * Mon Feb 03 2025 Milan Crha - 48~beta-1 - Update to 48.beta diff --git a/sources b/sources index c07dbaa..029e225 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-48.beta.tar.xz) = 7f6744bea6e1adb136f9bccd619c9753fa94d5f00a19a3a9e8ef2806b1ea572345abe7462b20ba66e86c9a77a25a0c6cd94ffafd6607f8712e15ed66ebe3dec4 +SHA512 (gnome-software-48.rc.tar.xz) = 830e70fc6af22e6f70047ef13f2307548f939c8191370de10cdfba9a9fc907297aa06831eebc859bf442889e3782e5168d559ad115a32c3623f5179b16489703 From 909247b0df67611394949fc4cbc492ade7d68393 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 14 Mar 2025 09:47:52 +0100 Subject: [PATCH 15/44] Update to 48.0 --- gnome-software.spec | 5 ++++- sources | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 3f2562c..cf92bf5 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,7 +25,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 48~rc +Version: 48.0 Release: 1%{?dist} Summary: A software center for GNOME @@ -288,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Fri Mar 14 2025 Milan Crha - 48.0-1 +- Update to 48.0 + * Mon Mar 03 2025 Milan Crha - 48~rc-1 - Update to 48.rc diff --git a/sources b/sources index 029e225..08e726a 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-48.rc.tar.xz) = 830e70fc6af22e6f70047ef13f2307548f939c8191370de10cdfba9a9fc907297aa06831eebc859bf442889e3782e5168d559ad115a32c3623f5179b16489703 +SHA512 (gnome-software-48.0.tar.xz) = ec61395a4376dfbe281337c29822f3854ab240c32172011b5bb589ef4d9c5ee7f1e4dc559e6cdaf5d24c728eb3e01dc7cd6069813c23e280dbb5aaf8f6336742 From 807d38c4d22e6b29f599ab09bf29993421a8ed33 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 11 Apr 2025 08:37:11 +0200 Subject: [PATCH 16/44] Update to 48.1 --- gnome-software.spec | 5 ++++- sources | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index cf92bf5..763ed62 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -25,7 +25,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 48.0 +Version: 48.1 Release: 1%{?dist} Summary: A software center for GNOME @@ -288,6 +288,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Fri Apr 11 2025 Milan Crha - 48.1-1 +- Update to 48.1 + * Fri Mar 14 2025 Milan Crha - 48.0-1 - Update to 48.0 diff --git a/sources b/sources index 08e726a..31b7ac3 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-48.0.tar.xz) = ec61395a4376dfbe281337c29822f3854ab240c32172011b5bb589ef4d9c5ee7f1e4dc559e6cdaf5d24c728eb3e01dc7cd6069813c23e280dbb5aaf8f6336742 +SHA512 (gnome-software-48.1.tar.xz) = 7cb1e7a7527e65e6283916cecee55fa4e65d71e538ced65af488d28e94801755d2ab961d2f4603cec0f781893e298fee109df4a839ee99fc3de2cefb82f25a97 From c3fcc6a85e3cac28893a05bb162a01efc02eba08 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 29 Apr 2025 11:05:44 +0200 Subject: [PATCH 17/44] Switch from PackageKit to DNF5 plugin --- 0001-dnf5-plugin.patch | 5565 ++++++++++++++++++++++++++++++++++++++++ gnome-software.spec | 35 +- 2 files changed, 5599 insertions(+), 1 deletion(-) create mode 100644 0001-dnf5-plugin.patch diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch new file mode 100644 index 0000000..f7b5b67 --- /dev/null +++ b/0001-dnf5-plugin.patch @@ -0,0 +1,5565 @@ +at commit 31a1ad73cc4fa28b0d352a7ac392bec76e9c10a4 +Date: Wed Apr 2 14:21:03 2025 +0200 + +diff --git a/meson_options.txt b/meson_options.txt +index bc32ba561..523f572cd 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -2,6 +2,7 @@ option('tests', type : 'boolean', value : true, description : 'enable tests') + option('man', type : 'boolean', value : true, description : 'enable man pages') + option('packagekit', type : 'boolean', value : true, description : 'enable PackageKit support') + option('packagekit_autoremove', type : 'boolean', value : false, description : 'autoremove packages in PackageKit') ++option('dnf5', type : 'boolean', value : true, description : 'enable dnf5 support') + option('polkit', type : 'boolean', value : true, description : 'enable PolKit support') + option('eos_updater', type : 'boolean', value : false, description : 'enable eos-updater support') + option('fwupd', type : 'boolean', value : true, description : 'enable fwupd support') +diff --git a/plugins/dnf5/gs-dnf5-progress-helper.c b/plugins/dnf5/gs-dnf5-progress-helper.c +new file mode 100644 +index 000000000..d720adc87 +--- /dev/null ++++ b/plugins/dnf5/gs-dnf5-progress-helper.c +@@ -0,0 +1,532 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * vi:set noexpandtab tabstop=8 shiftwidth=8: ++ * ++ * Copyright (C) 2024 Red Hat ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++ ++#include ++ ++#include "gs-dnf5-generated.h" ++ ++#include "gs-dnf5-progress-helper.h" ++ ++struct _GsDnf5ProgressHelper ++{ ++ GObject parent; ++ ++ GsDnf5RpmRpm *rpm_proxy; /* (owned) */ ++ GsDnf5Base *base_proxy; /* (owned) */ ++ GsApp *app; /* (owned) */ ++ gchar *session_object_path; /* (owned) */ ++ GHashTable *ongoing_downloads; /* (owned); gchar * (download_id) ~> DownloadInfo * */ ++ GsAppState recover_download_state; ++ guint64 to_download_total; ++ guint64 downloaded_total; ++ ++ gulong download_add_new_id; ++ gulong download_progress_id; ++ gulong download_mirror_failure_id; ++ gulong download_end_id; ++ gulong transaction_before_begin_id; ++ gulong transaction_after_complete_id; ++ gulong transaction_elem_progress_id; ++ gulong transaction_action_progress_id; ++ gulong transaction_action_start_id; ++ gulong transaction_action_stop_id; ++ gulong transaction_script_error_id; ++ gulong transaction_script_start_id; ++ gulong transaction_script_stop_id; ++ gulong transaction_transaction_progress_id; ++ gulong transaction_transaction_start_id; ++ gulong transaction_transaction_stop_id; ++ gulong transaction_unpack_error_id; ++ gulong transaction_verify_progress_id; ++ gulong transaction_verify_start_id; ++ gulong transaction_verify_stop_id; ++}; ++ ++G_DEFINE_TYPE (GsDnf5ProgressHelper, gs_dnf5_progress_helper, G_TYPE_OBJECT) ++ ++static void ++gs_dnf5_update_download_progress (GsDnf5ProgressHelper *self) ++{ ++ if (self->to_download_total > 0) ++ gs_app_set_progress (self->app, 100 * self->downloaded_total / self->to_download_total); ++} ++ ++typedef struct { ++ guint64 downloaded; ++ guint64 total; ++} DownloadInfo; ++ ++static void ++gs_dnf5_download_add_new_cb (GsDnf5Base *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_download_id, ++ const gchar *arg_description, ++ gint64 arg_total_to_download, ++ GsDnf5ProgressHelper *self) ++{ ++ DownloadInfo *download_info; ++ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: download_id:'%s' description:'%s' total_to_download:%" G_GINT64_FORMAT, ++ G_STRFUNC, arg_download_id, arg_description, arg_total_to_download); ++ g_return_if_fail (arg_download_id != NULL); ++ ++ if (arg_total_to_download <= 0) ++ return; ++ ++ if (g_hash_table_size (self->ongoing_downloads) == 0) { ++ self->recover_download_state = gs_app_get_state (self->app); ++ self->to_download_total = 0; ++ self->downloaded_total = 0; ++ gs_app_set_state (self->app, GS_APP_STATE_DOWNLOADING); ++ } ++ ++ download_info = g_new0 (DownloadInfo, 1); ++ download_info->total = arg_total_to_download; ++ ++ g_hash_table_insert (self->ongoing_downloads, g_strdup (arg_download_id), download_info); ++ self->to_download_total += arg_total_to_download; ++ ++ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); ++ gs_dnf5_update_download_progress (self); ++} ++ ++static void ++gs_dnf5_download_end_cb (GsDnf5Base *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_download_id, ++ guint arg_transfer_status, ++ const gchar *arg_message, ++ GsDnf5ProgressHelper *self) ++{ ++ DownloadInfo *download_info; ++ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: download_id:'%s' transfer_status:%u message:'%s'", ++ G_STRFUNC, arg_download_id, arg_transfer_status, arg_message); ++ g_return_if_fail (arg_download_id != NULL); ++ ++ download_info = g_hash_table_lookup (self->ongoing_downloads, arg_download_id); ++ if (download_info != NULL) { ++ self->downloaded_total -= download_info->downloaded; ++ self->downloaded_total += download_info->total; ++ ++ /* remove after being done with the `download_info`, because it frees it */ ++ g_hash_table_remove (self->ongoing_downloads, arg_download_id); ++ download_info = NULL; ++ ++ /* it's the last package to be downloaded */ ++ if (g_hash_table_size (self->ongoing_downloads) == 0) { ++ gs_app_set_state (self->app, self->recover_download_state); ++ gs_app_set_progress (self->app, GS_APP_PROGRESS_UNKNOWN); ++ gs_app_set_size_download (self->app, GS_SIZE_TYPE_UNKNOWN, 0); ++ } else { ++ gs_dnf5_update_download_progress (self); ++ } ++ } ++} ++ ++static void ++gs_dnf5_download_mirror_failure_cb (GsDnf5Base *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_download_id, ++ const gchar *arg_message, ++ const gchar *arg_url, ++ const gchar *arg_metadata, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: download_id:'%s' message:'%s' url:'%s' metadata:'%s'", ++ G_STRFUNC, arg_download_id, arg_message, arg_url, arg_metadata); ++} ++ ++static void ++gs_dnf5_download_progress_cb (GsDnf5Base *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_download_id, ++ gint64 arg_total_to_download, ++ gint64 arg_downloaded, ++ GsDnf5ProgressHelper *self) ++{ ++ DownloadInfo *download_info; ++ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: download_id:'%s' total_to_download:%" G_GINT64_FORMAT " downloaded:%" G_GINT64_FORMAT, ++ G_STRFUNC, arg_download_id, arg_total_to_download, arg_downloaded); ++ ++ download_info = g_hash_table_lookup (self->ongoing_downloads, arg_download_id); ++ if (download_info != NULL) { ++ self->downloaded_total += arg_downloaded - download_info->downloaded; ++ download_info->downloaded = arg_downloaded; ++ if (download_info->downloaded != (guint64) arg_total_to_download) { ++ self->to_download_total -= download_info->total; ++ download_info->total = arg_total_to_download; ++ self->to_download_total += download_info->total; ++ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); ++ } ++ gs_dnf5_update_download_progress (self); ++ } ++} ++ ++static void ++gs_dnf5_transaction_before_begin_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ gs_app_set_progress (self->app, 0); ++ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_after_complete_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ gboolean arg_success, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: success:%d", G_STRFUNC, arg_success); ++ gs_app_set_progress (self->app, 100); ++} ++ ++static void ++gs_dnf5_transaction_elem_progress_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ guint64 arg_processed, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s' progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_processed, arg_total); ++ if (arg_total != 0) ++ gs_app_set_progress (self->app, arg_processed * 100 / arg_total); ++} ++ ++static void ++gs_dnf5_transaction_action_progress_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ guint64 arg_processed, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s' progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_processed, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_action_start_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ guint arg_action, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s' action:%u total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_action, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_action_stop_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s' total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_script_error_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ guint arg_scriptlet_type, ++ guint64 arg_return_code, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s' scriptlet-type:%u return-code:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_scriptlet_type, arg_return_code); ++} ++ ++static void ++gs_dnf5_transaction_script_start_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ guint arg_scriptlet_type, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s' scriptlet-type:%u", G_STRFUNC, arg_nevra, arg_scriptlet_type); ++} ++ ++static void ++gs_dnf5_transaction_script_stop_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ guint arg_scriptlet_type, ++ guint64 arg_return_code, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s' scriptlet-type:%u return-code:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_scriptlet_type, arg_return_code); ++} ++ ++static void ++gs_dnf5_transaction_transaction_progress_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ guint64 arg_processed, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_processed, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_transaction_start_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_transaction_stop_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_unpack_error_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_nevra, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: nevra:'%s'", G_STRFUNC, arg_nevra); ++} ++ ++static void ++gs_dnf5_transaction_verify_progress_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ guint64 arg_processed, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_processed, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_verify_start_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); ++} ++ ++static void ++gs_dnf5_transaction_verify_stop_cb (GsDnf5RpmRpm *object, ++ const gchar *arg_session_object_path, ++ guint64 arg_total, ++ GsDnf5ProgressHelper *self) ++{ ++ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) ++ return; ++ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); ++} ++ ++static void ++gs_dnf5_progress_helper_dispose (GObject *object) ++{ ++ GsDnf5ProgressHelper *self = GS_DNF5_PROGRESS_HELPER (object); ++ GObject *base_proxy = G_OBJECT (self->base_proxy); ++ GObject *rpm_proxy = G_OBJECT (self->rpm_proxy); ++ ++ #define clear_signal_handler(_proxy, _id) G_STMT_START { \ ++ if (_id) { \ ++ g_signal_handler_disconnect (_proxy, _id); \ ++ _id = 0; \ ++ } \ ++ } G_STMT_END ++ ++ clear_signal_handler (base_proxy, self->download_add_new_id); ++ clear_signal_handler (base_proxy, self->download_progress_id); ++ clear_signal_handler (base_proxy, self->download_mirror_failure_id); ++ clear_signal_handler (base_proxy, self->download_end_id); ++ ++ clear_signal_handler (rpm_proxy, self->transaction_before_begin_id); ++ clear_signal_handler (rpm_proxy, self->transaction_after_complete_id); ++ clear_signal_handler (rpm_proxy, self->transaction_elem_progress_id); ++ clear_signal_handler (rpm_proxy, self->transaction_action_progress_id); ++ clear_signal_handler (rpm_proxy, self->transaction_action_start_id); ++ clear_signal_handler (rpm_proxy, self->transaction_action_stop_id); ++ clear_signal_handler (rpm_proxy, self->transaction_script_error_id); ++ clear_signal_handler (rpm_proxy, self->transaction_script_start_id); ++ clear_signal_handler (rpm_proxy, self->transaction_script_stop_id); ++ clear_signal_handler (rpm_proxy, self->transaction_transaction_progress_id); ++ clear_signal_handler (rpm_proxy, self->transaction_transaction_start_id); ++ clear_signal_handler (rpm_proxy, self->transaction_transaction_stop_id); ++ clear_signal_handler (rpm_proxy, self->transaction_unpack_error_id); ++ clear_signal_handler (rpm_proxy, self->transaction_verify_progress_id); ++ clear_signal_handler (rpm_proxy, self->transaction_verify_start_id); ++ clear_signal_handler (rpm_proxy, self->transaction_verify_stop_id); ++ ++ #undef clear_signal_handler ++ ++ gs_app_set_progress (self->app, GS_APP_PROGRESS_UNKNOWN); ++ ++ G_OBJECT_CLASS (gs_dnf5_progress_helper_parent_class)->dispose (object); ++} ++ ++static void ++gs_dnf5_progress_helper_finalize (GObject *object) ++{ ++ GsDnf5ProgressHelper *self = GS_DNF5_PROGRESS_HELPER (object); ++ ++ g_clear_object (&self->base_proxy); ++ g_clear_object (&self->rpm_proxy); ++ g_clear_object (&self->app); ++ g_clear_pointer (&self->session_object_path, g_free); ++ g_clear_pointer (&self->ongoing_downloads, g_hash_table_destroy); ++ ++ G_OBJECT_CLASS (gs_dnf5_progress_helper_parent_class)->finalize (object); ++} ++ ++static void ++gs_dnf5_progress_helper_class_init (GsDnf5ProgressHelperClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->dispose = gs_dnf5_progress_helper_dispose; ++ object_class->finalize = gs_dnf5_progress_helper_finalize; ++} ++ ++static void ++gs_dnf5_progress_helper_init (GsDnf5ProgressHelper *self) ++{ ++ self->ongoing_downloads = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); ++} ++ ++GsDnf5ProgressHelper * ++gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy, ++ GsDnf5RpmRpm *rpm_proxy, ++ GsApp *progress_app, ++ const gchar *session_object_path) ++{ ++ GsDnf5ProgressHelper *self; ++ ++ g_return_val_if_fail (GS_DNF5_IS_BASE (base_proxy), NULL); ++ g_return_val_if_fail (GS_DNF5_IS_RPM_RPM (rpm_proxy), NULL); ++ g_return_val_if_fail (GS_IS_APP (progress_app), NULL); ++ g_return_val_if_fail (session_object_path != NULL, NULL); ++ ++ self = g_object_new (GS_TYPE_DNF5_PROGRESS_HELPER, NULL); ++ self->base_proxy = g_object_ref (base_proxy); ++ self->rpm_proxy = g_object_ref (rpm_proxy); ++ self->app = g_object_ref (progress_app); ++ self->session_object_path = g_strdup (session_object_path); ++ ++ gs_app_set_progress (self->app, GS_APP_PROGRESS_UNKNOWN); ++ ++ self->download_add_new_id = ++ g_signal_connect_object (base_proxy, "download_add_new", ++ G_CALLBACK (gs_dnf5_download_add_new_cb), self, 0); ++ self->download_progress_id = ++ g_signal_connect_object (base_proxy, "download_progress", ++ G_CALLBACK (gs_dnf5_download_progress_cb), self, 0); ++ self->download_mirror_failure_id = ++ g_signal_connect_object (base_proxy, "download_mirror_failure", ++ G_CALLBACK (gs_dnf5_download_mirror_failure_cb), self, 0); ++ self->download_end_id = ++ g_signal_connect_object (base_proxy, "download_end", ++ G_CALLBACK (gs_dnf5_download_end_cb), self, 0); ++ ++ self->transaction_before_begin_id = ++ g_signal_connect_object (rpm_proxy, "transaction_before_begin", ++ G_CALLBACK (gs_dnf5_transaction_before_begin_cb), self, 0); ++ self->transaction_after_complete_id = ++ g_signal_connect_object (rpm_proxy, "transaction_after_complete", ++ G_CALLBACK (gs_dnf5_transaction_after_complete_cb), self, 0); ++ self->transaction_elem_progress_id = ++ g_signal_connect_object (rpm_proxy, "transaction_elem_progress", ++ G_CALLBACK (gs_dnf5_transaction_elem_progress_cb), self, 0); ++ self->transaction_action_progress_id = ++ g_signal_connect_object (rpm_proxy, "transaction_action_progress", ++ G_CALLBACK (gs_dnf5_transaction_action_progress_cb), self, 0); ++ self->transaction_action_start_id = ++ g_signal_connect_object (rpm_proxy, "transaction_action_start", ++ G_CALLBACK (gs_dnf5_transaction_action_start_cb), self, 0); ++ self->transaction_action_stop_id = ++ g_signal_connect_object (rpm_proxy, "transaction_action_stop", ++ G_CALLBACK (gs_dnf5_transaction_action_stop_cb), self, 0); ++ self->transaction_script_error_id = ++ g_signal_connect_object (rpm_proxy, "transaction_script_error", ++ G_CALLBACK (gs_dnf5_transaction_script_error_cb), self, 0); ++ self->transaction_script_start_id = ++ g_signal_connect_object (rpm_proxy, "transaction_script_start", ++ G_CALLBACK (gs_dnf5_transaction_script_start_cb), self, 0); ++ self->transaction_script_stop_id = ++ g_signal_connect_object (rpm_proxy, "transaction_script_stop", ++ G_CALLBACK (gs_dnf5_transaction_script_stop_cb), self, 0); ++ self->transaction_transaction_progress_id = ++ g_signal_connect_object (rpm_proxy, "transaction_transaction_progress", ++ G_CALLBACK (gs_dnf5_transaction_transaction_progress_cb), self, 0); ++ self->transaction_transaction_start_id = ++ g_signal_connect_object (rpm_proxy, "transaction_transaction_start", ++ G_CALLBACK (gs_dnf5_transaction_transaction_start_cb), self, 0); ++ self->transaction_transaction_stop_id = ++ g_signal_connect_object (rpm_proxy, "transaction_transaction_stop", ++ G_CALLBACK (gs_dnf5_transaction_transaction_stop_cb), self, 0); ++ self->transaction_unpack_error_id = ++ g_signal_connect_object (rpm_proxy, "transaction_unpack_error", ++ G_CALLBACK (gs_dnf5_transaction_unpack_error_cb), self, 0); ++ self->transaction_verify_progress_id = ++ g_signal_connect_object (rpm_proxy, "transaction_verify_progress", ++ G_CALLBACK (gs_dnf5_transaction_verify_progress_cb), self, 0); ++ self->transaction_verify_start_id = ++ g_signal_connect_object (rpm_proxy, "transaction_verify_start", ++ G_CALLBACK (gs_dnf5_transaction_verify_start_cb), self, 0); ++ self->transaction_verify_stop_id = ++ g_signal_connect_object (rpm_proxy, "transaction_verify_stop", ++ G_CALLBACK (gs_dnf5_transaction_verify_stop_cb), self, 0); ++ ++ return self; ++} +diff --git a/plugins/dnf5/gs-dnf5-progress-helper.h b/plugins/dnf5/gs-dnf5-progress-helper.h +new file mode 100644 +index 000000000..73d528d54 +--- /dev/null ++++ b/plugins/dnf5/gs-dnf5-progress-helper.h +@@ -0,0 +1,30 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * vi:set noexpandtab tabstop=8 shiftwidth=8: ++ * ++ * Copyright (C) 2024 Red Hat ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#pragma once ++ ++#include ++#include ++ ++#include ++ ++#include "gs-dnf5-generated.h" ++ ++G_BEGIN_DECLS ++ ++#define GS_TYPE_DNF5_PROGRESS_HELPER (gs_dnf5_progress_helper_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GsDnf5ProgressHelper, gs_dnf5_progress_helper, GS, DNF5_PROGRESS_HELPER, GObject) ++ ++GsDnf5ProgressHelper * ++ gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy, ++ GsDnf5RpmRpm *rpm_proxy, ++ GsApp *progress_app, ++ const gchar *session_object_path); ++ ++G_END_DECLS +diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c +new file mode 100644 +index 000000000..93f22dc4f +--- /dev/null ++++ b/plugins/dnf5/gs-plugin-dnf5.c +@@ -0,0 +1,3738 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * vi:set noexpandtab tabstop=8 shiftwidth=8: ++ * ++ * Copyright (C) 2024 Red Hat ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++/* ++ * SECTION: dnf5 plugin. ++ * ++ * Talks to dnf5 to manage packages through dnf. ++ */ ++ ++#include ++ ++#include ++#include ++ ++#include "gs-app-private.h" ++ ++#include "gs-worker-thread.h" ++#include "gs-dnf5-generated.h" ++#include "gs-dnf5-rpm-generated.h" ++#include "gs-dnf5-progress-helper.h" ++ ++#include "gs-plugin-dnf5.h" ++ ++#define GS_DNF5_RELEASEVER_DEFAULT NULL ++#define GS_DNF5_INTERFACE_RPM_DNF "org.rpm.dnf.v0" ++#define GS_DNF5_OBJECT_PATH_RPM_DNF "/org/rpm/dnf/v0" ++ ++/* for how long a session can be left opened until it's auto-closed */ ++#define GS_SESSION_LIFETIME_SECS (5 * 60) ++ ++struct _GsPluginDnf5 ++{ ++ GsPlugin parent; ++ ++ GsWorkerThread *worker; /* (owned) */ ++ GDBusConnection *connection; ++ GsDnf5RpmTransaction *rpm_transaction_proxy; ++ guint rpm_transaction_watch_id; ++ gint calling_rpm; ++ ++ struct _SessionData { ++ GMutex mutex; ++ GCond cond; ++ guint autoclose_timer; ++ GsDnf5SessionManager *proxy; ++ gchar *object_path; ++ guint n_used; ++ gint needs_reset; ++ } session_data; ++ ++ struct _DependencySizesData { ++ GMutex mutex; ++ GPtrArray *apps; /* (element-type GsApp) */ ++ GCancellable *cancellable; /* non-NULL, when an op is running */ ++ } dependency_sizes_data; ++}; ++ ++G_DEFINE_TYPE (GsPluginDnf5, gs_plugin_dnf5, GS_TYPE_PLUGIN) ++ ++#define assert_in_worker(self) \ ++ g_assert (gs_worker_thread_is_in_worker_context (self->worker)) ++ ++static gint ++gs_dnf5_get_priority_for_interactivity (gboolean interactive) ++{ ++ return interactive ? G_PRIORITY_DEFAULT : G_PRIORITY_LOW; ++} ++ ++static void ++gs_dnf5_convert_error (GError **error) ++{ ++ gboolean not_authorized; ++ if (error == NULL || *error == NULL) ++ return; ++ ++ /* The message is not localized in the dnf5, thus this should work */ ++ not_authorized = strstr ((*error)->message, "GDBus.Error:org.rpm.dnf.v0.Error: Not authorized") != NULL; ++ ++ g_dbus_error_strip_remote_error (*error); ++ gs_utils_error_convert_gdbus (error); ++ if (not_authorized && g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) { ++ (*error)->domain = GS_PLUGIN_ERROR; ++ (*error)->code = GS_PLUGIN_ERROR_AUTH_REQUIRED; ++ } else if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) || ( ++ !gs_utils_error_convert_gdbus (error) && ++ !gs_utils_error_convert_gio (error))) { ++ (*error)->domain = GS_PLUGIN_ERROR; ++ (*error)->code = GS_PLUGIN_ERROR_FAILED; ++ } ++} ++ ++static void ++gs_dnf5_report_error (GsPluginDnf5 *self, ++ const GError *error, ++ gboolean interactive) ++{ ++ g_autoptr(GsPluginEvent) event = NULL; ++ ++ event = gs_plugin_event_new ("error", error, ++ NULL); ++ if (interactive) ++ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INTERACTIVE); ++ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING); ++ gs_plugin_report_event (GS_PLUGIN (self), event); ++} ++ ++/* The session reset is used to refresh information about the packages, ++ like after a package install/uninstall, it might not be noticed by ++ the daemon internal structures that the package is installed/uninstalled. */ ++static void ++gs_dnf5_mark_session_needs_reset (GsPluginDnf5 *self) ++{ ++ g_atomic_int_set (&self->session_data.needs_reset, 1); ++} ++ ++static void ++gs_dnf5_rpm_start_transaction_cb (GsDnf5RpmTransaction *proxy, ++ const gchar *dbcookie, ++ guint transaction_id, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = user_data; ++ ++ g_debug ("%s: %p: dbcookie:'%s' transaction_id:%u", G_STRFUNC, self, dbcookie, transaction_id); ++} ++ ++static void ++gs_dnf5_rpm_end_transaction_cb (GsDnf5RpmTransaction *proxy, ++ const gchar *dbcookie, ++ guint transaction_id, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = user_data; ++ ++ g_debug ("%s: %p: dbcookie:'%s' transaction_id:%u", G_STRFUNC, self, dbcookie, transaction_id); ++ ++ /* the transactions are for install/uninstall, thus ++ mark the connection to require a reset() */ ++ gs_dnf5_mark_session_needs_reset (self); ++ ++ /* also do not know what precisely changed, thus a heavy hammer */ ++ gs_plugin_reload (GS_PLUGIN (self)); ++} ++ ++static void ++gs_dnf5_got_rpm_transaction_proxy_cb (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ g_autoptr(GsPluginDnf5) self = user_data; ++ g_autoptr(GError) local_error = NULL; ++ ++ self->rpm_transaction_proxy = gs_dnf5_rpm_transaction_proxy_new_finish (result, &local_error); ++ ++ if (self->rpm_transaction_proxy != NULL) { ++ g_debug ("Opened RPM Transaction D-Bus proxy"); ++ ++ g_signal_connect_object (self->rpm_transaction_proxy, "start-transaction", ++ G_CALLBACK (gs_dnf5_rpm_start_transaction_cb), self, 0); ++ g_signal_connect_object (self->rpm_transaction_proxy, "end-transaction", ++ G_CALLBACK (gs_dnf5_rpm_end_transaction_cb), self, 0); ++ } else { ++ g_debug ("Failed to open RPM Transaction proxy: %s", local_error ? local_error->message : "Unknown error"); ++ } ++} ++ ++static void ++gs_dnf5_rpm_transaction_appeared_cb (GDBusConnection *connection, ++ const gchar *name, ++ const gchar *name_owner, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = user_data; ++ ++ g_debug ("%s: name:'%s' owner:'%s' existing-proxy:%p calling_rpm:%d", ++ G_STRFUNC, name, name_owner, self->rpm_transaction_proxy, ++ g_atomic_int_get (&self->calling_rpm)); ++ ++ if (name_owner != NULL && *name_owner != '\0' && !g_atomic_int_get (&self->calling_rpm)) { ++ g_clear_object (&self->rpm_transaction_proxy); ++ gs_dnf5_rpm_transaction_proxy_new (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ "org.rpm.announce", ++ "/org/rpm/Transaction", ++ NULL, ++ gs_dnf5_got_rpm_transaction_proxy_cb, ++ g_object_ref (self)); ++ } ++} ++ ++static void ++gs_dnf5_rpm_transaction_vanished_cb (GDBusConnection *connection, ++ const gchar *name, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = user_data; ++ ++ g_debug ("%s: name:'%s' existing-proxy:%p", G_STRFUNC, name, self->rpm_transaction_proxy); ++ ++ g_clear_object (&self->rpm_transaction_proxy); ++} ++ ++static void ++gs_dnf5_bus_get_cb (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ g_autoptr(GTask) task = user_data; ++ g_autoptr(GError) local_error = NULL; ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (g_task_get_source_object (task)); ++ ++ self->connection = g_bus_get_finish (result, &local_error); ++ gs_dnf5_convert_error (&local_error); ++ ++ if (self->connection != NULL) { ++ self->rpm_transaction_watch_id = g_bus_watch_name_on_connection (self->connection, ++ "org.rpm.announce", ++ G_BUS_NAME_WATCHER_FLAGS_NONE, ++ gs_dnf5_rpm_transaction_appeared_cb, ++ gs_dnf5_rpm_transaction_vanished_cb, ++ self, ++ NULL); ++ } ++ ++ if (self->connection != NULL) ++ g_task_return_boolean (task, TRUE); ++ else ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++} ++ ++static void ++gs_plugin_dnf5_setup_async (GsPlugin *plugin, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ g_autoptr(GTask) task = NULL; ++ ++ g_debug ("dnf5 setup"); ++ ++ /* Start up a worker thread to process all the plugin’s function calls. */ ++ self->worker = gs_worker_thread_new ("gs-plugin-dnf5"); ++ ++ task = g_task_new (plugin, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_setup_async); ++ ++ g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, gs_dnf5_bus_get_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_setup_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++static void ++gs_dnf5_shutdown_cb (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ g_autoptr(GTask) task = user_data; ++ GsPluginDnf5 *self = g_task_get_source_object (task); ++ g_autoptr(GsWorkerThread) worker = NULL; ++ g_autoptr(GError) local_error = NULL; ++ ++ worker = g_steal_pointer (&self->worker); ++ ++ if (!gs_worker_thread_shutdown_finish (worker, result, &local_error)) ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ else ++ g_task_return_boolean (task, TRUE); ++} ++ ++static void ++gs_plugin_dnf5_shutdown_async (GsPlugin *plugin, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ g_autoptr(GTask) task = NULL; ++ g_autoptr(GCancellable) dependency_sizes_cancellable = NULL; ++ ++ task = g_task_new (plugin, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_shutdown_async); ++ ++ /* stop any dependency sizes ongoing operation */ ++ g_mutex_lock (&self->dependency_sizes_data.mutex); ++ if (self->dependency_sizes_data.cancellable) ++ dependency_sizes_cancellable = g_object_ref (self->dependency_sizes_data.cancellable); ++ g_mutex_unlock (&self->dependency_sizes_data.mutex); ++ if (dependency_sizes_cancellable != NULL) ++ g_cancellable_cancel (dependency_sizes_cancellable); ++ ++ /* Stop the worker thread. */ ++ gs_worker_thread_shutdown_async (self->worker, cancellable, gs_dnf5_shutdown_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_shutdown_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++static void ++gs_dnf5_close_session_real (GsDnf5SessionManager *proxy, ++ const gchar *session_path) ++{ ++ g_autoptr(GError) local_error = NULL; ++ gboolean result = FALSE; ++ ++ /* Not passing operation's cancellable, because it can be cancelled by the user, ++ but the session needs to be closed. */ ++ if (gs_dnf5_session_manager_call_close_session_sync (proxy, session_path, &result, NULL, &local_error)) ++ g_warn_if_fail (result); ++ else ++ g_debug ("Failed to close session: %s", local_error->message); ++} ++ ++static gboolean ++gs_dnf5_autoclose_session_cb (gpointer user_data) ++{ ++ GsPluginDnf5 *self = user_data; ++ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); ++ ++ self->session_data.autoclose_timer = 0; ++ if (self->session_data.n_used == 0 && self->session_data.proxy != NULL) { ++ gs_dnf5_close_session_real (self->session_data.proxy, self->session_data.object_path); ++ ++ g_clear_object (&self->session_data.proxy); ++ g_clear_pointer (&self->session_data.object_path, g_free); ++ } ++ ++ return G_SOURCE_REMOVE; ++} ++ ++static gboolean ++gs_dnf5_reset_session_sync (GsPluginDnf5 *self, ++ const gchar *session_path, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autoptr(GsDnf5Base) base_proxy = NULL; ++ g_autofree gchar *error_msg = NULL; ++ gboolean success = FALSE; ++ ++ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ if (base_proxy == NULL) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Base proxy: "); ++ return FALSE; ++ } ++ ++ if (!gs_dnf5_base_call_reset_sync (base_proxy, &success, &error_msg, cancellable, error)) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to call Base::reset(): "); ++ return FALSE; ++ } ++ ++ if (!success) { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to execute Base::reset(): %s", error_msg ? error_msg : "Unknown error"); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static gchar * /* (transfer full) */ ++gs_dnf5_open_session (GsPluginDnf5 *self, ++ const gchar *releasever, ++ GsDnf5SessionManager **out_proxy, /* (transfer full) */ ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autofree gchar *session_path = NULL; ++ ++ if (releasever == GS_DNF5_RELEASEVER_DEFAULT) { ++ if (self->session_data.autoclose_timer) { ++ g_source_remove (self->session_data.autoclose_timer); ++ self->session_data.autoclose_timer = 0; ++ } ++ ++ if (self->session_data.proxy != NULL) { ++ GsDnf5SessionManager *proxy = self->session_data.proxy; ++ ++ while (g_atomic_int_get (&self->session_data.needs_reset) && ++ !g_cancellable_is_cancelled (cancellable)) { ++ g_autoptr(GError) local_error = NULL; ++ if (self->session_data.n_used > 0) { ++ g_debug ("Existing session needs reset, waiting for opened operations to finish"); ++ while (self->session_data.n_used && g_atomic_int_get (&self->session_data.needs_reset)) { ++ g_cond_wait_until (&self->session_data.cond, &self->session_data.mutex, ++ /* check whether was cancelled up to three times per second */ ++ g_get_monotonic_time () + (G_TIME_SPAN_SECOND / 3)); ++ ++ if (g_cancellable_set_error_if_cancelled (cancellable, error)) ++ return NULL; ++ } ++ } ++ ++ if (g_cancellable_set_error_if_cancelled (cancellable, error)) ++ return NULL; ++ ++ if (g_atomic_int_get (&self->session_data.needs_reset)) { ++ gboolean success; ++ ++ success = gs_dnf5_reset_session_sync (self, self->session_data.object_path, cancellable, &local_error); ++ g_atomic_int_set (&self->session_data.needs_reset, 0); ++ ++ /* let any waiter for session reset know the conditions changed */ ++ g_cond_broadcast (&self->session_data.cond); ++ ++ if (success) { ++ g_debug ("Successfully reset existing session"); ++ } else { ++ g_debug ("Failed to reset existing session: %s", local_error ? local_error->message : "Unknown error"); ++ break; ++ } ++ } else { ++ g_debug ("Wanted to reset the session, but someone else did that already, thus skipping the reset"); ++ break; ++ } ++ } ++ ++ if (g_cancellable_set_error_if_cancelled (cancellable, error)) ++ return NULL; ++ ++ if (proxy == self->session_data.proxy) { ++ self->session_data.n_used++; ++ g_debug ("Using existing session"); ++ *out_proxy = g_object_ref (self->session_data.proxy); ++ return g_strdup (self->session_data.object_path); ++ } ++ } ++ } ++ ++ g_debug ("Creating new session"); ++ *out_proxy = gs_dnf5_session_manager_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_NONE, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ GS_DNF5_OBJECT_PATH_RPM_DNF, ++ cancellable, ++ error); ++ ++ if (*out_proxy == NULL) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to get Session Manager: "); ++ return NULL; ++ } ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ ++ if (releasever != GS_DNF5_RELEASEVER_DEFAULT) { ++ g_variant_builder_add (options_builder, "{sv}", "releasever", ++ g_variant_new_string (releasever)); ++ } else { ++ g_autoptr(GVariantBuilder) config = NULL; ++ ++ config = g_variant_builder_new (G_VARIANT_TYPE ("a{ss}")); ++ g_variant_builder_add (config, "{ss}", "optional_metadata_types", ++ "appstream"); ++ ++ g_variant_builder_add (options_builder, "{sv}", "config", ++ g_variant_builder_end (config)); ++ } ++ ++ if (!gs_dnf5_session_manager_call_open_session_sync (*out_proxy, ++ g_variant_builder_end (options_builder), ++ &session_path, ++ cancellable, ++ error)) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to open session: "); ++ return NULL; ++ } ++ ++ if (releasever == GS_DNF5_RELEASEVER_DEFAULT) { ++ g_assert (self->session_data.proxy == NULL); ++ self->session_data.proxy = g_object_ref (*out_proxy); ++ self->session_data.object_path = g_strdup (session_path); ++ self->session_data.n_used = 1; ++ } ++ ++ return g_steal_pointer (&session_path); ++} ++ ++static void ++gs_dnf5_close_session (GsPluginDnf5 *self, ++ GsDnf5SessionManager *proxy, ++ const gchar *session_path) ++{ ++ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); ++ ++ if (self->session_data.proxy == proxy) { ++ g_assert (self->session_data.n_used > 0); ++ self->session_data.n_used--; ++ ++ /* let any waiter for session reset know the n_used changed */ ++ g_cond_broadcast (&self->session_data.cond); ++ ++ if (self->session_data.n_used == 0) { ++ g_assert (self->session_data.autoclose_timer == 0); ++ self->session_data.autoclose_timer = g_timeout_add_seconds (GS_SESSION_LIFETIME_SECS, gs_dnf5_autoclose_session_cb, self); ++ } ++ } else { ++ gs_dnf5_close_session_real (proxy, session_path); ++ } ++} ++ ++/* Returns whether to continue */ ++typedef gboolean (* GsDnf5ForeachFunc) (GsPluginDnf5 *self, ++ GVariant *value, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error); ++ ++static gboolean ++gs_dnf5_foreach_item (GsPluginDnf5 *self, ++ GVariant *array, ++ GsDnf5ForeachFunc func, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GVariantIter iter; ++ GVariant *child; ++ ++ g_variant_iter_init (&iter, array); ++ while (child = g_variant_iter_next_value (&iter), child != NULL) { ++ g_autoptr(GError) local_error = NULL; ++ gboolean done; ++ ++ done = !func (self, child, user_data, cancellable, &local_error); ++ g_variant_unref (child); ++ ++ if (local_error) { ++ g_propagate_error (error, g_steal_pointer (&local_error)); ++ return FALSE; ++ } ++ ++ if (g_cancellable_set_error_if_cancelled (cancellable, error)) ++ return FALSE; ++ ++ if (done) ++ break; ++ } ++ ++ return TRUE; ++} ++ ++static void ++gs_dnf5_app_set_str (GVariantDict *dict, ++ const gchar *key, ++ GsApp *app, ++ void (*set_func) (GsApp *app, ++ const gchar *value)) ++{ ++ g_autoptr(GVariant) value = NULL; ++ ++ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_STRING); ++ if (value != NULL) ++ set_func (app, g_variant_get_string (value, NULL)); ++} ++ ++static void ++gs_dnf5_app_set_str2 (GVariantDict *dict, ++ const gchar *key, ++ GsApp *app, ++ void (*set_func) (GsApp *app, ++ GsAppQuality quality, ++ const gchar *value)) ++{ ++ g_autoptr(GVariant) value = NULL; ++ ++ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_STRING); ++ if (value != NULL) ++ set_func (app, GS_APP_QUALITY_NORMAL, g_variant_get_string (value, NULL)); ++} ++ ++static void ++gs_dnf5_app_set_size (GVariantDict *dict, ++ const gchar *key, ++ GsApp *app, ++ void (*set_func) (GsApp *app, ++ GsSizeType size_type, ++ guint64 size_bytes)) ++{ ++ g_autoptr(GVariant) value = NULL; ++ ++ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_UINT64); ++ if (value != NULL) ++ set_func (app, GS_SIZE_TYPE_VALID, g_variant_get_uint64 (value)); ++} ++ ++static gchar * ++gs_dnf5_dup_version_from_dict (GVariantDict *dict) ++{ ++ g_autoptr(GVariant) version = NULL; ++ ++ version = g_variant_dict_lookup_value (dict, "version", G_VARIANT_TYPE_STRING); ++ if (version != NULL) { ++ const gchar *version_str = g_variant_get_string (version, NULL); ++ if (version_str != NULL && *version_str != '\0') { ++ g_autoptr(GVariant) release = NULL; ++ const gchar *release_str = NULL; ++ release = g_variant_dict_lookup_value (dict, "release", G_VARIANT_TYPE_STRING); ++ if (release != NULL) ++ release_str = g_variant_get_string (release, NULL); ++ if (release_str != NULL && *release_str != '\0') { ++ g_autofree gchar *version_release_str = g_strconcat (version_str, "-", release_str, NULL); ++ return g_steal_pointer (&version_release_str); ++ } else { ++ return g_strdup (version_str); ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static void ++gs_dnf5_app_set_version (GVariantDict *dict, ++ GsApp *app, ++ void (*set_func) (GsApp *app, ++ const gchar *value)) ++{ ++ g_autofree gchar *ver_str = gs_dnf5_dup_version_from_dict (dict); ++ if (ver_str != NULL) ++ set_func (app, ver_str); ++} ++ ++static void ++gs_plugin_dnf5_set_packaging_format (GsApp *app) ++{ ++ gs_app_set_metadata (app, "GnomeSoftware::PackagingFormat", "RPM"); ++ gs_app_set_metadata (app, "GnomeSoftware::PackagingBaseCssColor", "error_color"); ++} ++ ++static void ++gs_dnf5_update_app_state (GsApp *app, ++ GsAppState state, ++ GVariantDict *dict) ++{ ++ if (state == GS_APP_STATE_UNKNOWN) { ++ g_autoptr(GVariant) value = NULL; ++ value = g_variant_dict_lookup_value (dict, "is_installed", G_VARIANT_TYPE_BOOLEAN); ++ if (value == NULL) ++ return; ++ if (g_variant_get_boolean (value)) ++ state = GS_APP_STATE_INSTALLED; ++ else ++ state = GS_APP_STATE_AVAILABLE; ++ } ++ /* The state can be only installed, available or updatable */ ++ if (gs_app_get_state (app) == state || ++ gs_app_get_state (app) == GS_APP_STATE_UPDATABLE) ++ return; ++ if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) ++ gs_app_set_state (app, state); ++ else if (gs_app_get_state (app) != GS_APP_STATE_INSTALLED || ++ state == GS_APP_STATE_UPDATABLE) ++ gs_app_set_state (app, state); ++} ++ ++static void ++gs_dnf5_update_app_changelogs (GsApp *app, ++ GVariantDict *dict) ++{ ++ GVariantIter iter; ++ GVariant *child; ++ g_autoptr(GString) changes = NULL; ++ g_autoptr(GVariant) changelogs = NULL; ++ ++ changelogs = g_variant_dict_lookup_value (dict, "changelogs", NULL); ++ if (changelogs == NULL) ++ return; ++ ++ g_variant_iter_init (&iter, changelogs); ++ while (child = g_variant_iter_next_value (&iter), child != NULL) { ++ gint64 when = 0; ++ const gchar *whom = NULL; ++ const gchar *what = NULL; ++ ++ g_variant_get (child, "(x&s&s)", &when, &whom, &what); ++ ++ if (whom != NULL && what != NULL) { ++ g_autofree gchar *tmp = NULL; ++ const gchar *eml_start, *eml_end; ++ ++ /* Hide the email address in the changes */ ++ eml_start = strchr (whom, '<'); ++ eml_end = strrchr (whom, '>'); ++ if (eml_start != NULL && eml_start < eml_end) { ++ if (g_ascii_isspace (eml_end[1])) ++ eml_end++; ++ tmp = g_malloc0 (strlen (whom) + 1); ++ strncpy (tmp, whom, eml_start - whom); ++ strcat (tmp, eml_end + 1); ++ whom = tmp; ++ } ++ ++ if (changes == NULL) ++ changes = g_string_new (""); ++ else ++ g_string_append (changes, "\n\n"); ++ g_string_append (changes, whom); ++ g_string_append_c (changes, '\n'); ++ g_string_append (changes, what); ++ } ++ ++ g_variant_unref (child); ++ } ++ ++ if (changes != NULL) ++ gs_app_set_update_details_text (app, changes->str); ++} ++ ++typedef struct { ++ GsAppList *list; ++ GsAppState set_state; ++ GHashTable *nevra_to_app; /* (nullable): gchar *nevra ~> GsApp * */ ++ GHashTable *replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> gchar *old_version */ ++} ReadPackageData; ++ ++static gboolean ++gs_dnf5_read_package_cb (GsPluginDnf5 *self, ++ GVariant *package_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ ReadPackageData *rpd = user_data; ++ GsAppList *list = rpd->list; ++ GHashTable *nevra_to_app = rpd->nevra_to_app; ++ g_autofree gchar *name = NULL; ++ g_autoptr(GsApp) app = NULL; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) value = NULL; ++ ++ dict = g_variant_dict_new (package_array); ++ ++ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); ++ if (value != NULL) { ++ const gchar *name_const = g_variant_get_string (value, NULL); ++ app = gs_plugin_cache_lookup (GS_PLUGIN (self), name_const); ++ if (app == NULL) ++ name = g_strdup (name_const); ++ g_clear_pointer (&value, g_variant_unref); ++ } ++ ++ if (app == NULL) { ++ app = gs_app_new (NULL); ++ gs_app_set_management_plugin (app, GS_PLUGIN (self)); ++ gs_app_set_metadata (app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); ++ gs_plugin_dnf5_set_packaging_format (app); ++ gs_app_set_kind (app, AS_COMPONENT_KIND_GENERIC); ++ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE); ++ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM); ++ ++ gs_dnf5_app_set_str (dict, "name", app, gs_app_add_source); ++ gs_dnf5_app_set_str (dict, "nevra", app, gs_app_add_source_id); ++ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_name); ++ gs_dnf5_app_set_str2 (dict, "summary", app, gs_app_set_summary); ++ gs_dnf5_app_set_str2 (dict, "description", app, gs_app_set_description); ++ gs_dnf5_app_set_str2 (dict, "license", app, gs_app_set_license); ++ gs_dnf5_app_set_size (dict, "install_size", app, gs_app_set_size_installed); ++ gs_dnf5_app_set_size (dict, "download_size", app, gs_app_set_size_download); ++ ++ value = g_variant_dict_lookup_value (dict, "url", G_VARIANT_TYPE_STRING); ++ if (value != NULL) { ++ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, g_variant_get_string (value, NULL)); ++ g_clear_pointer (&value, g_variant_unref); ++ } ++ ++ gs_plugin_cache_add (GS_PLUGIN (self), name, app); ++ } ++ ++ gs_dnf5_update_app_changelogs (app, dict); ++ gs_dnf5_update_app_state (app, rpd->set_state, dict); ++ ++ if (rpd->set_state == GS_APP_STATE_UPDATABLE) { ++ gs_dnf5_app_set_version (dict, app, gs_app_set_update_version); ++ gs_app_add_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT); ++ } else { ++ gs_dnf5_app_set_version (dict, app, gs_app_set_version); ++ } ++ ++ if (nevra_to_app != NULL) { ++ g_clear_pointer (&value, g_variant_unref); ++ value = g_variant_dict_lookup_value (dict, "nevra", G_VARIANT_TYPE_STRING); ++ if (value != NULL) { ++ const gchar *nevra_const = g_variant_get_string (value, NULL); ++ if (nevra_const != NULL && *nevra_const != '\0') ++ g_hash_table_insert (nevra_to_app, g_strdup (nevra_const), g_object_ref (app)); ++ } ++ } ++ ++ gs_app_list_add (list, app); ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_read_repo_cb (GsPluginDnf5 *self, ++ GVariant *package_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsAppList *list = user_data; ++ g_autoptr(GsApp) app = NULL; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) value = NULL; ++ const gchar *id; ++ ++ dict = g_variant_dict_new (package_array); ++ ++ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_STRING); ++ if (value == NULL) ++ return TRUE; ++ ++ id = g_variant_get_string (value, NULL); ++ if (id == NULL || *id == '\0') ++ return TRUE; ++ ++ /* Skip sub-repos */ ++ if (g_str_has_suffix (id, "-source") || ++ g_str_has_suffix (id, "-debuginfo") || ++ g_str_has_suffix (id, "-testing")) ++ return TRUE; ++ ++ g_clear_pointer (&value, g_variant_unref); ++ id = NULL; ++ ++ app = gs_app_new (NULL); ++ gs_app_set_management_plugin (app, GS_PLUGIN (self)); ++ gs_app_set_kind (app, AS_COMPONENT_KIND_REPOSITORY); ++ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE); ++ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM); ++ gs_app_add_quirk (app, GS_APP_QUIRK_NOT_LAUNCHABLE); ++ gs_app_set_metadata (app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); ++ gs_plugin_dnf5_set_packaging_format (app); ++ gs_app_set_metadata (app, "GnomeSoftware::SortKey", "300"); ++ gs_app_set_origin_ui (app, _("Packages")); ++ ++ gs_dnf5_app_set_str (dict, "id", app, gs_app_set_id); ++ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_name); ++ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_description); ++ ++ value = g_variant_dict_lookup_value (dict, "enabled", G_VARIANT_TYPE_BOOLEAN); ++ if (value != NULL) { ++ gs_app_set_state (app, g_variant_get_boolean (value) ? ++ GS_APP_STATE_INSTALLED : GS_APP_STATE_AVAILABLE); ++ g_clear_pointer (&value, g_variant_unref); ++ } else { ++ gs_app_set_state (app, GS_APP_STATE_INSTALLED); ++ } ++ ++ gs_app_list_add (list, app); ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_refine_from_advisory_packages_cb (GsPluginDnf5 *self, ++ GVariant *array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GHashTable *nevra_to_app = user_data; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) var_nevra = NULL; ++ ++ dict = g_variant_dict_new (array); ++ ++ var_nevra = g_variant_dict_lookup_value (dict, "nevra", G_VARIANT_TYPE_STRING); ++ if (var_nevra != NULL) { ++ const gchar *nevra = g_variant_get_string (var_nevra, NULL); ++ if (nevra != NULL) { ++ GsApp *app; ++ ++ app = g_hash_table_lookup (nevra_to_app, nevra); ++ if (app != NULL) ++ gs_app_set_update_urgency (app, AS_URGENCY_KIND_CRITICAL); ++ } ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_refine_from_advisory_collections_cb (GsPluginDnf5 *self, ++ GVariant *array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GHashTable *nevra_to_app = user_data; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) packages = NULL; ++ gboolean res = TRUE; ++ ++ dict = g_variant_dict_new (array); ++ ++ packages = g_variant_dict_lookup_value (dict, "packages", G_VARIANT_TYPE_ARRAY); ++ if (packages != NULL) ++ res = gs_dnf5_foreach_item (self, packages, gs_dnf5_refine_from_advisory_packages_cb, nevra_to_app, cancellable, error); ++ ++ return res; ++} ++ ++static gboolean ++gs_dnf5_refine_from_advisory_cb (GsPluginDnf5 *self, ++ GVariant *array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GHashTable *nevra_to_app = user_data; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) collections = NULL; ++ g_autoptr(GVariant) severity = NULL; ++ gboolean res = TRUE; ++ ++ dict = g_variant_dict_new (array); ++ ++ /* ensure only advisories with severity 'critical' are traversed */ ++ severity = g_variant_dict_lookup_value (dict, "severity", G_VARIANT_TYPE_STRING); ++ if (severity == NULL || g_variant_get_string (severity, NULL) == NULL || ++ g_ascii_strcasecmp (g_variant_get_string (severity, NULL), "critical") != 0) ++ return TRUE; ++ ++ collections = g_variant_dict_lookup_value (dict, "collections", G_VARIANT_TYPE_ARRAY); ++ if (collections != NULL) ++ res = gs_dnf5_foreach_item (self, collections, gs_dnf5_refine_from_advisory_collections_cb, nevra_to_app, cancellable, error); ++ ++ return res; ++} ++ ++static GVariant * ++gs_dnf5_dup_package_attrs (void) ++{ ++ g_autoptr(GVariantBuilder) attrs_builder = NULL; ++ ++ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (attrs_builder, "s", "nevra"); ++ g_variant_builder_add (attrs_builder, "s", "name"); ++ g_variant_builder_add (attrs_builder, "s", "epoch"); ++ g_variant_builder_add (attrs_builder, "s", "version"); ++ g_variant_builder_add (attrs_builder, "s", "release"); ++ g_variant_builder_add (attrs_builder, "s", "arch"); ++ g_variant_builder_add (attrs_builder, "s", "repo_id"); ++ g_variant_builder_add (attrs_builder, "s", "install_size"); ++ g_variant_builder_add (attrs_builder, "s", "download_size"); ++ g_variant_builder_add (attrs_builder, "s", "is_installed"); ++ g_variant_builder_add (attrs_builder, "s", "summary"); ++ g_variant_builder_add (attrs_builder, "s", "url"); ++ g_variant_builder_add (attrs_builder, "s", "license"); ++ g_variant_builder_add (attrs_builder, "s", "description"); ++ g_variant_builder_add (attrs_builder, "s", "changelogs"); ++ ++ return g_variant_builder_end (attrs_builder); ++} ++ ++static void ++gs_dnf5_gather_async_result_cb (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GAsyncResult **out_result = user_data; ++ *out_result = g_object_ref (result); ++} ++ ++static void ++gs_dnf5_confirm_key_response_cb (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ g_autoptr(GError) local_error = NULL; ++ ++ if (!gs_dnf5_rpm_repo_call_confirm_key_finish (GS_DNF5_RPM_REPO (source_object), result, &local_error)) ++ g_debug ("Failed to confirm key: %s", local_error->message); ++} ++ ++typedef struct { ++ gchar *title; ++ gchar *msg; ++ gchar *details; ++ gchar *accept_label; ++ gchar *key_id; ++ GsPluginDnf5 *self; /* (owned) */ ++ GWeakRef repo_proxy_weakref; /* GsDnf5RpmRepo */ ++} QuestionData; ++ ++static void ++question_data_free (QuestionData *data) ++{ ++ if (data != NULL) { ++ g_free (data->title); ++ g_free (data->msg); ++ g_free (data->details); ++ g_free (data->accept_label); ++ g_free (data->key_id); ++ g_clear_object (&data->self); ++ g_weak_ref_clear (&data->repo_proxy_weakref); ++ g_free (data); ++ } ++} ++ ++static gboolean ++gs_dnf5_ask_question_idle_cb (gpointer user_data) ++{ ++ QuestionData *data = user_data; ++ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; ++ ++ repo_proxy = g_weak_ref_get (&data->repo_proxy_weakref); ++ if (repo_proxy != NULL) { ++ gboolean confirmed; ++ ++ confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label); ++ ++ gs_dnf5_rpm_repo_call_confirm_key (repo_proxy, data->key_id, confirmed, NULL, gs_dnf5_confirm_key_response_cb, NULL); ++ } ++ ++ return G_SOURCE_REMOVE; ++} ++ ++typedef struct { ++ GsPluginDnf5 *self; /* (not owned) */ ++ const gchar *session_path; /* (not owned) */ ++ GsDnf5RpmRepo *repo_proxy; /* (not owned) */ ++} RepoKeyImportData; ++ ++static void ++gs_dnf5_repo_key_import_request_cb (GsDnf5Base *object, ++ const gchar *arg_session_object_path, ++ const gchar *arg_key_id, ++ const gchar *const *arg_user_ids, ++ const gchar *arg_key_fingerprint, ++ const gchar *arg_key_url, ++ gint64 arg_timestamp, ++ RepoKeyImportData *repo_key_import_data) ++{ ++ QuestionData *data; ++ g_autoptr(GString) user_ids = NULL; ++ g_autofree gchar *key_filename = NULL; ++ g_autoptr(GString) str_fingerprint = NULL; ++ GString *details = NULL; ++ const gchar *fingerprint; ++ const gchar *from; ++ ++ if (g_strcmp0 (repo_key_import_data->session_path, arg_session_object_path) != 0) ++ return; ++ ++ user_ids = g_string_new (NULL); ++ for (guint i = 0; arg_user_ids != NULL && arg_user_ids[i] != NULL; i++) { ++ if (i > 0) ++ g_string_append (user_ids, ", "); ++ g_string_append_c (user_ids, '"'); ++ g_string_append (user_ids, arg_user_ids[i]); ++ g_string_append_c (user_ids, '"'); ++ } ++ ++ g_debug ("%s: key_id:'%s' user_ids:[%s] key_fingerprint:'%s' key_url:'%s' timestamp:%" G_GINT64_FORMAT, ++ G_STRFUNC, arg_key_id, user_ids->str, arg_key_fingerprint, arg_key_url, arg_timestamp); ++ ++ if (g_ascii_strncasecmp (arg_key_url, "file:", 5) == 0) ++ key_filename = g_filename_from_uri (arg_key_url, NULL, NULL); ++ ++ if ((strlen (arg_key_fingerprint) % 4) == 0) { ++ str_fingerprint = g_string_new (arg_key_fingerprint); ++ for (gint i = str_fingerprint->len - 4; i > 0; i -= 4) { ++ g_string_insert_c (str_fingerprint, i, ' '); ++ } ++ } ++ ++ details = g_string_new (""); ++ ++ from = key_filename != NULL ? key_filename : arg_key_url; ++ fingerprint = str_fingerprint != NULL ? str_fingerprint->str : arg_key_fingerprint; ++ ++ #define add_nonempty_line(_format, _value) G_STMT_START { \ ++ if ((_value) != NULL && *(_value) != '\0') { \ ++ g_string_append_printf (details, _format, _value); \ ++ g_string_append_c (details, '\n'); \ ++ } \ ++ } G_STMT_END ++ ++ /* Translators: the '%s' is replaced with the key user name */ ++ add_nonempty_line (_("Key user: %s"), user_ids->str); ++ /* Translators: the '%s' is replaced with the key fingerprint, a few hex digits */ ++ add_nonempty_line (_("Fingerprint: %s"), fingerprint); ++ /* Translators: the '%s' is replaced with the local path or a URI to the key */ ++ add_nonempty_line (_("From: %s"), from); ++ ++ #undef add_nonempty_line ++ ++ data = g_new0 (QuestionData, 1); ++ data->title = g_strdup (_("Import Key")); ++ /* Translators: the '%s' is replaced with the key ID, usually a few hex digits */ ++ data->msg = g_strdup_printf (_("Do you want to import key %s?"), arg_key_id); ++ data->details = g_string_free (details, FALSE); ++ data->accept_label = g_strdup (_("_Import Key")); ++ data->key_id = g_strdup (arg_key_id); ++ data->self = g_object_ref (repo_key_import_data->self); ++ g_weak_ref_init (&data->repo_proxy_weakref, repo_key_import_data->repo_proxy); ++ ++ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free); ++} ++ ++static void ++gs_dnf5_goal_cancelled_cb (GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ g_autoptr(GCancellable) run_cancellable = user_data; ++ g_autoptr(GError) local_error = NULL; ++ g_autofree gchar *error_message = NULL; ++ gboolean succeeded = FALSE; ++ ++ if (gs_dnf5_goal_call_cancel_finish (GS_DNF5_GOAL (source_object), &succeeded, &error_message, result, &local_error)) { ++ if (succeeded) { ++ g_debug ("%s: Transaction cancelled by the user", G_STRFUNC); ++ g_cancellable_cancel (run_cancellable); ++ } else { ++ g_debug ("%s: Cannot cancel transaction: %s", G_STRFUNC, error_message); ++ } ++ } else if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { ++ g_debug ("%s: Failed to cancel transaction: %s", G_STRFUNC, local_error->message); ++ } ++} ++ ++static gboolean ++gs_dnf5_reset_transaction_sync (GsPluginDnf5 *self, ++ const gchar *session_path, ++ GsDnf5RpmRpm *rpm_proxy, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autoptr(GsDnf5Goal) goal_proxy = NULL; ++ ++ goal_proxy = gs_dnf5_goal_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ if (goal_proxy == NULL) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Goal proxy: "); ++ return FALSE; ++ } ++ ++ return gs_dnf5_goal_call_reset_sync (goal_proxy, cancellable, error); ++} ++ ++/* this is needed to receive D-Bus signals about progress in the worker thread */ ++static gboolean ++gs_dnf5_goal_call_do_transaction_sync_wrapper (GsDnf5Goal *proxy, ++ GVariant *arg_options, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autoptr(GMainContext) main_context = g_main_context_ref_thread_default (); ++ g_autoptr(GAsyncResult) async_result = NULL; ++ gs_dnf5_goal_call_do_transaction (proxy, arg_options, cancellable, gs_dnf5_gather_async_result_cb, &async_result); ++ while (!async_result) ++ g_main_context_iteration (main_context, TRUE); ++ return gs_dnf5_goal_call_do_transaction_finish (proxy, async_result, error); ++} ++ ++typedef struct { ++ GsDnf5Goal *goal_proxy; ++ GCancellable *run_cancellable; ++} TransactionData; ++ ++static void ++gs_dnf5_transaction_cancelled_cb (GCancellable *parent_cancellable, ++ TransactionData *transaction_data) ++{ ++ gs_dnf5_goal_call_cancel (transaction_data->goal_proxy, transaction_data->run_cancellable, ++ gs_dnf5_goal_cancelled_cb, g_object_ref (transaction_data->run_cancellable)); ++} ++ ++typedef enum { ++ GS_DNF5_TRANSACTION_FLAG_NONE = 0, ++ GS_DNF5_TRANSACTION_FLAG_OFFLINE = 1 << 0, ++ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY = 1 << 1 ++} GsDnf5TransactionFlags; ++ ++static gboolean ++gs_dnf5_run_transaction (GsPluginDnf5 *self, ++ const gchar *session_path, ++ GsDnf5RpmRpm *rpm_proxy, ++ GsApp *progress_app, ++ GsDnf5TransactionFlags flags, ++ GVariant **out_transaction, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autoptr(GsDnf5Base) base_proxy = NULL; ++ g_autoptr(GsDnf5Goal) goal_proxy = NULL; ++ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) transaction = NULL; ++ g_autoptr(GsDnf5ProgressHelper) helper = NULL; ++ g_autoptr(GCancellable) run_cancellable = NULL; ++ RepoKeyImportData repo_key_import_data; ++ TransactionData transaction_data; ++ gulong repo_key_import_id; ++ gulong cancellable_id = 0; ++ guint result = 0; ++ gboolean success; ++ ++ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ if (base_proxy == NULL) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Base proxy: "); ++ return FALSE; ++ } ++ ++ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ if (repo_proxy == NULL) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Repo proxy: "); ++ return FALSE; ++ } ++ ++ goal_proxy = gs_dnf5_goal_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ if (goal_proxy == NULL) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Goal proxy: "); ++ return FALSE; ++ } ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (goal_proxy), G_MAXINT); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ ++ if (progress_app != NULL) ++ helper = gs_dnf5_progress_helper_new (base_proxy, rpm_proxy, progress_app, session_path); ++ ++ repo_key_import_data.self = self; ++ repo_key_import_data.session_path = session_path; ++ repo_key_import_data.repo_proxy = repo_proxy; ++ repo_key_import_id = g_signal_connect (base_proxy, "repo_key_import_request", ++ G_CALLBACK (gs_dnf5_repo_key_import_request_cb), &repo_key_import_data); ++ ++ if (!gs_dnf5_goal_call_resolve_sync (goal_proxy, ++ g_variant_builder_end (options_builder), ++ &transaction, ++ &result, ++ cancellable, ++ error)) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to resolve transaction: "); ++ g_signal_handler_disconnect (base_proxy, repo_key_import_id); ++ return FALSE; ++ } ++ ++ if (out_transaction != NULL && transaction != NULL) ++ *out_transaction = g_variant_ref_sink (transaction); ++ ++ /* 0 ... all fine, 1 ... there are warnings, 2 ... failed */ ++ if (result == 2) { ++ g_auto(GStrv) problems = NULL; ++ ++ if (gs_dnf5_goal_call_get_transaction_problems_string_sync (goal_proxy, &problems, cancellable, error) && ++ problems != NULL) { ++ /* extra space at the end, but no harm, it reuses translated string */ ++ GString *msg = g_string_new ("Failed to resolve transaction: "); ++ for (guint i = 0; problems[i] != NULL; i++) { ++ g_string_append_c (msg, '\n'); ++ g_string_append (msg, problems[i]); ++ } ++ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, msg->str); ++ } else { ++ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, ++ _("Unknown error")); ++ g_prefix_error (error, "Failed to resolve transaction: "); ++ } ++ g_signal_handler_disconnect (base_proxy, repo_key_import_id); ++ return FALSE; ++ } ++ ++ g_clear_pointer (&options_builder, g_variant_builder_unref); ++ ++ if ((flags & GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY) != 0) { ++ g_signal_handler_disconnect (base_proxy, repo_key_import_id); ++ return TRUE; ++ } ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ if ((flags & GS_DNF5_TRANSACTION_FLAG_OFFLINE) != 0) { ++ g_variant_builder_add (options_builder, "{sv}", "offline", ++ g_variant_new_boolean (TRUE)); ++ } ++ ++ run_cancellable = g_cancellable_new (); ++ if (cancellable != NULL) { ++ transaction_data.goal_proxy = goal_proxy; ++ transaction_data.run_cancellable = run_cancellable; ++ ++ cancellable_id = g_cancellable_connect (cancellable, G_CALLBACK (gs_dnf5_transaction_cancelled_cb), ++ &transaction_data, NULL); ++ } ++ ++ success = gs_dnf5_goal_call_do_transaction_sync_wrapper (goal_proxy, ++ g_variant_builder_end (options_builder), ++ run_cancellable, ++ error); ++ ++ g_cancellable_disconnect (cancellable, cancellable_id); ++ g_signal_handler_disconnect (base_proxy, repo_key_import_id); ++ ++ /* in case there is a pending async call result for the cancel() invocation */ ++ g_cancellable_cancel (run_cancellable); ++ ++ if (!success) { ++ g_auto(GStrv) problems = NULL; ++ ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to run transaction: "); ++ ++ if (error != NULL && *error != NULL && ++ gs_dnf5_goal_call_get_transaction_problems_string_sync (goal_proxy, &problems, cancellable, NULL) && ++ problems != NULL) { ++ GString *msg = g_string_new ((*error)->message); ++ for (guint i = 0; problems[i] != NULL; i++) { ++ g_string_append_c (msg, '\n'); ++ g_string_append (msg, problems[i]); ++ } ++ g_clear_error (error); ++ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, msg->str); ++ } ++ } ++ ++ return success; ++} ++ ++enum OpKindType { OP_KIND_UNKNOWN, OP_KIND_INSTALL, OP_KIND_UPGRADE, OP_KIND_DOWNGRADE, OP_KIND_REINSTALL, OP_KIND_REMOVE, OP_KIND_REPLACED }; ++ ++static enum OpKindType ++gs_dnf5_get_package_op_kind (GVariant *item_array) ++{ ++ struct _op_kinds { ++ const gchar *name; ++ enum OpKindType kind; ++ } op_kinds[] = { ++ { "Install", OP_KIND_INSTALL }, ++ { "Upgrade", OP_KIND_UPGRADE }, ++ { "Downgrade", OP_KIND_DOWNGRADE }, ++ { "Reinstall", OP_KIND_REINSTALL }, ++ { "Remove", OP_KIND_REMOVE }, ++ { "Replaced", OP_KIND_REPLACED } ++ }; ++ const gchar *str = NULL; ++ ++ g_variant_get_child (item_array, 0, "&s", &str); ++ if (str == NULL || g_ascii_strcasecmp (str, "package") != 0) ++ return OP_KIND_UNKNOWN; ++ ++ g_variant_get_child (item_array, 1, "&s", &str); ++ if (str == NULL) ++ return OP_KIND_UNKNOWN; ++ ++ for (guint i = 0; i < G_N_ELEMENTS (op_kinds); i++) { ++ if (g_ascii_strcasecmp (str, op_kinds[i].name) == 0) { ++ return op_kinds[i].kind; ++ } ++ } ++ ++ return OP_KIND_UNKNOWN; ++} ++ ++static gboolean ++gs_dnf5_gather_update_replaces_cb (GsPluginDnf5 *self, ++ GVariant *item_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ ReadPackageData *rpd = user_data; ++ enum OpKindType op_kind = gs_dnf5_get_package_op_kind (item_array); ++ ++ if (op_kind == OP_KIND_REPLACED && rpd->replaces) { ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) value = NULL; ++ ++ value = g_variant_get_child_value (item_array, 4); ++ dict = g_variant_dict_new (value); ++ g_clear_pointer (&value, g_variant_unref); ++ ++ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_INT32); ++ if (value != NULL) { ++ gint32 id = g_variant_get_int32 (value); ++ gchar *version = gs_dnf5_dup_version_from_dict (dict); ++ if (version != NULL) ++ g_hash_table_insert (rpd->replaces, GINT_TO_POINTER (id), version); ++ } ++ } ++ ++ return TRUE; ++} ++ ++typedef struct _UpdateReplacesData { ++ GsApp *app; ++ GHashTable *replaces; ++} UpdateReplacesData; ++ ++static gboolean ++gs_dnf5_gather_update_replaced_pkgs_cb (GsPluginDnf5 *self, ++ GVariant *item_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ UpdateReplacesData *upd = user_data; ++ gint32 replaces_id = g_variant_get_int32 (item_array); ++ const gchar *old_version = g_hash_table_lookup (upd->replaces, GINT_TO_POINTER (replaces_id)); ++ if (old_version != NULL) ++ gs_app_set_version (upd->app, old_version); ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_gather_updates_from_transaction_cb (GsPluginDnf5 *self, ++ GVariant *item_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ ReadPackageData *rpd = user_data; ++ enum OpKindType op_kind = gs_dnf5_get_package_op_kind (item_array); ++ const gchar *str; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) value = NULL; ++ g_autoptr(GsApp) upd_app = NULL; ++ g_autoptr(GString) nevra = NULL; ++ GsApp *known_app; ++ ++ if (op_kind == OP_KIND_UNKNOWN || op_kind == OP_KIND_REPLACED) ++ return TRUE; ++ ++ value = g_variant_get_child_value (item_array, 4); ++ dict = g_variant_dict_new (value); ++ g_clear_pointer (&value, g_variant_unref); ++ ++ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); ++ if (value == NULL) ++ return TRUE; ++ str = g_variant_get_string (value, NULL); ++ if (str == NULL) ++ return TRUE; ++ ++ nevra = g_string_new (str); ++ ++ g_clear_pointer (&value, g_variant_unref); ++ value = g_variant_dict_lookup_value (dict, "evr", G_VARIANT_TYPE_STRING); ++ if (value == NULL) ++ return TRUE; ++ str = g_variant_get_string (value, NULL); ++ if (str == NULL) ++ return TRUE; ++ ++ g_string_append_c (nevra, '-'); ++ g_string_append (nevra, str); ++ ++ g_clear_pointer (&value, g_variant_unref); ++ value = g_variant_dict_lookup_value (dict, "arch", G_VARIANT_TYPE_STRING); ++ if (value == NULL) ++ return TRUE; ++ str = g_variant_get_string (value, NULL); ++ if (str == NULL) ++ return TRUE; ++ ++ g_string_append_c (nevra, '.'); ++ g_string_append (nevra, str); ++ ++ known_app = g_hash_table_lookup (rpd->nevra_to_app, nevra->str); ++ if (known_app != NULL) { ++ upd_app = g_object_ref (known_app); ++ if (gs_app_get_version (upd_app) == NULL) ++ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_version); ++ else ++ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_update_version); ++ ++ if (rpd->replaces != NULL) { ++ g_autoptr(GVariantDict) dict2 = NULL; ++ ++ g_clear_pointer (&value, g_variant_unref); ++ value = g_variant_get_child_value (item_array, 3); ++ dict2 = g_variant_dict_new (value); ++ g_clear_pointer (&value, g_variant_unref); ++ value = g_variant_dict_lookup_value (dict2, "replaces", G_VARIANT_TYPE_ARRAY); ++ if (value != NULL) { ++ UpdateReplacesData upd = { 0, }; ++ ++ upd.app = upd_app; ++ upd.replaces = rpd->replaces; ++ ++ if (!gs_dnf5_foreach_item (self, value, gs_dnf5_gather_update_replaced_pkgs_cb, &upd, cancellable, error)) ++ return FALSE; ++ } ++ } ++ } else { ++ upd_app = gs_app_new (NULL); ++ gs_app_set_management_plugin (upd_app, GS_PLUGIN (self)); ++ gs_app_set_metadata (upd_app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); ++ gs_plugin_dnf5_set_packaging_format (upd_app); ++ gs_app_set_kind (upd_app, AS_COMPONENT_KIND_GENERIC); ++ gs_app_set_bundle_kind (upd_app, AS_BUNDLE_KIND_PACKAGE); ++ gs_app_set_scope (upd_app, AS_COMPONENT_SCOPE_SYSTEM); ++ gs_app_add_quirk (upd_app, GS_APP_QUIRK_HIDE_EVERYWHERE); ++ gs_app_add_quirk (upd_app, GS_APP_QUIRK_NEEDS_REBOOT); ++ ++ gs_dnf5_app_set_str (dict, "name", upd_app, gs_app_add_source); ++ gs_dnf5_app_set_str2 (dict, "name", upd_app, gs_app_set_name); ++ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_version); ++ gs_app_add_source_id (upd_app, nevra->str); ++ ++ gs_app_list_add (rpd->list, upd_app); ++ g_hash_table_insert (rpd->nevra_to_app, g_string_free (g_steal_pointer (&nevra), FALSE), g_object_ref (upd_app)); ++ } ++ ++ gs_dnf5_app_set_size (dict, "install_size", upd_app, gs_app_set_size_installed); ++ gs_dnf5_app_set_size (dict, "download_size", upd_app, gs_app_set_size_download); ++ ++ switch (op_kind) { ++ case OP_KIND_INSTALL: ++ gs_app_set_state (upd_app, GS_APP_STATE_AVAILABLE); ++ break; ++ case OP_KIND_UPGRADE: ++ case OP_KIND_DOWNGRADE: ++ case OP_KIND_REINSTALL: ++ gs_app_set_state (upd_app, GS_APP_STATE_UPDATABLE); ++ break; ++ case OP_KIND_REMOVE: ++ gs_app_set_state (upd_app, GS_APP_STATE_UNAVAILABLE); ++ break; ++ case OP_KIND_REPLACED: ++ case OP_KIND_UNKNOWN: ++ default: ++ g_warn_if_reached (); ++ break; ++ } ++ ++ return TRUE; ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_list_apps_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsAppQueryTristate is_for_update = GS_APP_QUERY_TRISTATE_UNSET; ++ GsAppQueryTristate is_source = GS_APP_QUERY_TRISTATE_UNSET; ++ GsPluginListAppsData *data = task_data; ++ const gchar * const *provides_files = NULL; ++ const gchar *provides_tag = NULL; ++ GsAppQueryProvidesType provides_type = GS_APP_QUERY_PROVIDES_UNKNOWN; ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GsAppList) list = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ g_autoptr(GError) local_error = NULL; ++ gboolean success = TRUE; ++ ++ assert_in_worker (self); ++ ++ if (data->query != NULL) { ++ is_for_update = gs_app_query_get_is_for_update (data->query); ++ is_source = gs_app_query_get_is_source (data->query); ++ provides_files = gs_app_query_get_provides_files (data->query); ++ provides_type = gs_app_query_get_provides (data->query, &provides_tag); ++ } ++ ++ if ((is_for_update == GS_APP_QUERY_TRISTATE_UNSET && ++ is_source == GS_APP_QUERY_TRISTATE_UNSET && ++ provides_tag == NULL && ++ provides_files == NULL) || ++ is_for_update == GS_APP_QUERY_TRISTATE_FALSE || ++ is_source == GS_APP_QUERY_TRISTATE_FALSE || ++ gs_app_query_get_n_properties_set (data->query) != 1) { ++ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, ++ "Unsupported query"); ++ return; ++ } ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ return; ++ } ++ ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = rpm_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); ++ } ++ ++ if (success) { ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ list = gs_app_list_new (); ++ ++ if (is_for_update == GS_APP_QUERY_TRISTATE_TRUE) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ gs_dnf5_dup_package_attrs ()); ++ g_variant_builder_add (options_builder, "{sv}", "with_provides", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_filenames", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_src", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "scope", ++ g_variant_new_string ("upgrades")); ++ ++ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ &local_error); ++ ++ if (success) { ++ g_autoptr(GHashTable) nevra_to_app = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); ++ g_autoptr(GHashTable) replaces = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); ++ ReadPackageData rpd = { 0, }; ++ rpd.list = list; ++ rpd.set_state = GS_APP_STATE_UPDATABLE; ++ rpd.nevra_to_app = nevra_to_app; ++ rpd.replaces = replaces; ++ ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error); ++ ++ if (success && g_hash_table_size (nevra_to_app) > 0) { ++ g_autoptr(GVariantBuilder) options_builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_autoptr(GVariant) transaction = NULL; ++ const gchar * const packages[2] = { NULL, NULL }; ++ ++ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error); ++ ++ success = success && ++ gs_dnf5_rpm_rpm_call_upgrade_sync (rpm_proxy, ++ packages, ++ g_variant_builder_end (options_builder2), ++ cancellable, ++ &local_error); ++ success = success && ++ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, ++ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY, ++ &transaction, cancellable, &local_error); ++ if (success && transaction != NULL) { ++ success = gs_dnf5_foreach_item (self, transaction, ++ gs_dnf5_gather_update_replaces_cb, &rpd, ++ cancellable, &local_error); ++ success = success && ++ gs_dnf5_foreach_item (self, transaction, ++ gs_dnf5_gather_updates_from_transaction_cb, &rpd, ++ cancellable, &local_error); ++ } ++ } ++ ++ if (success && g_hash_table_size (nevra_to_app) > 0) { ++ g_autoptr(GsDnf5Advisory) advisory_proxy = NULL; ++ g_autoptr(GError) local_error2 = NULL; ++ gboolean adv_success; ++ ++ advisory_proxy = gs_dnf5_advisory_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error2); ++ adv_success = advisory_proxy != NULL; ++ ++ if (adv_success) { ++ g_autoptr(GVariantBuilder) adv_options_builder = NULL; ++ g_autoptr(GVariantBuilder) attrs_builder = NULL; ++ g_autoptr(GVariantBuilder) severities_builder = NULL; ++ g_autoptr(GVariant) adv_result = NULL; ++ ++ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (attrs_builder, "s", "severity"); ++ g_variant_builder_add (attrs_builder, "s", "collections"); ++ ++ severities_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (severities_builder, "s", "critical"); ++ ++ adv_options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (adv_options_builder, "{sv}", "advisory_attrs", ++ g_variant_builder_end (attrs_builder)); ++ g_variant_builder_add (adv_options_builder, "{sv}", "availability", ++ g_variant_new_string ("updates")); ++ g_variant_builder_add (adv_options_builder, "{sv}", "severities", ++ g_variant_builder_end (severities_builder)); ++ ++ adv_success = gs_dnf5_advisory_call_list_sync (advisory_proxy, ++ g_variant_builder_end (adv_options_builder), ++ &adv_result, ++ cancellable, ++ &local_error2); ++ if (adv_success) { ++ adv_success = gs_dnf5_foreach_item (self, adv_result, ++ gs_dnf5_refine_from_advisory_cb, nevra_to_app, ++ cancellable, &local_error2); ++ } else { ++ g_debug ("Failed to call Advisory::list: %s", local_error2->message); ++ } ++ } else { ++ g_debug ("Failed to create Advisory proxy: %s", local_error2->message); ++ } ++ } ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call Rpm::list: "); ++ } ++ } else if (is_source == GS_APP_QUERY_TRISTATE_TRUE) { ++ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; ++ ++ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = repo_proxy != NULL; ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariantBuilder) attrs_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (repo_proxy), G_MAXINT); ++ ++ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (attrs_builder, "s", "id"); ++ g_variant_builder_add (attrs_builder, "s", "name"); ++ g_variant_builder_add (attrs_builder, "s", "enabled"); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "repo_attrs", ++ g_variant_builder_end (attrs_builder)); ++ g_variant_builder_add (options_builder, "{sv}", "enable_disable", ++ g_variant_new_string ("all")); ++ ++ success = gs_dnf5_rpm_repo_call_list_sync (repo_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ &local_error); ++ ++ if (success) { ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_repo_cb, list, cancellable, &local_error); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call list: "); ++ } ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Rpm repo proxy: "); ++ } ++ } else if (provides_files != NULL) { ++ g_autoptr(GVariantBuilder) patterns_builder = NULL; ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ ++ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ for (guint i = 0; provides_files[i] != NULL; i++) { ++ g_variant_builder_add (patterns_builder, "s", provides_files[i]); ++ } ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "scope", ++ g_variant_new_string ("all")); ++ g_variant_builder_add (options_builder, "{sv}", "with_nevra", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_provides", ++ g_variant_new_boolean (TRUE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_filenames", ++ g_variant_new_boolean (TRUE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_src", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ gs_dnf5_dup_package_attrs ()); ++ g_variant_builder_add (options_builder, "{sv}", "patterns", ++ g_variant_builder_end (patterns_builder)); ++ ++ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ &local_error); ++ ++ if (success) { ++ ReadPackageData rpd = { 0, }; ++ rpd.list = list; ++ rpd.set_state = GS_APP_STATE_UNKNOWN; ++ rpd.nevra_to_app = NULL; ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call list: "); ++ } ++ } else if (provides_tag != NULL) { ++ g_autoptr(GVariantBuilder) patterns_builder = NULL; ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ g_autofree gchar *pattern1 = NULL, *pattern2 = NULL; ++ ++ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ ++ switch (provides_type) { ++ case GS_APP_QUERY_PROVIDES_PACKAGE_NAME: ++ g_variant_builder_add (patterns_builder, "s", provides_tag); ++ break; ++ case GS_APP_QUERY_PROVIDES_GSTREAMER: ++ pattern1 = g_strdup_printf ("gstreamer0.10(%s)", provides_tag); ++ pattern2 = g_strdup_printf ("gstreamer1(%s)", provides_tag); ++ break; ++ case GS_APP_QUERY_PROVIDES_FONT: ++ pattern1 = g_strdup_printf ("font(%s)", provides_tag); ++ break; ++ case GS_APP_QUERY_PROVIDES_MIME_HANDLER: ++ pattern1 = g_strdup_printf ("mimehandler(%s)", provides_tag); ++ break; ++ case GS_APP_QUERY_PROVIDES_PS_DRIVER: ++ pattern1 = g_strdup_printf ("postscriptdriver(%s)", provides_tag); ++ break; ++ case GS_APP_QUERY_PROVIDES_PLASMA: ++ pattern1 = g_strdup_printf ("plasma4(%s)", provides_tag); ++ pattern2 = g_strdup_printf ("plasma5(%s)", provides_tag); ++ break; ++ case GS_APP_QUERY_PROVIDES_UNKNOWN: ++ default: ++ g_assert_not_reached (); ++ } ++ if (pattern1 != NULL) ++ g_variant_builder_add (patterns_builder, "s", pattern1); ++ if (pattern2 != NULL) ++ g_variant_builder_add (patterns_builder, "s", pattern2); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "scope", ++ g_variant_new_string ("all")); ++ g_variant_builder_add (options_builder, "{sv}", "with_nevra", ++ g_variant_new_boolean (provides_type == GS_APP_QUERY_PROVIDES_PACKAGE_NAME)); ++ g_variant_builder_add (options_builder, "{sv}", "with_provides", ++ g_variant_new_boolean (provides_type != GS_APP_QUERY_PROVIDES_PACKAGE_NAME)); ++ g_variant_builder_add (options_builder, "{sv}", "with_filenames", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_src", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ gs_dnf5_dup_package_attrs ()); ++ g_variant_builder_add (options_builder, "{sv}", "patterns", ++ g_variant_builder_end (patterns_builder)); ++ ++ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ &local_error); ++ ++ if (success) { ++ ReadPackageData rpd = { 0, }; ++ rpd.list = list; ++ rpd.set_state = GS_APP_STATE_UNKNOWN; ++ rpd.nevra_to_app = NULL; ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call list: "); ++ } ++ } else { ++ g_assert_not_reached (); ++ } ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ if (success) { ++ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_list_apps_async (GsPlugin *plugin, ++ GsAppQuery *query, ++ GsPluginListAppsFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE) != 0; ++ g_autoptr(GTask) task = NULL; ++ ++ task = gs_plugin_list_apps_data_new_task (plugin, query, flags, ++ cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_list_apps_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_list_apps_thread_cb, g_steal_pointer (&task)); ++} ++ ++static GsAppList * ++gs_plugin_dnf5_list_apps_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_pointer (G_TASK (result), error); ++} ++ ++static gboolean ++gs_dnf5_gather_dependency_sizes_cb (GsPluginDnf5 *self, ++ GVariant *item_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsApp *main_app = user_data; ++ const gchar *str; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) value = NULL; ++ g_autoptr(GsApp) dep_app = NULL; ++ ++ g_variant_get_child (item_array, 0, "&s", &str); ++ if (str == NULL || g_ascii_strcasecmp (str, "package") != 0) ++ return TRUE; ++ ++ g_variant_get_child (item_array, 2, "&s", &str); ++ if (str == NULL || g_ascii_strcasecmp (str, "dependency") != 0) ++ return TRUE; ++ ++ value = g_variant_get_child_value (item_array, 4); ++ dict = g_variant_dict_new (value); ++ g_clear_pointer (&value, g_variant_unref); ++ ++ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); ++ if (value == NULL) ++ return TRUE; ++ str = g_variant_get_string (value, NULL); ++ if (str == NULL) ++ return TRUE; ++ /* the app itself is in the list too */ ++ if (g_strcmp0 (gs_app_get_source_default (main_app), str) == 0) ++ return TRUE; ++ ++ /* always create a new app, no cached can be used here */ ++ dep_app = gs_app_new (NULL); ++ gs_app_set_management_plugin (dep_app, GS_PLUGIN (self)); ++ gs_app_set_metadata (dep_app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); ++ gs_plugin_dnf5_set_packaging_format (dep_app); ++ gs_app_set_kind (dep_app, AS_COMPONENT_KIND_GENERIC); ++ gs_app_set_bundle_kind (dep_app, AS_BUNDLE_KIND_PACKAGE); ++ gs_app_set_scope (dep_app, AS_COMPONENT_SCOPE_SYSTEM); ++ gs_app_add_quirk (dep_app, GS_APP_QUIRK_HIDE_EVERYWHERE); ++ ++ gs_dnf5_app_set_str (dict, "name", dep_app, gs_app_add_source); ++ gs_dnf5_app_set_str (dict, "nevra", dep_app, gs_app_add_source_id); ++ gs_dnf5_app_set_str2 (dict, "name", dep_app, gs_app_set_name); ++ gs_dnf5_app_set_str2 (dict, "summary", dep_app, gs_app_set_summary); ++ gs_dnf5_app_set_str2 (dict, "description", dep_app, gs_app_set_description); ++ gs_dnf5_app_set_str2 (dict, "license", dep_app, gs_app_set_license); ++ gs_dnf5_app_set_size (dict, "install_size", dep_app, gs_app_set_size_installed); ++ gs_dnf5_app_set_size (dict, "download_size", dep_app, gs_app_set_size_download); ++ ++ gs_dnf5_update_app_state (dep_app, GS_APP_STATE_UNKNOWN, dict); ++ ++ gs_app_add_related (main_app, dep_app); ++ ++ return TRUE; ++} ++ ++static gpointer ++gs_dnf5_dependency_sizes_thread (gpointer thread_data) ++{ ++ g_autofree GWeakRef *weakref = thread_data; ++ g_autoptr(GsPluginDnf5) self = g_weak_ref_get (weakref); ++ g_autoptr(GCancellable) cancellable = NULL; ++ g_autoptr(GError) local_error = NULL; ++ guint index = 0; ++ ++ if (self == NULL) ++ return NULL; ++ ++ g_mutex_lock (&self->dependency_sizes_data.mutex); ++ ++ if (self->dependency_sizes_data.cancellable != NULL) ++ cancellable = g_object_ref (self->dependency_sizes_data.cancellable); ++ ++ while (self->dependency_sizes_data.apps != NULL && index < self->dependency_sizes_data.apps->len && ++ !g_cancellable_is_cancelled (self->dependency_sizes_data.cancellable)) { ++ GsApp *app = g_ptr_array_index (self->dependency_sizes_data.apps, index); ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ gboolean success; ++ ++ index++; ++ ++ if (gs_app_is_installed (app) || ++ gs_app_list_length (gs_app_get_related (app)) > 0) ++ continue; ++ ++ g_mutex_unlock (&self->dependency_sizes_data.mutex); ++ ++ /* need to use temporary session, because the goals are piled up and cannot be cancelled yet */ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); ++ ++ success = session_path != NULL; ++ if (!success) ++ gs_dnf5_convert_error (&local_error); ++ ++ if (success) { ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = rpm_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); ++ } ++ } ++ ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_autoptr(GVariant) transaction = NULL; ++ const gchar *package_name = gs_app_get_source_default (app); ++ const gchar *packages[2] = { NULL, NULL }; ++ ++ packages[0] = package_name; ++ ++ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error); ++ ++ success = success && ++ gs_dnf5_rpm_rpm_call_install_sync (rpm_proxy, ++ (const gchar * const *) packages, ++ g_variant_builder_end (options_builder), ++ cancellable, ++ &local_error); ++ success = success && ++ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, ++ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY, ++ &transaction, cancellable, &local_error); ++ if (success && transaction != NULL) ++ success = gs_dnf5_foreach_item (self, transaction, gs_dnf5_gather_dependency_sizes_cb, app, cancellable, &local_error); ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ g_mutex_lock (&self->dependency_sizes_data.mutex); ++ ++ if (!success) ++ break; ++ } ++ ++ g_clear_pointer (&self->dependency_sizes_data.apps, g_ptr_array_unref); ++ g_clear_object (&self->dependency_sizes_data.cancellable); ++ g_mutex_unlock (&self->dependency_sizes_data.mutex); ++ ++ if (local_error != NULL) ++ g_debug ("Failed to get app dependency sizes: %s", local_error->message); ++ ++ return NULL; ++} ++ ++static gboolean ++gs_dnf5_get_dependency_sizes_sync (GsPluginDnf5 *self, ++ GHashTable *apps, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); ++ GHashTableIter iter; ++ gpointer value = NULL; ++ gboolean need_thread = FALSE; ++ ++ g_hash_table_iter_init (&iter, apps); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) { ++ GsApp *app = value; ++ GsAppList *related = gs_app_get_related (app); ++ ++ if (gs_app_has_quirk (app, GS_APP_QUIRK_HIDE_EVERYWHERE) || ++ gs_app_get_state (app) == GS_APP_STATE_AVAILABLE_LOCAL || ++ gs_app_is_installed (app) || ++ gs_app_list_length (related) > 0) ++ continue; ++ ++ /* getting list of the dependencies to be installed can take a long time, thus run it ++ in a new thread, to not block the refine job */ ++ if (self->dependency_sizes_data.apps == NULL) { ++ need_thread = TRUE; ++ self->dependency_sizes_data.apps = g_ptr_array_new_with_free_func (g_object_unref); ++ } ++ ++ if (!g_ptr_array_find (self->dependency_sizes_data.apps, app, NULL)) ++ g_ptr_array_add (self->dependency_sizes_data.apps, g_object_ref (app)); ++ } ++ ++ if (need_thread) { ++ GWeakRef *weakref = g_new0 (GWeakRef, 1); ++ ++ g_assert (self->dependency_sizes_data.cancellable == NULL); ++ self->dependency_sizes_data.cancellable = g_cancellable_new (); ++ ++ g_weak_ref_set (weakref, self); ++ ++ g_thread_unref (g_thread_new ("gs-dnf5-dependency-sizes", gs_dnf5_dependency_sizes_thread, g_steal_pointer (&weakref))); ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_refine_cb (GsPluginDnf5 *self, ++ GVariant *package_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GHashTable *apps = user_data; ++ const gchar *name; ++ GsApp *app; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) value = NULL; ++ ++ dict = g_variant_dict_new (package_array); ++ ++ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); ++ if (value == NULL) ++ return TRUE; ++ name = g_variant_get_string (value, NULL); ++ if (name == NULL) ++ return TRUE; ++ app = g_hash_table_lookup (apps, name); ++ if (app == NULL) ++ return TRUE; ++ ++ if (gs_app_has_management_plugin (app, NULL) && ++ gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE && ++ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) { ++ gs_app_set_management_plugin (app, GS_PLUGIN (self)); ++ gs_plugin_dnf5_set_packaging_format (app); ++ } ++ ++ gs_dnf5_app_set_version (dict, app, gs_app_set_version); ++ gs_dnf5_app_set_size (dict, "install_size", app, gs_app_set_size_installed); ++ gs_dnf5_app_set_size (dict, "download_size", app, gs_app_set_size_download); ++ ++ gs_dnf5_update_app_state (app, GS_APP_STATE_UNKNOWN, dict); ++ ++ return TRUE; ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_refine_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPlugin *plugin = GS_PLUGIN (self); ++ GsPluginRefineData *data = task_data; ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GHashTable) apps = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ g_autoptr(GError) local_error = NULL; ++ gboolean success = TRUE; ++ ++ assert_in_worker (self); ++ ++ apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); ++ for (guint i = 0; i < gs_app_list_length (data->list); i++) { ++ GsApp *app = gs_app_list_index (data->list, i); ++ if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && ( ++ gs_app_get_state (app) == GS_APP_STATE_UNKNOWN || ++ ((data->flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE_DATA) != 0 && ++ gs_app_list_length (gs_app_get_related (app)) == 0))) { ++ if (gs_app_get_source_default (app) != NULL && ++ ((gs_app_has_management_plugin (app, NULL) && ++ gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE && ++ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) || ++ gs_app_has_management_plugin (app, plugin))) ++ g_hash_table_insert (apps, g_strdup (gs_app_get_source_default (app)), g_object_ref (app)); ++ } ++ } ++ ++ if (g_hash_table_size (apps) == 0) { ++ g_task_return_boolean (task, TRUE); ++ return; ++ } ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ return; ++ } ++ ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = rpm_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); ++ } ++ ++ if (success) { ++ g_autoptr(GVariantBuilder) attrs_builder = NULL; ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariantBuilder) patterns_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ GHashTableIter iter; ++ gpointer key = NULL; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ ++ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_hash_table_iter_init (&iter, apps); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) { ++ const gchar *name = key; ++ g_variant_builder_add (patterns_builder, "s", name); ++ } ++ ++ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (attrs_builder, "s", "name"); ++ g_variant_builder_add (attrs_builder, "s", "version"); ++ g_variant_builder_add (attrs_builder, "s", "install_size"); ++ g_variant_builder_add (attrs_builder, "s", "download_size"); ++ g_variant_builder_add (attrs_builder, "s", "is_installed"); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "patterns", ++ g_variant_builder_end (patterns_builder)); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ g_variant_builder_end (attrs_builder)); ++ g_variant_builder_add (options_builder, "{sv}", "with_nevra", ++ g_variant_new_boolean (TRUE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_provides", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_filenames", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_src", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "scope", ++ g_variant_new_string ("all")); ++ g_variant_builder_add (options_builder, "{sv}", "latest_limit", ++ g_variant_new_int32 (1)); ++ ++ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ &local_error); ++ ++ if (success) { ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_refine_cb, apps, cancellable, &local_error); ++ ++ if (success && ++ (data->flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE_DATA) != 0) { ++ /* do not panic when cannot get dependency sizes for the apps */ ++ if (!gs_dnf5_get_dependency_sizes_sync (self, apps, cancellable, &local_error)) { ++ g_debug ("Failed to get dependency sizes: %s", local_error->message); ++ g_clear_error (&local_error); ++ } ++ } ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call list: "); ++ } ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ if (success) { ++ g_task_return_boolean (task, TRUE); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_refine_async (GsPlugin *plugin, ++ GsAppList *list, ++ GsPluginRefineFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE); ++ g_autoptr(GTask) task = NULL; ++ ++ task = gs_plugin_refine_data_new_task (plugin, list, flags, ++ cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_refine_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_refine_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_refine_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_refresh_metadata_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5Base) base_proxy = NULL; ++ g_autoptr(GError) local_error = NULL; ++ gboolean success = TRUE; ++ ++ assert_in_worker (self); ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ return; ++ } ++ ++ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = base_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Base proxy: "); ++ } ++ ++ if (success && GPOINTER_TO_INT (task_data) == 1) { ++ g_autofree gchar *op_error_msg = NULL; ++ gboolean op_success = FALSE; ++ ++ success = gs_dnf5_base_call_clean_sync (base_proxy, ++ "expire-cache", ++ &op_success, ++ &op_error_msg, ++ cancellable, ++ &local_error); ++ if (!success) ++ g_debug ("Failed to call expire-cache: %s", local_error->message); ++ else if (!op_success) ++ g_debug ("Failed to expire-cache: %s", op_error_msg); ++ ++ /* ignore expire-cache error, only log about it */ ++ g_clear_error (&local_error); ++ ++ /* reset the session, to take the expiration into the effect */ ++ success = gs_dnf5_reset_session_sync (self, session_path, cancellable, &local_error); ++ } ++ ++ if (success) { ++ gboolean result = FALSE; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (base_proxy), G_MAXINT); ++ ++ success = gs_dnf5_base_call_read_all_repos_sync (base_proxy, &result, cancellable, &local_error); ++ ++ /* 'result' is ignored here, due to no error provided */ ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call read-all-repos: "); ++ } ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ if (success) { ++ g_task_return_boolean (task, TRUE); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_refresh_metadata_async (GsPlugin *plugin, ++ guint64 cache_age_secs, ++ GsPluginRefreshMetadataFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_REFRESH_METADATA_FLAGS_INTERACTIVE) != 0; ++ g_autoptr(GTask) task = NULL; ++ ++ task = g_task_new (self, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_refresh_metadata_async); ++ /* no need to save whole structure, the seconds are not used at all, ++ thus make it only a flag whether to invalidate the cache or not first */ ++ g_task_set_task_data (task, GINT_TO_POINTER (cache_age_secs < 60 ? 1 : 0), NULL); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_refresh_metadata_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_refresh_metadata_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++void ++gs_plugin_adopt_app (GsPlugin *plugin, ++ GsApp *app) ++{ ++ if (gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE && ++ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) { ++ gs_app_set_management_plugin (app, plugin); ++ gs_plugin_dnf5_set_packaging_format (app); ++ return; ++ } else if (gs_app_get_kind (app) == AS_COMPONENT_KIND_OPERATING_SYSTEM) { ++ gs_app_set_management_plugin (app, plugin); ++ } ++} ++ ++static gboolean ++gs_dnf5_install_update (GsPlugin *plugin, ++ GsAppList *list, ++ gboolean is_install, ++ gboolean interactive, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GPtrArray) packages = NULL; ++ g_autoptr(GsAppList) used_apps = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ g_autoptr(GsApp) progress_app = NULL; ++ gboolean success; ++ ++ /* queue for install if installation needs the network */ ++ if (!gs_plugin_get_network_available (plugin)) { ++ if (is_install) { ++ for (guint i = 0; i < gs_app_list_length (list); i++) { ++ GsApp *app = gs_app_list_index (list, i); ++ if (gs_app_has_management_plugin (app, plugin)) { ++ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) { ++ g_warn_if_reached (); ++ continue; ++ } ++ ++ gs_app_set_state (app, GS_APP_STATE_QUEUED_FOR_INSTALL); ++ } ++ } ++ } ++ return TRUE; ++ } ++ ++ packages = g_ptr_array_new_with_free_func (g_free); ++ used_apps = gs_app_list_new (); ++ ++ for (guint i = 0; i < gs_app_list_length (list); i++) { ++ GsApp *app = gs_app_list_index (list, i); ++ if (gs_app_has_management_plugin (app, plugin)) { ++ const gchar *source = gs_app_get_source_default (app); ++ if (source != NULL) { ++ GFile *local_file = gs_app_get_local_file (app); ++ if (local_file != NULL) ++ g_ptr_array_add (packages, g_file_get_uri (local_file)); ++ else ++ g_ptr_array_add (packages, g_strdup (source)); ++ gs_app_list_add (used_apps, app); ++ if (progress_app == NULL) ++ progress_app = g_object_ref (app); ++ } ++ } else if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_PROXY)) { ++ GsAppList *related = gs_app_get_related (app); ++ for (guint j = 0; j < gs_app_list_length (related); j++) { ++ GsApp *rel_app = gs_app_list_index (related, j); ++ if (gs_app_has_management_plugin (rel_app, plugin)) { ++ const gchar *source = gs_app_get_source_default (rel_app); ++ if (source != NULL) { ++ GFile *local_file = gs_app_get_local_file (rel_app); ++ if (local_file != NULL) ++ g_ptr_array_add (packages, g_file_get_uri (local_file)); ++ else ++ g_ptr_array_add (packages, g_strdup (source)); ++ gs_app_list_add (used_apps, rel_app); ++ } ++ } ++ } ++ if (progress_app == NULL) ++ progress_app = g_object_ref (app); ++ } ++ } ++ ++ if (packages->len == 0) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "installing not available"); ++ return FALSE; ++ } ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (error); ++ return FALSE; ++ } ++ ++ for (guint i = 0; i < gs_app_list_length (used_apps); i++) { ++ GsApp *app = gs_app_list_index (used_apps, i); ++ g_autoptr(GsAppList) addons = NULL; ++ ++ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) { ++ g_warn_if_reached (); ++ continue; ++ } ++ ++ if (is_install) { ++ gs_app_set_state (app, GS_APP_STATE_INSTALLING); ++ addons = gs_app_dup_addons (app); ++ } ++ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { ++ GsApp *addon = gs_app_list_index (addons, j); ++ if (gs_app_get_to_be_installed (addon)) { ++ const gchar *addon_source = gs_app_get_source_default (app); ++ if (addon_source != NULL) { ++ g_ptr_array_add (packages, g_strdup (addon_source)); ++ if (is_install) ++ gs_app_set_state (addon, GS_APP_STATE_INSTALLING); ++ } ++ } ++ } ++ } ++ ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ success = rpm_proxy != NULL; ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ ++ /* NULL-terminate the array, to be like strv */ ++ g_ptr_array_add (packages, NULL); ++ ++ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, error); ++ ++ if (success && is_install) { ++ success = gs_dnf5_rpm_rpm_call_install_sync (rpm_proxy, ++ (const gchar * const *) packages->pdata, ++ g_variant_builder_end (options_builder), ++ cancellable, ++ error); ++ } else if (success) { ++ success = gs_dnf5_rpm_rpm_call_upgrade_sync (rpm_proxy, ++ (const gchar * const *) packages->pdata, ++ g_variant_builder_end (options_builder), ++ cancellable, ++ error); ++ } ++ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, ++ is_install ? GS_DNF5_TRANSACTION_FLAG_NONE : GS_DNF5_TRANSACTION_FLAG_OFFLINE, ++ NULL, cancellable, error); ++ if (success && !is_install) { ++ /* ensure the update is finished with the 'reboot' action */ ++ g_autoptr(GsDnf5Offline) offline_proxy = NULL; ++ ++ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ success = offline_proxy != NULL; ++ if (success) { ++ g_autofree gchar *op_error_msg = NULL; ++ gboolean op_success = FALSE; ++ ++ success = gs_dnf5_offline_call_set_finish_action_sync (offline_proxy, "reboot", &op_success, &op_error_msg, cancellable, error); ++ if (success && !op_success) { ++ success = FALSE; ++ if (op_error_msg != NULL) { ++ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg); ++ g_prefix_error_literal (error, "Failed to set finish action: "); ++ } ++ } else if (!success) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to set finish action: "); ++ } ++ } else { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Offline proxy: "); ++ } ++ } ++ } else { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ if (success) { ++ gs_dnf5_mark_session_needs_reset (self); ++ } else { ++ gs_dnf5_convert_error (error); ++ ++ if (error != NULL && *error != NULL) ++ gs_dnf5_report_error (self, *error, interactive); ++ } ++ ++ for (guint i = 0; i < gs_app_list_length (used_apps); i++) { ++ GsApp *app = gs_app_list_index (used_apps, i); ++ g_autoptr(GsAppList) addons = NULL; ++ ++ if (gs_app_get_state (app) == GS_APP_STATE_INSTALLING) { ++ if (success) { ++ gs_app_set_state (app, GS_APP_STATE_INSTALLED); ++ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); ++ } else { ++ gs_app_set_state_recover (app); ++ } ++ } else if (success) { ++ /* apps prepared for offline update are downloaded */ ++ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); ++ } ++ ++ if (is_install) ++ addons = gs_app_dup_addons (app); ++ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { ++ GsApp *addon = gs_app_list_index (addons, j); ++ if (gs_app_get_state (addon) == GS_APP_STATE_INSTALLING) { ++ if (success) { ++ gs_app_set_state (addon, GS_APP_STATE_INSTALLED); ++ gs_app_set_size_download (addon, GS_SIZE_TYPE_VALID, 0); ++ } else { ++ gs_app_set_state_recover (addon); ++ } ++ } else if (success) { ++ /* apps prepared for offline update are downloaded */ ++ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); ++ } ++ } ++ } ++ ++ return success; ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_install_apps_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPlugin *plugin = GS_PLUGIN (self); ++ GsPluginInstallAppsData *data = task_data; ++ g_autoptr(GError) local_error = NULL; ++ gboolean success; ++ ++ assert_in_worker (self); ++ ++ g_atomic_int_inc (&self->calling_rpm); ++ ++ success = gs_dnf5_install_update (plugin, data->apps, TRUE, ++ (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0, ++ cancellable, &local_error); ++ ++ g_atomic_int_dec_and_test (&self->calling_rpm); ++ ++ if (success) ++ g_task_return_boolean (task, TRUE); ++ else ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++} ++ ++static void ++gs_plugin_dnf5_install_apps_async (GsPlugin *plugin, ++ GsAppList *apps, ++ GsPluginInstallAppsFlags flags, ++ GsPluginProgressCallback progress_callback, ++ gpointer progress_user_data, ++ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, ++ gpointer app_needs_user_action_data, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0; ++ g_autoptr(GTask) task = NULL; ++ ++ task = gs_plugin_install_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, ++ app_needs_user_action_callback, app_needs_user_action_data, ++ cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_install_apps_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_install_apps_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_install_apps_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_uninstall_apps_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPlugin *plugin = GS_PLUGIN (self); ++ GsPluginUninstallAppsData *data = task_data; ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GPtrArray) touched_apps = NULL; ++ g_autoptr(GPtrArray) packages = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ g_autoptr(GsApp) progress_app = NULL; ++ g_autoptr(GError) local_error = NULL; ++ gboolean success; ++ ++ assert_in_worker (self); ++ ++ touched_apps = g_ptr_array_new_with_free_func (g_object_unref); ++ packages = g_ptr_array_new_with_free_func (g_free); ++ ++ for (guint i = 0; i < gs_app_list_length (data->apps); i++) { ++ g_autoptr(GsAppList) addons = NULL; ++ GsApp *app = gs_app_list_index (data->apps, i); ++ const gchar *source; ++ if (!gs_app_has_management_plugin (app, plugin)) ++ continue; ++ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) { ++ g_warn_if_reached (); ++ continue; ++ } ++ source = gs_app_get_source_default (app); ++ if (source == NULL) { ++ g_task_return_new_error (task, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "remove not available"); ++ return; ++ } ++ g_ptr_array_add (packages, g_strdup (source)); ++ g_ptr_array_add (touched_apps, g_object_ref (app)); ++ gs_app_set_state (app, GS_APP_STATE_REMOVING); ++ ++ if (progress_app == NULL) ++ progress_app = g_object_ref (app); ++ ++ addons = gs_app_dup_addons (app); ++ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { ++ GsApp *addon = gs_app_list_index (addons, j); ++ if (gs_app_get_state (addon) == GS_APP_STATE_INSTALLED) { ++ const gchar *addon_source = gs_app_get_source_default (addon); ++ if (addon_source != NULL) { ++ g_ptr_array_add (packages, g_strdup (addon_source)); ++ g_ptr_array_add (touched_apps, g_object_ref (addon)); ++ gs_app_set_state (addon, GS_APP_STATE_REMOVING); ++ } ++ } ++ } ++ } ++ ++ if (packages->len == 0) { ++ g_task_return_boolean (task, TRUE); ++ return; ++ } ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ return; ++ } ++ ++ g_atomic_int_inc (&self->calling_rpm); ++ ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = rpm_proxy != NULL; ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "strict", ++ g_variant_new_boolean (FALSE)); ++ ++ /* NULL-terminate the array, to be like strv */ ++ g_ptr_array_add (packages, NULL); ++ ++ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error); ++ ++ success = success && ++ gs_dnf5_rpm_rpm_call_remove_sync (rpm_proxy, ++ (const gchar * const *) packages->pdata, ++ g_variant_builder_end (options_builder), ++ cancellable, ++ &local_error); ++ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, GS_DNF5_TRANSACTION_FLAG_NONE, NULL, cancellable, &local_error); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ g_atomic_int_dec_and_test (&self->calling_rpm); ++ ++ if (success) { ++ gs_dnf5_mark_session_needs_reset (self); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ gs_dnf5_report_error (self, local_error, (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0); ++ } ++ ++ for (guint i = 0; i < touched_apps->len; i++) { ++ GsApp *app = g_ptr_array_index (touched_apps, i); ++ if (gs_app_get_state (app) == GS_APP_STATE_REMOVING) { ++ if (success) ++ gs_app_set_state (app, GS_APP_STATE_UNKNOWN); ++ else ++ gs_app_set_state_recover (app); ++ } ++ } ++ ++ if (success) ++ g_task_return_boolean (task, TRUE); ++ else ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++} ++ ++static void ++gs_plugin_dnf5_uninstall_apps_async (GsPlugin *plugin, ++ GsAppList *apps, ++ GsPluginUninstallAppsFlags flags, ++ GsPluginProgressCallback progress_callback, ++ gpointer progress_user_data, ++ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, ++ gpointer app_needs_user_action_data, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0; ++ g_autoptr(GTask) task = NULL; ++ ++ task = gs_plugin_uninstall_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, ++ app_needs_user_action_callback, app_needs_user_action_data, ++ cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_uninstall_apps_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_uninstall_apps_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_uninstall_apps_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_update_apps_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPlugin *plugin = GS_PLUGIN (self); ++ GsPluginUpdateAppsData *data = task_data; ++ g_autoptr(GError) local_error = NULL; ++ gboolean success; ++ ++ assert_in_worker (self); ++ ++ g_atomic_int_inc (&self->calling_rpm); ++ ++ success = gs_dnf5_install_update (plugin, data->apps, FALSE, ++ (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0, ++ cancellable, &local_error); ++ ++ g_atomic_int_dec_and_test (&self->calling_rpm); ++ ++ if (success) ++ g_task_return_boolean (task, TRUE); ++ else ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++} ++ ++static void ++gs_plugin_dnf5_update_apps_async (GsPlugin *plugin, ++ GsAppList *apps, ++ GsPluginUpdateAppsFlags flags, ++ GsPluginProgressCallback progress_callback, ++ gpointer progress_user_data, ++ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, ++ gpointer app_needs_user_action_data, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0; ++ g_autoptr(GTask) task = NULL; ++ ++ task = gs_plugin_update_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, ++ app_needs_user_action_callback, app_needs_user_action_data, ++ cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_update_apps_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_update_apps_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_update_apps_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++static gboolean ++gs_dnf5_manage_repository_sync (GsPluginDnf5 *self, ++ GsApp *repository, ++ gboolean enable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; ++ gboolean success; ++ ++ if (!gs_app_has_management_plugin (repository, GS_PLUGIN (self))) ++ return TRUE; ++ ++ if (gs_app_get_kind (repository) != AS_COMPONENT_KIND_REPOSITORY) { ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "wrong app kind (%s) passed to manage_repository", ++ as_component_kind_to_string (gs_app_get_kind (repository))); ++ return FALSE; ++ } ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (error); ++ return FALSE; ++ } ++ ++ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ success = repo_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); ++ } ++ ++ if (success) { ++ const gchar *ids[2] = { NULL, NULL }; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (repo_proxy), G_MAXINT); ++ ++ ids[0] = gs_app_get_id (repository); ++ ++ if (enable) ++ success = gs_dnf5_rpm_repo_call_enable_sync (repo_proxy, ids, cancellable, error); ++ else ++ success = gs_dnf5_rpm_repo_call_disable_sync (repo_proxy, ids, cancellable, error); ++ ++ if (success) { ++ if (enable) ++ gs_app_set_state (repository, GS_APP_STATE_INSTALLED); ++ else ++ gs_app_set_state (repository, GS_APP_STATE_AVAILABLE); ++ } else { ++ gs_dnf5_convert_error (error); ++ g_prefix_error (error, "Failed to call %s: ", enable ? "enable" : "disable"); ++ } ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ return success; ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_enable_repository_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPluginManageRepositoryData *data = task_data; ++ g_autoptr(GError) local_error = NULL; ++ ++ assert_in_worker (self); ++ ++ if (gs_dnf5_manage_repository_sync (self, data->repository, TRUE, cancellable, &local_error)) { ++ g_task_return_boolean (task, TRUE); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_enable_repository_async (GsPlugin *plugin, ++ GsApp *repository, ++ GsPluginManageRepositoryFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0; ++ g_autoptr(GTask) task = NULL; ++ ++ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, ++ cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_enable_repository_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_enable_repository_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_enable_repository_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_disable_repository_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPluginManageRepositoryData *data = task_data; ++ g_autoptr(GError) local_error = NULL; ++ ++ assert_in_worker (self); ++ ++ if (gs_dnf5_manage_repository_sync (self, data->repository, FALSE, cancellable, &local_error)) { ++ g_task_return_boolean (task, TRUE); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_disable_repository_async (GsPlugin *plugin, ++ GsApp *repository, ++ GsPluginManageRepositoryFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0; ++ g_autoptr(GTask) task = NULL; ++ ++ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, ++ cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_disable_repository_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_disable_repository_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_disable_repository_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++static gboolean ++gs_dnf5_pick_rpm_desktop_file_cb (GsPlugin *plugin, ++ GsApp *app, ++ const gchar *filename, ++ GKeyFile *key_file, ++ gpointer user_data) ++{ ++ return strstr (filename, "/snapd/") == NULL && ++ strstr (filename, "/snap/") == NULL && ++ strstr (filename, "/flatpak/") == NULL && ++ g_key_file_has_group (key_file, "Desktop Entry") && ++ !g_key_file_has_key (key_file, "Desktop Entry", "X-Flatpak", NULL) && ++ !g_key_file_has_key (key_file, "Desktop Entry", "X-SnapInstanceName", NULL); ++} ++ ++static void ++gs_plugin_dnf5_launch_async (GsPlugin *plugin, ++ GsApp *app, ++ GsPluginLaunchFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ gs_plugin_app_launch_filtered_async (plugin, app, flags, ++ gs_dnf5_pick_rpm_desktop_file_cb, NULL, ++ cancellable, ++ callback, user_data); ++} ++ ++static gboolean ++gs_plugin_dnf5_launch_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return gs_plugin_app_launch_filtered_finish (plugin, result, error); ++} ++ ++static gboolean ++gs_dnf5_file_to_app_sync (GsPluginDnf5 *self, ++ GsAppList *list, ++ GFile *file, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autofree gchar *content_type = NULL; ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ gboolean success = TRUE; ++ const gchar *mimetypes[] = { ++ "application/x-app-package", ++ "application/x-deb", ++ "application/vnd.debian.binary-package", ++ "application/x-redhat-package-manager", ++ "application/x-rpm", ++ NULL }; ++ ++ /* does this match any of the mimetypes we support */ ++ content_type = gs_utils_get_content_type (file, cancellable, error); ++ if (content_type == NULL) ++ return FALSE; ++ if (!g_strv_contains (mimetypes, content_type)) ++ return TRUE; ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (error); ++ return FALSE; ++ } ++ ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ success = rpm_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); ++ } ++ ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariantBuilder) patterns_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ g_autofree gchar *file_uri = NULL; ++ ++ file_uri = g_file_get_path (file); ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ ++ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (patterns_builder, "s", file_uri); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "patterns", ++ g_variant_builder_end (patterns_builder)); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ gs_dnf5_dup_package_attrs ()); ++ g_variant_builder_add (options_builder, "{sv}", "with_provides", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_filenames", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "with_src", ++ g_variant_new_boolean (FALSE)); ++ g_variant_builder_add (options_builder, "{sv}", "scope", ++ g_variant_new_string ("all")); ++ ++ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ error); ++ ++ if (success) { ++ g_autofree gchar *basename = g_file_get_basename (file); ++ ReadPackageData rpd = { 0, }; ++ rpd.list = list; ++ rpd.set_state = GS_APP_STATE_AVAILABLE_LOCAL; ++ rpd.nevra_to_app = NULL; ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, error); ++ ++ if (success) { ++ for (guint i = 0; i < gs_app_list_length (list); i++) { ++ GsApp *app = gs_app_list_index (list, i); ++ /* it's already downloaded, because it's a local file */ ++ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); ++ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE); ++ gs_app_set_local_file (app, file); ++ gs_app_set_metadata (app, "GnomeSoftware::packagename-value", basename); ++ } ++ } ++ } else { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to call list: "); ++ } ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ return success; ++} ++ ++static void ++gs_dnf5_file_to_app_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ g_autoptr(GsAppList) list = gs_app_list_new (); ++ g_autoptr(GError) local_error = NULL; ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPluginFileToAppData *data = task_data; ++ ++ if (gs_dnf5_file_to_app_sync (self, list, data->file, cancellable, &local_error)) { ++ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); ++ } else if (local_error != NULL) { ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } else { ++ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); ++ } ++} ++ ++static void ++gs_plugin_dnf5_file_to_app_async (GsPlugin *plugin, ++ GFile *file, ++ GsPluginFileToAppFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ g_autoptr(GTask) task = NULL; ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ gboolean interactive = (flags & GS_PLUGIN_FILE_TO_APP_FLAGS_INTERACTIVE) != 0; ++ ++ task = gs_plugin_file_to_app_data_new_task (plugin, file, flags, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_file_to_app_async); ++ ++ /* Queue a job to get the apps. */ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_file_to_app_thread_cb, g_steal_pointer (&task)); ++} ++ ++static GsAppList * ++gs_plugin_dnf5_file_to_app_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_pointer (G_TASK (result), error); ++} ++ ++static gboolean ++gs_dnf5_system_upgrade_sync (GsPluginDnf5 *self, ++ const gchar *releasever, ++ GsApp *progress_app, ++ gboolean download_only, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GsAppList) list = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5Offline) offline_proxy = NULL; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ gboolean success; ++ ++ session_path = gs_dnf5_open_session (self, releasever, &session_manager, cancellable, error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (error); ++ return FALSE; ++ } ++ ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ success = rpm_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); ++ } ++ ++ if (success) { ++ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ error); ++ success = offline_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to create Offline proxy: "); ++ } ++ } ++ ++ if (success) { ++ gboolean pending = FALSE; ++ g_autoptr(GVariant) transaction_status = NULL; ++ ++ if (gs_dnf5_offline_call_get_status_sync (offline_proxy, &pending, &transaction_status, cancellable, error) && ++ pending && transaction_status != NULL) { ++ const gchar *target_releasever = NULL; ++ gboolean found = g_variant_lookup (transaction_status, "target_releasever", "&s", &target_releasever); ++ if (found && g_strcmp0 (target_releasever, releasever) == 0) { ++ g_debug ("There is prepared an upgrade for version '%s' already, skipping preparation", releasever); ++ gs_dnf5_close_session (self, session_manager, session_path); ++ return TRUE; ++ } else if (found && target_releasever != NULL) { ++ g_debug ("There is prepared an update for version '%s', but needs version '%s', redo preparation", target_releasever, releasever); ++ } ++ } ++ } ++ ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ ++ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, error); ++ ++ success = success && ++ gs_dnf5_rpm_rpm_call_system_upgrade_sync (rpm_proxy, ++ g_variant_builder_end (options_builder), ++ cancellable, ++ error); ++ ++ if (success) { ++ success = gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, GS_DNF5_TRANSACTION_FLAG_OFFLINE, NULL, cancellable, error); ++ ++ if (success && !download_only) { ++ g_autofree gchar *op_error_msg = NULL; ++ gboolean op_success = FALSE; ++ ++ /* ensure the upgrade is finished with the 'reboot' action */ ++ success = gs_dnf5_offline_call_set_finish_action_sync (offline_proxy, "reboot", &op_success, &op_error_msg, cancellable, error); ++ if (success && !op_success) { ++ success = FALSE; ++ if (op_error_msg != NULL) { ++ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg); ++ g_prefix_error_literal (error, "Failed to set finish action: "); ++ } ++ } else if (!success) { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to set finish action: "); ++ } ++ } ++ } else { ++ gs_dnf5_convert_error (error); ++ g_prefix_error_literal (error, "Failed to call system_upgrade: "); ++ } ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ return success; ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_download_upgrade_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPluginDownloadUpgradeData *data = task_data; ++ g_autoptr(GError) local_error = NULL; ++ ++ assert_in_worker (self); ++ ++ gs_app_set_state (data->app, GS_APP_STATE_DOWNLOADING); ++ ++ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, TRUE, cancellable, &local_error)) { ++ gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE); ++ g_task_return_boolean (task, TRUE); ++ } else { ++ gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE); ++ gs_dnf5_convert_error (&local_error); ++ gs_dnf5_report_error (self, local_error, (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_download_upgrade_async (GsPlugin *plugin, ++ GsApp *app, ++ GsPluginDownloadUpgradeFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ g_autoptr(GTask) task = NULL; ++ gboolean interactive = (flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0; ++ ++ task = gs_plugin_download_upgrade_data_new_task (plugin, app, flags, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_download_upgrade_async); ++ ++ /* only process this app if was created by this plugin */ ++ if (!gs_app_has_management_plugin (app, plugin)) { ++ g_task_return_boolean (task, TRUE); ++ return; ++ } ++ ++ /* check is distro-upgrade */ ++ if (gs_app_get_kind (app) != AS_COMPONENT_KIND_OPERATING_SYSTEM) { ++ g_task_return_boolean (task, TRUE); ++ return; ++ } ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_download_upgrade_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_download_upgrade_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++/* Run in @worker. */ ++static void ++gs_dnf5_trigger_upgrade_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPluginTriggerUpgradeData *data = task_data; ++ g_autoptr(GError) local_error = NULL; ++ ++ assert_in_worker (self); ++ ++ gs_app_set_state (data->app, GS_APP_STATE_PENDING_INSTALL); ++ ++ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, FALSE, cancellable, &local_error)) { ++ gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE); ++ g_task_return_boolean (task, TRUE); ++ } else { ++ gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE); ++ gs_dnf5_convert_error (&local_error); ++ gs_dnf5_report_error (self, local_error, (data->flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_trigger_upgrade_async (GsPlugin *plugin, ++ GsApp *app, ++ GsPluginTriggerUpgradeFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ g_autoptr(GTask) task = NULL; ++ gboolean interactive = (flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0; ++ ++ task = gs_plugin_trigger_upgrade_data_new_task (plugin, app, flags, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_trigger_upgrade_async); ++ ++ /* only process this app if was created by this plugin */ ++ if (!gs_app_has_management_plugin (app, plugin)) { ++ g_task_return_boolean (task, TRUE); ++ return; ++ } ++ ++ /* check is distro-upgrade */ ++ if (gs_app_get_kind (app) != AS_COMPONENT_KIND_OPERATING_SYSTEM) { ++ g_task_return_boolean (task, TRUE); ++ return; ++ } ++ ++ gs_app_set_state (app, GS_APP_STATE_PENDING_INSTALL); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_trigger_upgrade_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_trigger_upgrade_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++static void ++gs_plugin_dnf5_dispose (GObject *object) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (object); ++ ++ if (self->rpm_transaction_watch_id != 0) { ++ g_bus_unwatch_name (self->rpm_transaction_watch_id); ++ self->rpm_transaction_watch_id = 0; ++ } ++ ++ g_clear_object (&self->worker); ++ g_clear_object (&self->connection); ++ g_clear_object (&self->rpm_transaction_proxy); ++ ++ g_mutex_lock (&self->session_data.mutex); ++ if (self->session_data.autoclose_timer) { ++ g_source_remove (self->session_data.autoclose_timer); ++ self->session_data.autoclose_timer = 0; ++ } ++ ++ if (self->session_data.proxy != NULL) { ++ gs_dnf5_close_session_real (self->session_data.proxy, self->session_data.object_path); ++ ++ g_clear_object (&self->session_data.proxy); ++ g_clear_pointer (&self->session_data.object_path, g_free); ++ } ++ g_mutex_unlock (&self->session_data.mutex); ++ ++ g_mutex_lock (&self->dependency_sizes_data.mutex); ++ g_clear_pointer (&self->dependency_sizes_data.apps, g_ptr_array_unref); ++ g_clear_object (&self->dependency_sizes_data.cancellable); ++ g_mutex_unlock (&self->dependency_sizes_data.mutex); ++ ++ G_OBJECT_CLASS (gs_plugin_dnf5_parent_class)->dispose (object); ++} ++ ++static void ++gs_plugin_dnf5_finalize (GObject *object) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (object); ++ ++ g_mutex_clear (&self->session_data.mutex); ++ g_cond_clear (&self->session_data.cond); ++ g_mutex_clear (&self->dependency_sizes_data.mutex); ++ ++ G_OBJECT_CLASS (gs_plugin_dnf5_parent_class)->finalize (object); ++} ++ ++static void ++gs_plugin_dnf5_class_init (GsPluginDnf5Class *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass); ++ ++ object_class->dispose = gs_plugin_dnf5_dispose; ++ object_class->finalize = gs_plugin_dnf5_finalize; ++ ++ plugin_class->setup_async = gs_plugin_dnf5_setup_async; ++ plugin_class->setup_finish = gs_plugin_dnf5_setup_finish; ++ plugin_class->shutdown_async = gs_plugin_dnf5_shutdown_async; ++ plugin_class->shutdown_finish = gs_plugin_dnf5_shutdown_finish; ++ plugin_class->list_apps_async = gs_plugin_dnf5_list_apps_async; ++ plugin_class->list_apps_finish = gs_plugin_dnf5_list_apps_finish; ++ plugin_class->refine_async = gs_plugin_dnf5_refine_async; ++ plugin_class->refine_finish = gs_plugin_dnf5_refine_finish; ++ plugin_class->refresh_metadata_async = gs_plugin_dnf5_refresh_metadata_async; ++ plugin_class->refresh_metadata_finish = gs_plugin_dnf5_refresh_metadata_finish; ++ plugin_class->update_apps_async = gs_plugin_dnf5_update_apps_async; ++ plugin_class->update_apps_finish = gs_plugin_dnf5_update_apps_finish; ++ plugin_class->enable_repository_async = gs_plugin_dnf5_enable_repository_async; ++ plugin_class->enable_repository_finish = gs_plugin_dnf5_enable_repository_finish; ++ plugin_class->disable_repository_async = gs_plugin_dnf5_disable_repository_async; ++ plugin_class->disable_repository_finish = gs_plugin_dnf5_disable_repository_finish; ++ plugin_class->install_apps_async = gs_plugin_dnf5_install_apps_async; ++ plugin_class->install_apps_finish = gs_plugin_dnf5_install_apps_finish; ++ plugin_class->uninstall_apps_async = gs_plugin_dnf5_uninstall_apps_async; ++ plugin_class->uninstall_apps_finish = gs_plugin_dnf5_uninstall_apps_finish; ++ plugin_class->launch_async = gs_plugin_dnf5_launch_async; ++ plugin_class->launch_finish = gs_plugin_dnf5_launch_finish; ++ plugin_class->file_to_app_async = gs_plugin_dnf5_file_to_app_async; ++ plugin_class->file_to_app_finish = gs_plugin_dnf5_file_to_app_finish; ++ plugin_class->download_upgrade_async = gs_plugin_dnf5_download_upgrade_async; ++ plugin_class->download_upgrade_finish = gs_plugin_dnf5_download_upgrade_finish; ++ plugin_class->trigger_upgrade_async = gs_plugin_dnf5_trigger_upgrade_async; ++ plugin_class->trigger_upgrade_finish = gs_plugin_dnf5_trigger_upgrade_finish; ++} ++ ++static void ++gs_plugin_dnf5_init (GsPluginDnf5 *self) ++{ ++ GsPlugin *plugin = GS_PLUGIN (self); ++ ++ g_mutex_init (&self->session_data.mutex); ++ g_cond_init (&self->session_data.cond); ++ g_mutex_init (&self->dependency_sizes_data.mutex); ++ ++ /* getting app properties from appstream is quicker */ ++ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "appstream"); ++ ++ /* like appstream, we need the icon plugin to load cached icons into pixbufs */ ++ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_BEFORE, "icons"); ++ ++ /* prioritize over packagekit */ ++ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit"); ++ ++ /* set name of MetaInfo file */ ++ gs_plugin_set_appstream_id (plugin, "org.gnome.Software.Plugin.Dnf5"); ++} ++ ++GType ++gs_plugin_query_type (void) ++{ ++ return GS_TYPE_PLUGIN_DNF5; ++} +diff --git a/plugins/dnf5/gs-plugin-dnf5.h b/plugins/dnf5/gs-plugin-dnf5.h +new file mode 100644 +index 000000000..319d5843c +--- /dev/null ++++ b/plugins/dnf5/gs-plugin-dnf5.h +@@ -0,0 +1,20 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * vi:set noexpandtab tabstop=8 shiftwidth=8: ++ * ++ * Copyright (C) 2024 Red Hat ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#pragma once ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define GS_TYPE_PLUGIN_DNF5 (gs_plugin_dnf5_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GsPluginDnf5, gs_plugin_dnf5, GS, PLUGIN_DNF5, GsPlugin) ++ ++G_END_DECLS +diff --git a/plugins/dnf5/meson.build b/plugins/dnf5/meson.build +new file mode 100644 +index 000000000..6059a9da1 +--- /dev/null ++++ b/plugins/dnf5/meson.build +@@ -0,0 +1,39 @@ ++cargs = ['-DG_LOG_DOMAIN="GsDnf5"'] ++ ++dnf5_generated = gnome.gdbus_codegen( ++ 'gs-dnf5-generated', ++ sources: [ 'org.rpm.dnf.v0.Advisory.xml', ++ 'org.rpm.dnf.v0.Base.xml', ++ 'org.rpm.dnf.v0.Goal.xml', ++ 'org.rpm.dnf.v0.Offline.xml', ++ 'org.rpm.dnf.v0.rpm.Repo.xml', ++ 'org.rpm.dnf.v0.rpm.Rpm.xml', ++ 'org.rpm.dnf.v0.SessionManager.xml' ++ ], ++ interface_prefix : 'org.rpm.dnf.v0', ++ namespace : 'GsDnf5' ++) ++ ++dnf5_generated += gnome.gdbus_codegen( ++ 'gs-dnf5-rpm-generated', ++ sources: [ 'org.rpm.Transaction.xml' ], ++ interface_prefix : 'org.rpm', ++ namespace : 'GsDnf5Rpm' ++) ++ ++shared_module( ++ 'gs_plugin_dnf5', ++ dnf5_generated, ++ sources : [ ++ 'gs-dnf5-progress-helper.c', ++ 'gs-plugin-dnf5.c' ++ ], ++ include_directories : [ ++ include_directories('../..'), ++ include_directories('../../lib'), ++ ], ++ install : true, ++ install_dir: plugin_dir, ++ c_args : cargs, ++ dependencies : [ plugin_libs ], ++) +diff --git a/plugins/dnf5/org.rpm.Transaction.xml b/plugins/dnf5/org.rpm.Transaction.xml +new file mode 100644 +index 000000000..7d0cbab48 +--- /dev/null ++++ b/plugins/dnf5/org.rpm.Transaction.xml +@@ -0,0 +1,26 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml b/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml +new file mode 100644 +index 000000000..6aecee9fa +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml +@@ -0,0 +1,74 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/dnf5/org.rpm.dnf.v0.Base.xml b/plugins/dnf5/org.rpm.dnf.v0.Base.xml +new file mode 100644 +index 000000000..dfdc1745a +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.Base.xml +@@ -0,0 +1,153 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/dnf5/org.rpm.dnf.v0.Goal.xml b/plugins/dnf5/org.rpm.dnf.v0.Goal.xml +new file mode 100644 +index 000000000..f1ba08953 +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.Goal.xml +@@ -0,0 +1,118 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/dnf5/org.rpm.dnf.v0.Offline.xml b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml +new file mode 100644 +index 000000000..083b5b17c +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml +@@ -0,0 +1,82 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml b/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml +new file mode 100644 +index 000000000..a0facd692 +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml +@@ -0,0 +1,69 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml +new file mode 100644 +index 000000000..13c4cf1f8 +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml +@@ -0,0 +1,85 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml +new file mode 100644 +index 000000000..5df68cd91 +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml +@@ -0,0 +1,468 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/plugins/meson.build b/plugins/meson.build +index a108e47e5..a131a5cd3 100644 +--- a/plugins/meson.build ++++ b/plugins/meson.build +@@ -16,6 +16,9 @@ subdir('fedora-pkgdb-collections') + if get_option('dkms') + subdir('dkms') + endif ++if get_option('dnf5') ++ subdir('dnf5') ++endif + if get_option('eos_updater') + subdir('eos-updater') + endif +diff --git a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c +index 669bd7fdb..d6ae9a268 100644 +--- a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c ++++ b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c +@@ -160,6 +160,7 @@ gs_plugin_rpm_ostree_init (GsPluginRpmOstree *self) + * more sense to use a custom plugin instead of using PackageKit. + */ + gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_CONFLICTS, "packagekit"); ++ gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_CONFLICTS, "dnf5"); + + /* need pkgname */ + gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_RUN_AFTER, "appstream"); +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 71aa486e2..d3e909dee 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -112,6 +112,7 @@ lib/gs-utils.c + src/org.gnome.Software.desktop.in + plugins/dkms/gs-plugin-dkms.c + plugins/core/gs-plugin-generic-updates.c ++plugins/dnf5/gs-plugin-dnf5.c + plugins/eos-updater/gs-plugin-eos-updater.c + plugins/epiphany/gs-plugin-epiphany.c + plugins/epiphany/org.gnome.Software.Plugin.Epiphany.metainfo.xml.in diff --git a/gnome-software.spec b/gnome-software.spec index 763ed62..0e01617 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -17,6 +17,9 @@ # Disable DKMS/akmods support for RHEL builds %bcond dkms %[!0%{?rhel}] +%bcond packagekit 0 +%bcond dnf5 1 + # this is not a library version %define gs_plugin_version 22 @@ -26,13 +29,15 @@ Name: gnome-software Version: 48.1 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later URL: https://apps.gnome.org/Software Source0: https://download.gnome.org/sources/gnome-software/48/%{name}-%{tarball_version}.tar.xz +Patch: 0001-dnf5-plugin.patch + # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 %if 0%{?fedora} || 0%{?rhel} >= 10 @@ -65,7 +70,9 @@ BuildRequires: pkgconfig(libsoup-3.0) BuildRequires: pkgconfig(malcontent-0) %endif BuildRequires: pkgconfig(ostree-1) +%if %{with packagekit} BuildRequires: pkgconfig(packagekit-glib2) >= %{packagekit_version} +%endif BuildRequires: pkgconfig(polkit-gobject-1) BuildRequires: pkgconfig(rpm) %if %{with rpmostree} @@ -79,6 +86,11 @@ Requires: appstream%{?_isa} >= %{appstream_version} %if %{with webapps} Requires: epiphany-runtime%{?_isa} %endif +%if %{with dnf5} +Requires: dnf5daemon-server%{?_isa} +Requires: libdnf5-plugin-appstream%{?_isa} +Requires: rpm-plugin-dbus-announce%{?_isa} +%endif Requires: flatpak%{?_isa} >= %{flatpak_version} Requires: flatpak-libs%{?_isa} >= %{flatpak_version} Requires: fwupd%{?_isa} >= %{fwupd_version} @@ -97,7 +109,9 @@ Requires: libadwaita >= %{libadwaita_version} Requires: librsvg2%{?_isa} Requires: libxmlb%{?_isa} >= %{libxmlb_version} +%if %{with packagekit} Recommends: PackageKit%{?_isa} >= %{packagekit_version} +%endif Recommends: %{name}-fedora-langpacks Obsoletes: gnome-software-snap < 3.33.1 @@ -149,8 +163,17 @@ This package includes the rpm-ostree backend. -Dmalcontent=false \ %endif -Dgudev=true \ +%if %{with packagekit} -Dpackagekit=true \ -Dpackagekit_autoremove=true \ +%else + -Dpackagekit=false \ +%endif +%if %{with dnf5} + -Ddnf5=true \ +%else + -Ddnf5=false \ +%endif -Dexternal_appstream=false \ %if %{with rpmostree} -Drpm_ostree=true \ @@ -232,6 +255,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %dir %{_libdir}/gnome-software/plugins-%{gs_plugin_version} %{_libdir}/gnome-software/libgnomesoftware.so.%{gs_plugin_version} %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_appstream.so +%if %{with dnf5} +%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_dnf5.so +%endif %if %{with webapps} %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_epiphany.so %endif @@ -246,7 +272,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %endif %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_modalias.so %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_os-release.so +%if %{with packagekit} %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_packagekit.so +%endif %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_provenance-license.so %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_provenance.so %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_repos.so @@ -256,7 +284,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %dir %{_datadir}/swcatalog/xml %{_datadir}/swcatalog/xml/gnome-pwa-list-foss.xml %endif +%if %{with packagekit} %{_datadir}/dbus-1/services/org.freedesktop.PackageKit.service +%endif %{_datadir}/dbus-1/services/org.gnome.Software.service %{_datadir}/gnome-shell/search-providers/org.gnome.Software-search-provider.ini %{_datadir}/glib-2.0/schemas/org.gnome.software.gschema.xml @@ -288,6 +318,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Tue Apr 29 2025 Milan Crha - 48.1-2 +- Switch from PackageKit to DNF5 plugin + * Fri Apr 11 2025 Milan Crha - 48.1-1 - Update to 48.1 From b57929aa0d37a6d3bd613fb4091556644195ad58 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 2 Jun 2025 13:51:22 +0200 Subject: [PATCH 18/44] Require dnf5daemon-server-polkit for the dnf5 plugin It will be in pair with PackageKit, where the wheel users can install packages without being asked for the credentials. --- gnome-software.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/gnome-software.spec b/gnome-software.spec index 0e01617..e840e8d 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -88,6 +88,7 @@ Requires: epiphany-runtime%{?_isa} %endif %if %{with dnf5} Requires: dnf5daemon-server%{?_isa} +Requires: dnf5daemon-server-polkit Requires: libdnf5-plugin-appstream%{?_isa} Requires: rpm-plugin-dbus-announce%{?_isa} %endif From 315c97814f1b4f0a3bd5e5f1e66709f956ba1b5a Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 2 Jun 2025 13:53:45 +0200 Subject: [PATCH 19/44] Update to 48.2 --- gnome-software.spec | 7 +++++-- sources | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index e840e8d..f36f610 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -28,8 +28,8 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 48.1 -Release: 2%{?dist} +Version: 48.2 +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -319,6 +319,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Mon Jun 02 2025 Milan Crha - 48.2-1 +- Update to 48.2 + * Tue Apr 29 2025 Milan Crha - 48.1-2 - Switch from PackageKit to DNF5 plugin diff --git a/sources b/sources index 31b7ac3..da03639 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-48.1.tar.xz) = 7cb1e7a7527e65e6283916cecee55fa4e65d71e538ced65af488d28e94801755d2ab961d2f4603cec0f781893e298fee109df4a839ee99fc3de2cefb82f25a97 +SHA512 (gnome-software-48.2.tar.xz) = 3a03f475de56cb2ba534a0a7eaaaeb21677cf27a54db03bb84e3953dce796bfc437daf59b41155884260006c18562c50d7e01056aeb190a18449cbcbf2f5120c From bd322e2006e57dca80cc555b5d4acd85e3f0d4d7 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 23 Jun 2025 13:49:59 +0200 Subject: [PATCH 20/44] Switch %autosetup from gendiff to git backups It's easier to build patches on top of the current code when multiple files are involved. --- gnome-software.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gnome-software.spec b/gnome-software.spec index f36f610..1c0a1a1 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -48,6 +48,7 @@ BuildRequires: docbook-style-xsl BuildRequires: desktop-file-utils BuildRequires: gcc BuildRequires: gettext +BuildRequires: git-core BuildRequires: gtk-doc BuildRequires: itstool BuildRequires: libxslt @@ -153,7 +154,7 @@ This package includes the rpm-ostree backend. %endif %prep -%autosetup -p1 -S gendiff -n %{name}-%{tarball_version} +%autosetup -p1 -S git -n %{name}-%{tarball_version} %build %meson \ From 9d8fd61e9a565b9e23073f1e7a0bc79d4c15de35 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 27 Jun 2025 09:10:50 +0200 Subject: [PATCH 21/44] Update to 49.alpha --- 0001-dnf5-plugin.patch | 112 +++-- gnome-software.spec | 1021 +--------------------------------------- sources | 2 +- 3 files changed, 76 insertions(+), 1059 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index f7b5b67..b2e8117 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,11 +1,11 @@ -at commit 31a1ad73cc4fa28b0d352a7ac392bec76e9c10a4 -Date: Wed Apr 2 14:21:03 2025 +0200 +at commit f3042a5f73fc6b04e26ceca5a1a469879af30fd3 +Date: Fri Jun 20 11:20:33 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt -index bc32ba561..523f572cd 100644 +index cd49cf529..27fa15e8b 100644 --- a/meson_options.txt +++ b/meson_options.txt -@@ -2,6 +2,7 @@ option('tests', type : 'boolean', value : true, description : 'enable tests') +@@ -3,6 +3,7 @@ option('installed_tests', type : 'boolean', value : false, description : 'enable option('man', type : 'boolean', value : true, description : 'enable man pages') option('packagekit', type : 'boolean', value : true, description : 'enable PackageKit support') option('packagekit_autoremove', type : 'boolean', value : false, description : 'autoremove packages in PackageKit') @@ -589,10 +589,10 @@ index 000000000..73d528d54 +G_END_DECLS diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c new file mode 100644 -index 000000000..93f22dc4f +index 000000000..bae3bde28 --- /dev/null +++ b/plugins/dnf5/gs-plugin-dnf5.c -@@ -0,0 +1,3738 @@ +@@ -0,0 +1,3770 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -691,17 +691,22 @@ index 000000000..93f22dc4f + +static void +gs_dnf5_report_error (GsPluginDnf5 *self, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + const GError *error, + gboolean interactive) +{ + g_autoptr(GsPluginEvent) event = NULL; + ++ if (!event_callback) ++ return; ++ + event = gs_plugin_event_new ("error", error, + NULL); + if (interactive) + gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INTERACTIVE); + gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING); -+ gs_plugin_report_event (GS_PLUGIN (self), event); ++ event_callback (GS_PLUGIN (self), event, event_user_data); +} + +/* The session reset is used to refresh information about the packages, @@ -2186,7 +2191,7 @@ index 000000000..93f22dc4f +{ + GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); + GsAppQueryTristate is_for_update = GS_APP_QUERY_TRISTATE_UNSET; -+ GsAppQueryTristate is_source = GS_APP_QUERY_TRISTATE_UNSET; ++ const AsComponentKind *component_kinds = NULL; + GsPluginListAppsData *data = task_data; + const gchar * const *provides_files = NULL; + const gchar *provides_tag = NULL; @@ -2202,17 +2207,17 @@ index 000000000..93f22dc4f + + if (data->query != NULL) { + is_for_update = gs_app_query_get_is_for_update (data->query); -+ is_source = gs_app_query_get_is_source (data->query); ++ component_kinds = gs_app_query_get_component_kinds (data->query); + provides_files = gs_app_query_get_provides_files (data->query); + provides_type = gs_app_query_get_provides (data->query, &provides_tag); + } + + if ((is_for_update == GS_APP_QUERY_TRISTATE_UNSET && -+ is_source == GS_APP_QUERY_TRISTATE_UNSET && ++ component_kinds == NULL && + provides_tag == NULL && + provides_files == NULL) || + is_for_update == GS_APP_QUERY_TRISTATE_FALSE || -+ is_source == GS_APP_QUERY_TRISTATE_FALSE || ++ (component_kinds != NULL && !gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) || + gs_app_query_get_n_properties_set (data->query) != 1) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Unsupported query"); @@ -2359,7 +2364,7 @@ index 000000000..93f22dc4f + gs_dnf5_convert_error (&local_error); + g_prefix_error_literal (&local_error, "Failed to call Rpm::list: "); + } -+ } else if (is_source == GS_APP_QUERY_TRISTATE_TRUE) { ++ } else if (gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) { + g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; + + repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection, @@ -2534,6 +2539,8 @@ index 000000000..93f22dc4f +gs_plugin_dnf5_list_apps_async (GsPlugin *plugin, + GsAppQuery *query, + GsPluginListAppsFlags flags, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) @@ -2542,7 +2549,7 @@ index 000000000..93f22dc4f + gboolean interactive = (flags & GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE) != 0; + g_autoptr(GTask) task = NULL; + -+ task = gs_plugin_list_apps_data_new_task (plugin, query, flags, ++ task = gs_plugin_list_apps_data_new_task (plugin, query, flags, event_callback, event_user_data, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_list_apps_async); + @@ -2590,7 +2597,7 @@ index 000000000..93f22dc4f + if (str == NULL) + return TRUE; + /* the app itself is in the list too */ -+ if (g_strcmp0 (gs_app_get_source_default (main_app), str) == 0) ++ if (g_strcmp0 (gs_app_get_default_source (main_app), str) == 0) + return TRUE; + + /* always create a new app, no cached can be used here */ @@ -2676,7 +2683,7 @@ index 000000000..93f22dc4f + if (success) { + g_autoptr(GVariantBuilder) options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_autoptr(GVariant) transaction = NULL; -+ const gchar *package_name = gs_app_get_source_default (app); ++ const gchar *package_name = gs_app_get_default_source (app); + const gchar *packages[2] = { NULL, NULL }; + + packages[0] = package_name; @@ -2827,14 +2834,14 @@ index 000000000..93f22dc4f + GsApp *app = gs_app_list_index (data->list, i); + if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && ( + gs_app_get_state (app) == GS_APP_STATE_UNKNOWN || -+ ((data->flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE_DATA) != 0 && ++ ((data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_SIZE_DATA) != 0 && + gs_app_list_length (gs_app_get_related (app)) == 0))) { -+ if (gs_app_get_source_default (app) != NULL && ++ if (gs_app_get_default_source (app) != NULL && + ((gs_app_has_management_plugin (app, NULL) && + gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE && + gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) || + gs_app_has_management_plugin (app, plugin))) -+ g_hash_table_insert (apps, g_strdup (gs_app_get_source_default (app)), g_object_ref (app)); ++ g_hash_table_insert (apps, g_strdup (gs_app_get_default_source (app)), g_object_ref (app)); + } + } + @@ -2914,7 +2921,7 @@ index 000000000..93f22dc4f + success = gs_dnf5_foreach_item (self, result, gs_dnf5_refine_cb, apps, cancellable, &local_error); + + if (success && -+ (data->flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE_DATA) != 0) { ++ (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_SIZE_DATA) != 0) { + /* do not panic when cannot get dependency sizes for the apps */ + if (!gs_dnf5_get_dependency_sizes_sync (self, apps, cancellable, &local_error)) { + g_debug ("Failed to get dependency sizes: %s", local_error->message); @@ -2940,16 +2947,20 @@ index 000000000..93f22dc4f +static void +gs_plugin_dnf5_refine_async (GsPlugin *plugin, + GsAppList *list, -+ GsPluginRefineFlags flags, ++ GsPluginRefineFlags job_flags, ++ GsPluginRefineRequireFlags require_flags, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE); ++ gboolean interactive = (job_flags & GS_PLUGIN_REFINE_FLAGS_INTERACTIVE) != 0; + g_autoptr(GTask) task = NULL; + -+ task = gs_plugin_refine_data_new_task (plugin, list, flags, ++ task = gs_plugin_refine_data_new_task (plugin, list, job_flags, require_flags, ++ event_callback, event_user_data, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_refine_async); + @@ -3050,6 +3061,8 @@ index 000000000..93f22dc4f +gs_plugin_dnf5_refresh_metadata_async (GsPlugin *plugin, + guint64 cache_age_secs, + GsPluginRefreshMetadataFlags flags, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) @@ -3095,6 +3108,8 @@ index 000000000..93f22dc4f + GsAppList *list, + gboolean is_install, + gboolean interactive, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GError **error) +{ @@ -3131,7 +3146,7 @@ index 000000000..93f22dc4f + for (guint i = 0; i < gs_app_list_length (list); i++) { + GsApp *app = gs_app_list_index (list, i); + if (gs_app_has_management_plugin (app, plugin)) { -+ const gchar *source = gs_app_get_source_default (app); ++ const gchar *source = gs_app_get_default_source (app); + if (source != NULL) { + GFile *local_file = gs_app_get_local_file (app); + if (local_file != NULL) @@ -3147,7 +3162,7 @@ index 000000000..93f22dc4f + for (guint j = 0; j < gs_app_list_length (related); j++) { + GsApp *rel_app = gs_app_list_index (related, j); + if (gs_app_has_management_plugin (rel_app, plugin)) { -+ const gchar *source = gs_app_get_source_default (rel_app); ++ const gchar *source = gs_app_get_default_source (rel_app); + if (source != NULL) { + GFile *local_file = gs_app_get_local_file (rel_app); + if (local_file != NULL) @@ -3193,7 +3208,7 @@ index 000000000..93f22dc4f + for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { + GsApp *addon = gs_app_list_index (addons, j); + if (gs_app_get_to_be_installed (addon)) { -+ const gchar *addon_source = gs_app_get_source_default (app); ++ const gchar *addon_source = gs_app_get_default_source (app); + if (addon_source != NULL) { + g_ptr_array_add (packages, g_strdup (addon_source)); + if (is_install) @@ -3282,7 +3297,7 @@ index 000000000..93f22dc4f + gs_dnf5_convert_error (error); + + if (error != NULL && *error != NULL) -+ gs_dnf5_report_error (self, *error, interactive); ++ gs_dnf5_report_error (self, event_callback, event_user_data, *error, interactive); + } + + for (guint i = 0; i < gs_app_list_length (used_apps); i++) { @@ -3341,6 +3356,7 @@ index 000000000..93f22dc4f + + success = gs_dnf5_install_update (plugin, data->apps, TRUE, + (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0, ++ data->event_callback, data->event_user_data, + cancellable, &local_error); + + g_atomic_int_dec_and_test (&self->calling_rpm); @@ -3357,6 +3373,8 @@ index 000000000..93f22dc4f + GsPluginInstallAppsFlags flags, + GsPluginProgressCallback progress_callback, + gpointer progress_user_data, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, + gpointer app_needs_user_action_data, + GCancellable *cancellable, @@ -3368,6 +3386,7 @@ index 000000000..93f22dc4f + g_autoptr(GTask) task = NULL; + + task = gs_plugin_install_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, ++ event_callback, event_user_data, + app_needs_user_action_callback, app_needs_user_action_data, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_install_apps_async); @@ -3418,7 +3437,7 @@ index 000000000..93f22dc4f + g_warn_if_reached (); + continue; + } -+ source = gs_app_get_source_default (app); ++ source = gs_app_get_default_source (app); + if (source == NULL) { + g_task_return_new_error (task, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, + "remove not available"); @@ -3435,7 +3454,7 @@ index 000000000..93f22dc4f + for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { + GsApp *addon = gs_app_list_index (addons, j); + if (gs_app_get_state (addon) == GS_APP_STATE_INSTALLED) { -+ const gchar *addon_source = gs_app_get_source_default (addon); ++ const gchar *addon_source = gs_app_get_default_source (addon); + if (addon_source != NULL) { + g_ptr_array_add (packages, g_strdup (addon_source)); + g_ptr_array_add (touched_apps, g_object_ref (addon)); @@ -3499,7 +3518,8 @@ index 000000000..93f22dc4f + gs_dnf5_mark_session_needs_reset (self); + } else { + gs_dnf5_convert_error (&local_error); -+ gs_dnf5_report_error (self, local_error, (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0); ++ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, ++ local_error, (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0); + } + + for (guint i = 0; i < touched_apps->len; i++) { @@ -3524,6 +3544,8 @@ index 000000000..93f22dc4f + GsPluginUninstallAppsFlags flags, + GsPluginProgressCallback progress_callback, + gpointer progress_user_data, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, + gpointer app_needs_user_action_data, + GCancellable *cancellable, @@ -3535,6 +3557,7 @@ index 000000000..93f22dc4f + g_autoptr(GTask) task = NULL; + + task = gs_plugin_uninstall_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, ++ event_callback, event_user_data, + app_needs_user_action_callback, app_needs_user_action_data, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_uninstall_apps_async); @@ -3570,6 +3593,7 @@ index 000000000..93f22dc4f + + success = gs_dnf5_install_update (plugin, data->apps, FALSE, + (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0, ++ data->event_callback, data->event_user_data, + cancellable, &local_error); + + g_atomic_int_dec_and_test (&self->calling_rpm); @@ -3586,6 +3610,8 @@ index 000000000..93f22dc4f + GsPluginUpdateAppsFlags flags, + GsPluginProgressCallback progress_callback, + gpointer progress_user_data, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, + gpointer app_needs_user_action_data, + GCancellable *cancellable, @@ -3597,6 +3623,7 @@ index 000000000..93f22dc4f + g_autoptr(GTask) task = NULL; + + task = gs_plugin_update_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, ++ event_callback, event_user_data, + app_needs_user_action_callback, app_needs_user_action_data, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_update_apps_async); @@ -3708,6 +3735,8 @@ index 000000000..93f22dc4f +gs_plugin_dnf5_enable_repository_async (GsPlugin *plugin, + GsApp *repository, + GsPluginManageRepositoryFlags flags, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) @@ -3716,7 +3745,7 @@ index 000000000..93f22dc4f + gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0; + g_autoptr(GTask) task = NULL; + -+ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, ++ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, event_callback, event_user_data, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_enable_repository_async); + @@ -3757,6 +3786,8 @@ index 000000000..93f22dc4f +gs_plugin_dnf5_disable_repository_async (GsPlugin *plugin, + GsApp *repository, + GsPluginManageRepositoryFlags flags, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) @@ -3765,7 +3796,7 @@ index 000000000..93f22dc4f + gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0; + g_autoptr(GTask) task = NULL; + -+ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, ++ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, event_callback, event_user_data, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_disable_repository_async); + @@ -3949,6 +3980,8 @@ index 000000000..93f22dc4f +gs_plugin_dnf5_file_to_app_async (GsPlugin *plugin, + GFile *file, + GsPluginFileToAppFlags flags, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) @@ -3957,7 +3990,7 @@ index 000000000..93f22dc4f + GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); + gboolean interactive = (flags & GS_PLUGIN_FILE_TO_APP_FLAGS_INTERACTIVE) != 0; + -+ task = gs_plugin_file_to_app_data_new_task (plugin, file, flags, cancellable, callback, user_data); ++ task = gs_plugin_file_to_app_data_new_task (plugin, file, flags, event_callback, event_user_data, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_file_to_app_async); + + /* Queue a job to get the apps. */ @@ -4106,7 +4139,8 @@ index 000000000..93f22dc4f + } else { + gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE); + gs_dnf5_convert_error (&local_error); -+ gs_dnf5_report_error (self, local_error, (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0); ++ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, ++ local_error, (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0); + g_task_return_error (task, g_steal_pointer (&local_error)); + } +} @@ -4115,6 +4149,8 @@ index 000000000..93f22dc4f +gs_plugin_dnf5_download_upgrade_async (GsPlugin *plugin, + GsApp *app, + GsPluginDownloadUpgradeFlags flags, ++ GsPluginEventCallback event_callback, ++ void *event_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) @@ -4123,7 +4159,7 @@ index 000000000..93f22dc4f + g_autoptr(GTask) task = NULL; + gboolean interactive = (flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0; + -+ task = gs_plugin_download_upgrade_data_new_task (plugin, app, flags, cancellable, callback, user_data); ++ task = gs_plugin_download_upgrade_data_new_task (plugin, app, flags, event_callback, event_user_data, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_download_upgrade_async); + + /* only process this app if was created by this plugin */ @@ -4171,7 +4207,6 @@ index 000000000..93f22dc4f + } else { + gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE); + gs_dnf5_convert_error (&local_error); -+ gs_dnf5_report_error (self, local_error, (data->flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0); + g_task_return_error (task, g_steal_pointer (&local_error)); + } +} @@ -4321,9 +4356,6 @@ index 000000000..93f22dc4f + + /* prioritize over packagekit */ + gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit"); -+ -+ /* set name of MetaInfo file */ -+ gs_plugin_set_appstream_id (plugin, "org.gnome.Software.Plugin.Dnf5"); +} + +GType @@ -5540,10 +5572,10 @@ index a108e47e5..a131a5cd3 100644 subdir('eos-updater') endif diff --git a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c -index 669bd7fdb..d6ae9a268 100644 +index 627a5143e..64451bccc 100644 --- a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c +++ b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c -@@ -160,6 +160,7 @@ gs_plugin_rpm_ostree_init (GsPluginRpmOstree *self) +@@ -168,6 +168,7 @@ gs_plugin_rpm_ostree_init (GsPluginRpmOstree *self) * more sense to use a custom plugin instead of using PackageKit. */ gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_CONFLICTS, "packagekit"); diff --git a/gnome-software.spec b/gnome-software.spec index 1c0a1a1..5f6b255 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -28,13 +28,13 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 48.2 +Version: 49~alpha Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later URL: https://apps.gnome.org/Software -Source0: https://download.gnome.org/sources/gnome-software/48/%{name}-%{tarball_version}.tar.xz +Source0: https://download.gnome.org/sources/gnome-software/49/%{name}-%{tarball_version}.tar.xz Patch: 0001-dnf5-plugin.patch @@ -320,1019 +320,4 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog -* Mon Jun 02 2025 Milan Crha - 48.2-1 -- Update to 48.2 - -* Tue Apr 29 2025 Milan Crha - 48.1-2 -- Switch from PackageKit to DNF5 plugin - -* Fri Apr 11 2025 Milan Crha - 48.1-1 -- Update to 48.1 - -* Fri Mar 14 2025 Milan Crha - 48.0-1 -- Update to 48.0 - -* Mon Mar 03 2025 Milan Crha - 48~rc-1 -- Update to 48.rc - -* Mon Feb 03 2025 Milan Crha - 48~beta-1 -- Update to 48.beta - -* Thu Jan 16 2025 Fedora Release Engineering - 48~alpha3-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild - -* Tue Jan 14 2025 Milan Crha - 48~alpha3-1 -- Update to 48.alpha3 - -* Tue Jan 07 2025 Milan Crha - 48~alpha2-1 -- Update to 48.alpha2 - -* Mon Dec 09 2024 Milan Crha - 47.2-3 -- Resolves: #2272232 (Crash under gs_appstream_gather_merge_data()) - -* Thu Dec 05 2024 Yaakov Selkowitz - 47.2-2 -- Rebuild for fwupd 2.0 - -* Mon Nov 25 2024 Milan Crha - 47.2-1 -- Update to 47.2 - -* Thu Oct 10 2024 Milan Crha - 47.1-1 -- Update to 47.1 - -* Fri Oct 04 2024 Richard Hughes - 47.0-3 -- Rebuild against fwupd 2.0.0 - -* Thu Sep 19 2024 Milan Crha - 47.0-2 -- Resolves: #2312882 (dkms: Fix callback user data in a reload() function) - -* Fri Sep 13 2024 Milan Crha - 47.0-1 -- Update to 47.0 - -* Fri Aug 30 2024 Milan Crha - 47~rc-1 -- Update to 47.rc - -* Fri Aug 02 2024 Milan Crha - 47~beta-1 -- Update to 47.beta -- Build with DKMS/akmods plugin in Fedora - -* Thu Jul 18 2024 Fedora Release Engineering - 47~alpha-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild - -* Fri Jun 28 2024 Milan Crha - 47~alpha-1 -- Update to 47.alpha - -* Fri May 24 2024 Milan Crha - 46.2-1 -- Update to 46.2 - -* Thu Apr 25 2024 Milan Crha - 46.1-1 -- Update to 46.1 - -* Fri Apr 12 2024 Adam Williamson - 46.0-2 -- Backport MR #1949 to fix upgrading - -* Mon Mar 18 2024 Milan Crha - 46.0-1 -- Update to 46.0 - -* Fri Mar 01 2024 Milan Crha - 46~rc-1 -- Update to 46.rc - -* Fri Feb 09 2024 Milan Crha - 46~beta-1 -- Update to 46.beta - -* Fri Jan 26 2024 Milan Crha - 46~alpha-4 -- Resolves: #2260294 (Split fedora-langpacks plugin into a subpackage) - -* Wed Jan 24 2024 Fedora Release Engineering - 46~alpha-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Fri Jan 19 2024 Fedora Release Engineering - 46~alpha-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Fri Jan 05 2024 Milan Crha - 46~alpha-1 -- Update to 46.alpha - -* Fri Dec 01 2023 Milan Crha - 45.2-1 -- Update to 45.2 - -* Tue Nov 07 2023 Neal Gompa - 45.1-3 -- Fix appstream_version macro for prerelease appstream 1.0 package - -* Tue Nov 07 2023 Milan Crha - 45.1-2 -- Add patch to build with appstream 1.0 - -* Fri Oct 20 2023 Milan Crha - 45.1-1 -- Update to 45.1 - -* Fri Sep 15 2023 Milan Crha - 45.0-1 -- Update to 45.0 - -* Fri Sep 01 2023 Milan Crha - 45~rc-1 -- Update to 45.rc - -* Mon Jul 31 2023 Milan Crha - 45~beta-1 -- Update to 45.beta - -* Wed Jul 19 2023 Fedora Release Engineering - 45~alpha-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild - -* Fri Jun 30 2023 Milan Crha - 45~alpha-1 -- Update to 45.alpha - -* Thu Jun 22 2023 Tomas Popela - 44.2-2 -- Disable parental control (through malcontent) and rpm-ostree support in RHEL - -* Fri May 26 2023 Milan Crha - 44.2-1 -- Update to 44.2 - -* Fri May 19 2023 Milan Crha - 44.1-2 -- Rebuild for RPM - -* Fri Apr 21 2023 Milan Crha - 44.1-1 -- Update to 44.1 - -* Sun Mar 26 2023 Yaakov Selkowitz - 44.0-3 -- Fix libsoup runtime dependency - -* Fri Mar 24 2023 Milan Crha - 44.0-2 -- Resolves: #2181367 (Prefer Fedora Flatpaks before RPM before other sources for apps) - -* Fri Mar 17 2023 Milan Crha - 44.0-1 -- Update to 44.0 - -* Fri Mar 03 2023 Milan Crha - 44~rc-1 -- Update to 44.rc - -* Thu Feb 23 2023 Adam Williamson - 44~beta-2 -- Backport MR #1635 to fix update notifications - -* Tue Feb 14 2023 Milan Crha - 44.beta-1 -- Update to 44.beta - -* Thu Feb 09 2023 Michael Catanzaro - 44~alpha-3 -- Switch to libsoup 3 - -* Thu Jan 19 2023 Fedora Release Engineering - 44~alpha-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild - -* Mon Jan 09 2023 Milan Crha - 44.alpha-1 -- Update to 44.alpha - -* Fri Dec 02 2022 Milan Crha - 43.2-1 -- Update to 43.2 - -* Tue Nov 08 2022 Milan Crha - 43.1-3 -- Also skip gnome-pwa-list-foss.xml when building without WebApps - -* Tue Nov 08 2022 Milan Crha - 43.1-2 -- Disable WebApps for RHEL builds - -* Mon Oct 24 2022 Milan Crha - 43.1-1 -- Update to 43.1 - -* Wed Oct 05 2022 Milan Crha - 43.0-3 -- Resolves: #2132292 (rpm-ostree plugin refuses to update) - -* Tue Sep 27 2022 Kalev Lember - 43.0-2 -- Rebuild to fix sysprof-capture symbols leaking into libraries consuming it - -* Fri Sep 16 2022 Milan Crha - 43.0-1 -- Update to 43.0 - -* Tue Sep 13 2022 Milan Crha - 43.rc-2 -- Resolves: #2124869 (Cannot install RPM package file) - -* Fri Sep 02 2022 Milan Crha - 43.rc-1 -- Update to 43.rc - -* Wed Aug 17 2022 Milan Crha - 43.beta-3 -- Resolves: #2119089 (No enough apps to show for the "Editor's Choice" section) - -* Mon Aug 15 2022 Milan Crha - 43.beta-2 -- Add patch for install-queue - -* Fri Aug 05 2022 Milan Crha - 43.beta-1 -- Update to 43.beta - -* Thu Jul 21 2022 Fedora Release Engineering - 43.alpha-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild - -* Thu Jul 07 2022 Adam Williamson - 43.alpha-2 -- Backport MR #1401 to fix issue #1816 and fedora-workstation #107 - -* Fri Jul 01 2022 Milan Crha - 43.alpha-1 -- Update to 43.alpha - -* Fri Jun 17 2022 Richard Hughes - 42.2-4 -- Add patch to make fwupd user requests work - -* Thu Jun 16 2022 David King - 42.2-3 -- Filter private libraries from Provides -- Use pkgconfig for BuildRequires -- Improve directory onwership - -* Mon Jun 13 2022 Milan Crha - 42.2-2 -- Add patch for crash under gs_flatpak_refine_app_unlocked() - -* Mon May 30 2022 Milan Crha - 42.2-1 -- Update to 42.2 -- Add patch to correct order of the setup of the GsShell - -* Wed Apr 27 2022 Milan Crha - 42.1-1 -- Update to 42.1 - -* Fri Mar 18 2022 Milan Crha - 42.0-1 -- Update to 42.0 - -* Thu Mar 10 2022 Milan Crha - 42.rc-2 -- Add upstream patches for gs-download-utils (i#1677 and i#1679) - -* Mon Mar 07 2022 Milan Crha - 42.rc-1 -- Update to 42.rc - -* Mon Feb 21 2022 Milan Crha - 42.beta-3 -- Resolves: #2056082 (Enable PackageKit autoremove option) - -* Wed Feb 16 2022 Milan Crha - 42.beta-2 -- Resolves: #2054939 (Crash on a PackageKit app install) -- Add a temporary workaround for gtk_widget_measure error flood on GsAppRow - -* Fri Feb 11 2022 Milan Crha - 42.beta-1 -- Update to 42.beta - -* Thu Jan 20 2022 Fedora Release Engineering - 42~alpha-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild - -* Fri Jan 07 2022 Milan Crha - 42.alpha-1 -- Update to 42.alpha - -* Fri Dec 03 2021 Milan Crha - 41.2-1 -- Update to 41.2 - -* Fri Oct 29 2021 Milan Crha - 41.1-1 -- Update to 41.1 - -* Tue Oct 19 2021 Milan Crha - 41.0-6 -- Resolves: #2012863 (gs-installed-page: Change section on application state change) - -* Mon Oct 11 2021 Milan Crha - 41.0-5 -- Add patch to mark compulsory only repos, not apps from it - -* Fri Oct 08 2021 Milan Crha - 41.0-4 -- Resolves: #2011176 (flathub repo can't be added through gnome-software) -- Resolves: #2010660 (gs-repos-dialog: Can show also desktop applications) -- Resolves: #2010353 (Optional repos cannot be disabled) - -* Thu Oct 07 2021 Milan Crha - 41.0-3 -- Resolves: #2010740 (Refresh on repository setup change) - -* Mon Oct 04 2021 Milan Crha - 41.0-2 -- Resolves: #2009063 (Correct update notifications) - -* Mon Sep 20 2021 Milan Crha - 41.0-1 -- Update to 41.0 - -* Mon Sep 13 2021 Milan Crha - 41~rc-2 -- Resolves: #2003365 (packagekit: Ensure PkClient::interactive flag being set) - -* Wed Sep 08 2021 Milan Crha - 41~rc-1 -- Update to 41.rc - -* Wed Sep 01 2021 Milan Crha - 41~beta-3 -- Resolves: #1995817 (gs-updates-section: Check also dependencies' download size) - -* Tue Aug 24 2021 Kalev Lember - 41~beta-2 -- Enable parental controls support - -* Fri Aug 13 2021 Milan Crha - 41~beta-1 -- Update to 41.beta - -* Thu Jul 22 2021 Fedora Release Engineering - 41~alpha-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild - -* Wed Jul 21 2021 Milan Crha - 41~alpha-1 -- Update to 41.alpha - -* Mon Jul 12 2021 Milan Crha - 40.3-2 -- Add rpm-ostree patch to hide packages from the search results -- Add patch to implement what-provides search in the Flatpak plugin - -* Mon Jul 12 2021 Milan Crha - 40.3-1 -- Update to 40.3 - -* Wed Jun 23 2021 Milan Crha - 40.2-2 -- Add patch to automatically install application updates (i#1248) - -* Fri Jun 04 2021 Milan Crha - 40.2-1 -- Update to 40.2 - -* Mon May 03 2021 Milan Crha - 40.1-2 -- Add patch for crash under gs_details_page_refresh_all() (i#1227) - -* Mon May 03 2021 Milan Crha - 40.1-1 -- Update to 40.1 - -* Fri Mar 26 2021 Kalev Lember - 40.0-2 -- Rebuild to fix sysprof-capture symbols leaking into libraries consuming it - -* Mon Mar 22 2021 Kalev Lember - 40.0-1 -- Update to 40.0 - -* Thu Mar 18 2021 Adam Williamson - 40~rc-2 -- Backport a couple of bug fixes from upstream (icon display, crash bug) - -* Mon Mar 15 2021 Kalev Lember - 40~rc-1 -- Update to 40.rc - -* Wed Mar 10 2021 Adam Williamson - 40~beta-2 -- Backport MR #643 to fix update notifications on first run (#1930401) - -* Tue Feb 16 2021 Kalev Lember - 40~beta-1 -- Update to 40.beta - -* Mon Feb 08 2021 Richard Hughes - 3.38.1-1 -- New upstream version -- Fix package details not found for some packages -- Ignore harmless warnings when using unusual fwupd versions - -* Tue Jan 26 2021 Fedora Release Engineering - 3.38.0-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild - -* Mon Sep 14 2020 Kalev Lember - 3.38.0-2 -- Revert an optimization that broke packagekit updates - -* Fri Sep 11 2020 Kalev Lember - 3.38.0-1 -- Update to 3.38.0 - -* Tue Sep 01 2020 Kalev Lember - 3.37.92-1 -- Update to 3.37.92 - -* Tue Aug 18 2020 Richard Hughes - 3.36.1-4 -- Rebuild for the libxmlb API bump. - -* Sat Aug 01 2020 Fedora Release Engineering - 3.36.1-3 -- Second attempt - Rebuilt for - https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild - -* Mon Jul 27 2020 Fedora Release Engineering - 3.36.1-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild - -* Fri May 22 2020 Richard Hughes - 3.36.1-1 -- Update to 3.36.1 - -* Tue May 12 2020 Kalev Lember - 3.36.0-2 -- Backport various rpm-ostree backend fixes - -* Wed Mar 11 2020 Kalev Lember - 3.36.0-1 -- Update to 3.36.0 - -* Wed Mar 04 2020 Kalev Lember - 3.35.92-1 -- Update to 3.35.92 - -* Fri Feb 21 2020 Richard Hughes - 3.35.91-2 -- Backport a patch to fix a crash when looking at the application details. - -* Wed Feb 19 2020 Richard Hughes - 3.35.91-1 -- Update to 3.35.91. - -* Tue Jan 28 2020 Fedora Release Engineering - 3.35.2-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild - -* Mon Nov 25 2019 Richard Hughes - 3.35.2-1 -- Update to 3.35.2. - -* Fri Oct 18 2019 Kalev Lember - 3.34.1-6 -- Backport patches to fix a crash in gs_flatpak_get_installation (#1762689) - -* Mon Oct 14 2019 Kalev Lember - 3.34.1-5 -- Update renamed appstream ids for GNOME 3.34 - -* Fri Oct 11 2019 Richard Hughes - 3.34.1-4 -- Backport a simpler to correct the installed applications -- Resolves #1759193 - -* Fri Oct 11 2019 Richard Hughes - 3.34.1-3 -- Backport a better patch to correct the installed applications -- Resolves #1759193 - -* Thu Oct 10 2019 Richard Hughes - 3.34.1-2 -- Backport a patch to correct the applications shown in the installed list -- Resolves #1759193 - -* Mon Oct 07 2019 Kalev Lember - 3.34.1-1 -- Update to 3.34.1 - -* Wed Sep 25 2019 Kalev Lember - 3.34.0-2 -- Fix third party repo enabling not working (#1749566) - -* Mon Sep 09 2019 Kalev Lember - 3.34.0-1 -- Update to 3.34.0 - -* Thu Jul 25 2019 Fedora Release Engineering - 3.32.4-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild - -* Thu Jul 11 2019 Kalev Lember - 3.32.4-1 -- Update to 3.32.4 - -* Thu Jul 11 2019 Richard Hughes - 3.32.3-5 -- Disable the snap plugin. Canonical upstream are not going to be installing - gnome-software in the next LTS, prefering instead to ship a "Snap Store" - rather than GNOME Software. -- Enabling the snap plugin also enables the Snap Store which violated the same - rules which prevented us installing Flathub by default. -- The existing plugin is barely maintained and I don't want to be the one - responsible when it breaks. - -* Thu Jun 13 2019 Kalev Lember - 3.32.3-4 -- Rebuild for accidental libflatpak ABI break - -* Mon Jun 10 22:13:19 CET 2019 Igor Gnatenko - 3.32.3-3 -- Rebuild for RPM 4.15 - -* Mon Jun 10 15:42:01 CET 2019 Igor Gnatenko - 3.32.3-2 -- Rebuild for RPM 4.15 - -* Fri May 24 2019 Kalev Lember - 3.32.3-1 -- Update to 3.32.3 - -* Tue May 07 2019 Kalev Lember - 3.32.2-1 -- Update to 3.32.2 - -* Fri May 03 2019 Kalev Lember - 3.32.1-4 -- Update a patch to final upstream version - -* Tue Apr 30 2019 Kalev Lember - 3.32.1-3 -- Backport a number of rpm-ostree fixes - -* Tue Apr 16 2019 Adam Williamson - 3.32.1-2 -- Rebuild with Meson fix for #1699099 - -* Mon Apr 15 2019 Kalev Lember - 3.32.1-1 -- Update to 3.32.1 - -* Fri Apr 05 2019 Neal Gompa - 3.32.0-6 -- Require snapd instead of the obsolete snapd-login-service for snap subpackage - -* Wed Apr 03 2019 Kalev Lember - 3.32.0-5 -- Switch to system libdnf - -* Fri Mar 29 2019 Kalev Lember - 3.32.0-4 -- Rebuild for new rpm-ostree - -* Fri Mar 15 2019 Kalev Lember - 3.32.0-3 -- Add nm-connection-editor.desktop to Utilities folder (#1686851) - -* Tue Mar 12 2019 Kalev Lember - 3.32.0-2 -- Backport a patch to add shadows to app icons - -* Mon Mar 11 2019 Kalev Lember - 3.32.0-1 -- Update to 3.32.0 - -* Tue Mar 05 2019 Kalev Lember - 3.31.92-1 -- Update to 3.31.92 - -* Thu Feb 28 2019 Kalev Lember - 3.31.90-4 -- Change PackageKit requires to recommends - -* Wed Feb 27 2019 Kalev Lember - 3.31.90-3 -- Remove unneeded dpkg plugin - -* Mon Feb 25 2019 Kalev Lember - 3.31.90-2 -- Split rpm-ostree backend to its own subpackage - -* Sun Feb 24 2019 Kalev Lember - 3.31.90-1 -- Update to 3.31.90 -- Add "anaconda" repo to official repos list (#1679693) - -* Thu Jan 31 2019 Fedora Release Engineering - 3.31.2-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild - -* Wed Jan 16 2019 Kalev Lember - 3.31.2-1 -- Update to 3.31.2 - -* Fri Dec 14 2018 Kalev Lember - 3.31.1-2 -- Fix offline update notifications to show up (#1659231) - -* Tue Oct 09 2018 Kalev Lember - 3.31.1-1 -- Update to 3.31.1 - -* Fri Oct 05 2018 Kalev Lember - 3.30.2-1 -- Update to 3.30.2 - -* Wed Sep 26 2018 Kalev Lember - 3.30.1-2 -- Add modular repos to official repos list - -* Tue Sep 25 2018 Kalev Lember - 3.30.1-1 -- Update to 3.30.1 - -* Thu Sep 06 2018 Kalev Lember - 3.30.0-1 -- Update to 3.30.0 - -* Tue Aug 28 2018 Richard Hughes - 3.29.92-1 -- Update to 3.29.92 - -* Fri Jul 13 2018 Fedora Release Engineering - 3.29.1-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild - -* Wed May 09 2018 Kalev Lember - 3.29.1-1 -- Update to 3.29.1 - -* Mon Apr 09 2018 Kalev Lember - 3.28.1-1 -- Update to 3.28.1 - -* Thu Mar 29 2018 Kalev Lember - 3.28.0-5 -- Fix empty OS Updates showing up -- Make rpm-ostree update triggering work - -* Thu Mar 15 2018 Kalev Lember - 3.28.0-4 -- Fix opening results from gnome-shell search provider - -* Wed Mar 14 2018 Kalev Lember - 3.28.0-3 -- Fix crash on initial run with no network (#1554986) - -* Tue Mar 13 2018 Kalev Lember - 3.28.0-2 -- Backport an upstream patch to fix shell extensions app ID - -* Mon Mar 12 2018 Kalev Lember - 3.28.0-1 -- Update to 3.28.0 - -* Sun Mar 11 2018 Kalev Lember - 3.27.92-3 -- Rebuilt for gspell 1.8 - -* Wed Mar 07 2018 Kalev Lember - 3.27.92-2 -- Move org.gnome.Software.Featured.xml from -editor to main package - -* Mon Mar 05 2018 Kalev Lember - 3.27.92-1 -- Update to 3.27.92 - -* Sun Mar 04 2018 Neal Gompa - 3.27.90-4 -- Drop obsolete snapd-login-service requirement for snap plugin subpackage - -* Mon Feb 19 2018 Adam Williamson - 3.27.90-3 -- Backport fix for RHBZ #1546893 from upstream git - -* Mon Feb 19 2018 Kalev Lember - 3.27.90-2 -- Re-enable rpm-ostree plugin - -* Thu Feb 15 2018 Kalev Lember - 3.27.90-1 -- Update to 3.27.90 -- Temporarily disable the rpm-ostree plugin - -* Tue Feb 13 2018 Björn Esser - 3.27.4-4 -- Rebuild against newer gnome-desktop3 package - -* Thu Feb 08 2018 Kalev Lember - 3.27.4-3 -- Add fedora-workstation-repositories to nonfree-sources schema defaults - -* Wed Feb 07 2018 Fedora Release Engineering - 3.27.4-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild - -* Mon Jan 08 2018 Kalev Lember - 3.27.4-1 -- Update to 3.27.4 -- Drop unused --without packagekit option - -* Fri Jan 05 2018 Igor Gnatenko - 3.27.3-2 -- Remove obsolete scriptlets - -* Sat Dec 16 2017 Kalev Lember - 3.27.3-1 -- Update to 3.27.3 - -* Mon Nov 13 2017 Kalev Lember - 3.27.2-1 -- Update to 3.27.2 - -* Thu Nov 09 2017 Kalev Lember - 3.26.2-1 -- Update to 3.26.2 -- Re-enable fwupd support - -* Tue Oct 31 2017 Kalev Lember - 3.26.1-5 -- Enable the rpm-ostree plugin - -* Wed Oct 25 2017 Kalev Lember - 3.26.1-4 -- Fix "too many results returned" error after distro upgrades (#1496489) - -* Tue Oct 10 2017 Kalev Lember - 3.26.1-3 -- Backport a flatpakref installation fix - -* Mon Oct 09 2017 Richard Hughes - 3.26.1-2 -- Disable fwupd support until we get a 3.27.1 tarball - -* Sun Oct 08 2017 Kalev Lember - 3.26.1-1 -- Update to 3.26.1 - -* Mon Sep 11 2017 Kalev Lember - 3.26.0-1 -- Update to 3.26.0 - -* Sun Aug 27 2017 Kalev Lember - 3.25.91-1 -- Update to 3.25.91 - -* Tue Aug 15 2017 Kalev Lember - 3.25.90-1 -- Update to 3.25.90 - -* Fri Aug 11 2017 Igor Gnatenko - 3.25.4-6 -- Rebuilt after RPM update (№ 3) - -* Thu Aug 10 2017 Igor Gnatenko - 3.25.4-5 -- Rebuilt for RPM soname bump - -* Thu Aug 10 2017 Igor Gnatenko - 3.25.4-4 -- Rebuilt for RPM soname bump - -* Wed Aug 02 2017 Fedora Release Engineering - 3.25.4-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild - -* Wed Jul 26 2017 Fedora Release Engineering - 3.25.4-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild - -* Fri Jul 21 2017 Kalev Lember - 3.25.4-1 -- Update to 3.25.4 - -* Tue Jul 18 2017 Kalev Lember - 3.25.3-6 -- Drop a meson workaround now that meson is fixed - -* Wed Jun 28 2017 Neal Gompa - 3.25.3-5 -- Actually properly enable snap subpackage after removing conditional - -* Wed Jun 28 2017 Neal Gompa - 3.25.3-4 -- Remove unnecessary arch-specific conditional for snap subpackage - -* Tue Jun 27 2017 Neal Gompa - 3.25.3-3 -- Ensure snap subpackage is installed if snapd is installed - -* Fri Jun 23 2017 Richard Hughes - 3.24.3-2 -- Enable the snap subpackage - -* Fri Jun 23 2017 Kalev Lember - 3.25.3-1 -- Update to 3.25.3 -- Switch to the meson build system -- Add an -editor subpackage with new banner editor - -* Mon May 15 2017 Richard Hughes - 3.24.3-1 -- Update to 3.23.3 -- Fix a common crash when installing flatpakrepo files -- Ensure we show the banner when upgrades are available - -* Tue May 09 2017 Kalev Lember - 3.24.2-1 -- Update to 3.24.2 - -* Tue Apr 25 2017 Adam Williamson - 3.24.1-2 -- Backport crasher fix from upstream (RHBZ #1444669 / BGO #781217) - -* Tue Apr 11 2017 Kalev Lember - 3.24.1-1 -- Update to 3.24.1 - -* Tue Mar 21 2017 Kalev Lember - 3.24.0-1 -- Update to 3.24.0 - -* Thu Mar 16 2017 Kalev Lember - 3.23.92-1 -- Update to 3.23.92 - -* Mon Feb 27 2017 Richard Hughes - 3.23.91-1 -- Update to 3.23.91 - -* Mon Feb 13 2017 Richard Hughes - 3.23.90-1 -- Update to 3.23.90 - -* Fri Feb 10 2017 Fedora Release Engineering - 3.23.3-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild - -* Thu Dec 15 2016 Richard Hughes - 3.23.3-1 -- Update to 3.23.3 - -* Wed Nov 23 2016 Kalev Lember - 3.23.2-1 -- Update to 3.23.2 - -* Tue Nov 08 2016 Kalev Lember - 3.22.2-1 -- Update to 3.22.2 - -* Wed Oct 12 2016 Kalev Lember - 3.22.1-1 -- Update to 3.22.1 - -* Mon Sep 19 2016 Kalev Lember - 3.22.0-1 -- Update to 3.22.0 - -* Wed Sep 14 2016 Kalev Lember - 3.21.92-1 -- Update to 3.21.92 -- Don't set group tags - -* Thu Sep 01 2016 Kalev Lember - 3.21.91-1 -- Update to 3.21.91 - -* Wed Aug 17 2016 Kalev Lember - 3.21.90-2 -- Rebuilt for fixed libappstream-glib headers - -* Wed Aug 17 2016 Kalev Lember - 3.21.90-1 -- Update to 3.21.90 -- Tighten -devel subpackage dependencies - -* Thu Jul 28 2016 Richard Hughes - 3.21.4-2 -- Allow building without PackageKit for the atomic workstation. - -* Mon Jul 18 2016 Richard Hughes - 3.21.4-1 -- Update to 3.21.4 - -* Thu May 26 2016 Kalev Lember - 3.21.2-2 -- Build with flatpak support - -* Mon May 23 2016 Richard Hughes - 3.21.2-1 -- Update to 3.21.2 - -* Tue May 10 2016 Kalev Lember - 3.21.1-2 -- Require PackageKit 1.1.1 for system upgrade support - -* Mon Apr 25 2016 Richard Hughes - 3.21.1-1 -- Update to 3.21.1 - -* Mon Apr 25 2016 Richard Hughes - 3.20.2-1 -- Update to 3.20.1 -- Allow popular and featured apps to match any plugin -- Do not make the ODRS plugin depend on xdg-app -- Fix many of the os-upgrade issues and implement the latest mockups -- Make all the plugins more threadsafe -- Return all update descriptions newer than the installed version -- Show some non-fatal error messages if installing fails -- Use a background PackageKit transaction when downloading upgrades - -* Wed Apr 13 2016 Kalev Lember - 3.20.1-1 -- Update to 3.20.1 - -* Fri Apr 01 2016 Richard Hughes - 3.20.1-2 -- Set the list of official sources -- Compile with xdg-app support - -* Tue Mar 22 2016 Kalev Lember - 3.20.0-1 -- Update to 3.20.0 - -* Mon Mar 14 2016 Richard Hughes - 3.19.92-1 -- Update to 3.19.92 - -* Thu Mar 03 2016 Kalev Lember - 3.19.91-2 -- Set minimum required json-glib version - -* Mon Feb 29 2016 Richard Hughes - 3.19.91-1 -- Update to 3.19.91 - -* Mon Feb 15 2016 Richard Hughes - 3.19.90-1 -- Update to 3.19.90 - -* Wed Feb 03 2016 Fedora Release Engineering - 3.19.4-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild - -* Fri Jan 15 2016 Richard Hughes - 3.19.4-1 -- Update to 3.19.4 - -* Thu Dec 03 2015 Kalev Lember - 3.18.3-2 -- Require librsvg2 for the gdk-pixbuf svg loader - -* Thu Nov 05 2015 Richard Hughes - 3.18.3-1 -- Update to 3.18.3 -- Use the correct user agent string when downloading firmware -- Fix a crash in the limba plugin -- Fix installing web applications - -* Mon Oct 26 2015 Kalev Lember - 3.18.2-2 -- Fix apps reappearing as installed a few seconds after removal (#1275163) - -* Thu Oct 15 2015 Kalev Lember - 3.18.2-1 -- Update to 3.18.2 - -* Tue Oct 13 2015 Kalev Lember - 3.18.1-1 -- Update to 3.18.1 - -* Wed Oct 07 2015 Kalev Lember - 3.18.0-2 -- Backport two crasher fixes from upstream - -* Mon Sep 21 2015 Kalev Lember - 3.18.0-1 -- Update to 3.18.0 - -* Tue Sep 15 2015 Kalev Lember - 3.17.92-2 -- Update dependency versions - -* Tue Sep 15 2015 Richard Hughes - 3.17.92-1 -- Update to 3.17.92 - -* Thu Sep 10 2015 Richard Hughes - 3.17.91-2 -- Fix firmware updates - -* Thu Sep 03 2015 Kalev Lember - 3.17.91-1 -- Update to 3.17.91 - -* Wed Aug 19 2015 Kalev Lember - 3.17.90-1 -- Update to 3.17.90 - -* Wed Aug 12 2015 Richard Hughes - 3.17.3-1 -- Update to 3.17.3 - -* Wed Jul 22 2015 David King - 3.17.2-3 -- Bump for new gnome-desktop3 - -* Wed Jun 17 2015 Fedora Release Engineering - 3.17.2-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild - -* Fri Jun 05 2015 Kalev Lember - 3.17.2-1 -- Update to 3.17.2 - -* Mon May 25 2015 Kalev Lember - 3.17.1-1 -- Update to 3.17.1 - -* Fri May 15 2015 Kalev Lember - 3.16.2-2 -- Fix a crash under Wayland (#1221968) - -* Mon May 11 2015 Kalev Lember - 3.16.2-1 -- Update to 3.16.2 - -* Tue Apr 14 2015 Kalev Lember - 3.16.1-1 -- Update to 3.16.1 - -* Mon Mar 23 2015 Kalev Lember - 3.16.0-1 -- Update to 3.16.0 - -* Mon Mar 16 2015 Kalev Lember - 3.15.92-1 -- Update to 3.15.92 -- Use license macro for the COPYING file -- Add a patch to adapt to gnome-terminal desktop file rename - -* Mon Mar 02 2015 Kalev Lember - 3.15.91-1 -- Update to 3.15.91 - -* Sat Feb 21 2015 Kalev Lember - 3.15.90-3 -- Export DisplayName property on the packagekit session service - -* Thu Feb 19 2015 Kalev Lember - 3.15.90-2 -- Backport a crash fix - -* Tue Feb 17 2015 Richard Hughes - 3.15.90-1 -- Update to 3.15.90 - -* Mon Jan 19 2015 Richard Hughes - 3.15.4-1 -- Update to 3.15.4 - -* Tue Nov 25 2014 Kalev Lember - 3.15.2-1 -- Update to 3.15.2 - -* Thu Nov 13 2014 Richard Hughes - 3.14.2-3 -- Fix non-Fedora build - -* Tue Nov 11 2014 Richard Hughes - 3.14.2-2 -- Backport a patch to fix compilation - -* Mon Nov 10 2014 Kalev Lember - 3.14.2-1 -- Update to 3.14.2 - -* Sat Nov 08 2014 Kalev Lember - 3.14.1-3 -- Update the list of system apps - -* Sat Nov 01 2014 David King - 3.14.1-2 -- Rebuild for new libappstream-glib (#1156494) - -* Mon Oct 13 2014 Kalev Lember - 3.14.1-1 -- Update to 3.14.1 - -* Thu Oct 09 2014 Kalev Lember - 3.14.0-2 -- Depend on gnome-menus for app folder directory entries - -* Mon Sep 22 2014 Kalev Lember - 3.14.0-1 -- Update to 3.14.0 - -* Wed Sep 17 2014 Kalev Lember - 3.13.92-2 -- Set minimum required dependency versions (#1136343) - -* Tue Sep 16 2014 Kalev Lember - 3.13.92-1 -- Update to 3.13.92 -- Replace gnome-system-log with gnome-logs in the system apps list - -* Tue Sep 02 2014 Kalev Lember - 3.13.91-1 -- Update to 3.13.91 - -* Tue Aug 19 2014 Richard Hughes - 3.13.90-1 -- Update to 3.13.90 - -* Sat Aug 16 2014 Fedora Release Engineering - 3.13.5-0.2.git5c89189 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild - -* Mon Aug 11 2014 Kalev Lember - 3.13.5-0.1.git5c89189 -- Update to 3.13.5 git snapshot -- Ship HighContrast icons - -* Sun Aug 03 2014 Kalev Lember - 3.13.4-2 -- Replace Epiphany with Firefox in the system apps list - -* Wed Jul 23 2014 Kalev Lember - 3.13.4-1 -- Update to 3.13.4 - -* Wed Jun 25 2014 Kalev Lember - 3.13.3-1 -- Update to 3.13.3 - -* Thu Jun 12 2014 Richard Hughes - 3.13.3-0.2.git7491627 -- Depend on the newly-created appstream-data package and stop shipping - the metadata here. - -* Sat Jun 07 2014 Kalev Lember - 3.13.3-0.1.git7491627 -- Update to 3.13.3 git snapshot - -* Wed May 28 2014 Richard Hughes - 3.13.2-2 -- Rebuild with new metadata. - -* Wed May 28 2014 Kalev Lember - 3.13.2-1 -- Update to 3.13.2 - -* Thu May 15 2014 Kalev Lember - 3.13.1-4 -- Depend on gsettings-desktop-schemas - -* Mon May 12 2014 Richard Hughes - 3.13.1-3 -- Update the metadata and use appstream-util to install the metadata. - -* Wed May 07 2014 Kalev Lember - 3.13.1-2 -- Drop gnome-icon-theme dependency - -* Mon Apr 28 2014 Richard Hughes - 3.13.1-1 -- Update to 3.13.1 - -* Fri Apr 11 2014 Kalev Lember - 3.12.1-2 -- Rebuild with new metadata. - -* Fri Apr 11 2014 Richard Hughes - 3.12.1-1 -- Update to 3.12.1 - -* Mon Mar 24 2014 Richard Hughes - 3.12.0-1 -- Update to 3.12.0 - -* Thu Mar 20 2014 Richard Hughes - 3.11.92-1 -- Update to 3.11.92 - -* Tue Mar 18 2014 Richard Hughes - 3.11.91-2 -- Rebuild with new metadata. - -* Sat Mar 08 2014 Richard Hughes - 3.11.91-1 -- Update to 3.11.91 - -* Tue Feb 18 2014 Richard Hughes - 3.11.90-1 -- Update to 3.11.90 - -* Mon Feb 03 2014 Richard Hughes - 3.11.5-2 -- Require epiphany-runtime rather than the full application - -* Mon Feb 03 2014 Richard Hughes - 3.11.5-1 -- Update to 3.11.5 - -* Thu Jan 30 2014 Richard Hughes - 3.11.4-3 -- Rebuild for libpackagekit-glib soname bump - -* Wed Jan 22 2014 Richard Hughes - 3.11.4-2 -- Rebuild with metadata that has the correct screenshot url. - -* Thu Jan 16 2014 Richard Hughes - 3.11.4-1 -- Update to 3.11.4 - -* Tue Dec 17 2013 Richard Hughes - 3.11.3-1 -- Update to 3.11.3 - -* Tue Nov 19 2013 Richard Hughes - 3.11.2-1 -- Update to 3.11.2 - -* Tue Oct 29 2013 Richard Hughes - 3.11.1-1 -- Update to 3.11.1 -- Add a gnome shell search provider -- Add a module to submit the user rating to the fedora-tagger web service -- Add support for 'missing' codecs that we know exist but we can't install -- Add support for epiphany web applications -- Handle offline installation sensibly -- Save the user rating if the user clicks the rating stars -- Show a modal error message if install or remove actions failed -- Show a star rating on the application details page -- Show font screenshots -- Show more detailed version numbers when required -- Show screenshots to each application - -* Wed Sep 25 2013 Richard Hughes 3.10.0-1 -- New upstream release. -- New metadata for fedora, updates and updates-testing -- Add a plugin to query the PackageKit prepared-update file directly -- Do not clear the offline-update trigger if rebooting succeeded -- Do not load incompatible projects when parsing AppStream data -- Lots of updated translations -- Show the window right away when starting - -* Fri Sep 13 2013 Richard Hughes 3.9.3-1 -- New upstream release. -- Lots of new and fixed UI and updated metadata for Fedora 20 - -* Tue Sep 03 2013 Richard Hughes 3.9.2-1 -- New upstream release. -- Allow stock items in the AppStream XML -- Extract the AppStream URL and description from the XML -- Only present the window when the overview is complete -- Return the subcategories sorted by name - -* Mon Sep 02 2013 Richard Hughes 3.9.1-1 -- New upstream release which is a technical preview for the alpha. - -* Sun Sep 01 2013 Richard Hughes 0.1-3 -- Use buildroot not RPM_BUILD_ROOT -- Own all gnome-software directories -- Drop gtk-update-icon-cache requires and the mime database functionality - -* Thu Aug 29 2013 Richard Hughes 0.1-2 -- Add call to desktop-file-validate and fix other review comments. - -* Wed Aug 28 2013 Richard Hughes 0.1-1 -- First release for Fedora package review - +%autochangelog diff --git a/sources b/sources index da03639..dc17852 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-48.2.tar.xz) = 3a03f475de56cb2ba534a0a7eaaaeb21677cf27a54db03bb84e3953dce796bfc437daf59b41155884260006c18562c50d7e01056aeb190a18449cbcbf2f5120c +SHA512 (gnome-software-49.alpha.tar.xz) = 7616e9f3969b73651c502874ab20f78390b7284ba289451d68847e9afe47f306ef514f46fd633bc99440d98154329d2872237ffb73ae47222ab1d6273ae78b3d From b4aa6186d3ffea75de915d48f8bb4627398f2cff Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 27 Jun 2025 09:50:36 +0200 Subject: [PATCH 22/44] Bump plugin API version --- gnome-software.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnome-software.spec b/gnome-software.spec index 5f6b255..664794e 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -21,7 +21,7 @@ %bcond dnf5 1 # this is not a library version -%define gs_plugin_version 22 +%define gs_plugin_version 23 %global tarball_version %%(echo %{version} | tr '~' '.') From 6f0144b990cf025954b7b741ae3a70beb1603f56 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 30 Jun 2025 14:48:06 +0200 Subject: [PATCH 23/44] dnf5: Install 'gnome-software-local-file-packagekit.desktop' also for dnf5 plugin --- 0001-dnf5-plugin.patch | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index b2e8117..3277b35 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,5 +1,5 @@ -at commit f3042a5f73fc6b04e26ceca5a1a469879af30fd3 -Date: Fri Jun 20 11:20:33 2025 +0200 +at commit 523ea7ff97d4092faad730d9ddd2311d8de0fa22 +Date: Mon Jun 30 14:44:52 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt index cd49cf529..27fa15e8b 100644 @@ -5595,3 +5595,16 @@ index 71aa486e2..d3e909dee 100644 plugins/eos-updater/gs-plugin-eos-updater.c plugins/epiphany/gs-plugin-epiphany.c plugins/epiphany/org.gnome.Software.Plugin.Epiphany.metainfo.xml.in +diff --git a/src/meson.build b/src/meson.build +index 99dfb409f..e91a73bac 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -235,7 +235,7 @@ if get_option('fwupd') + ) + endif + +-if get_option('packagekit') or get_option('rpm_ostree') ++if get_option('packagekit') or get_option('rpm_ostree') or get_option('dnf5') + i18n.merge_file( + input: 'gnome-software-local-file-packagekit.desktop.in', + output: 'gnome-software-local-file-packagekit.desktop', From c924750e8af509c8256cd8163350bf323c9cd545 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 30 Jun 2025 15:42:41 +0200 Subject: [PATCH 24/44] dnf5: Update the dnf5 plugin patch to match the latest upstream main branch --- 0001-dnf5-plugin.patch | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index 3277b35..70ad781 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,4 +1,4 @@ -at commit 523ea7ff97d4092faad730d9ddd2311d8de0fa22 +at commit 87d6d722ec47390682581162d5c14d9b26d4a65e Date: Mon Jun 30 14:44:52 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt @@ -5596,15 +5596,15 @@ index 71aa486e2..d3e909dee 100644 plugins/epiphany/gs-plugin-epiphany.c plugins/epiphany/org.gnome.Software.Plugin.Epiphany.metainfo.xml.in diff --git a/src/meson.build b/src/meson.build -index 99dfb409f..e91a73bac 100644 +index 43e1b58e9..e8af8b058 100644 --- a/src/meson.build +++ b/src/meson.build -@@ -235,7 +235,7 @@ if get_option('fwupd') +@@ -245,7 +245,7 @@ if get_option('fwupd') ) endif -if get_option('packagekit') or get_option('rpm_ostree') +if get_option('packagekit') or get_option('rpm_ostree') or get_option('dnf5') i18n.merge_file( - input: 'gnome-software-local-file-packagekit.desktop.in', - output: 'gnome-software-local-file-packagekit.desktop', + input: + configure_file( From 91ecbf1717f7f6e0b68096998b9a79718cae3451 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 30 Jun 2025 15:57:08 +0200 Subject: [PATCH 25/44] Add script to update the dnf5-plugin patch To not have it saved on a single machine, easy to recover and run by anyone. --- .gitignore | 1 + dnf5-plugin/.gitignore | 1 + dnf5-plugin/update-patch.sh | 24 ++++++++++++++++++++++++ gnome-software.spec | 2 ++ 4 files changed, 28 insertions(+) create mode 100644 dnf5-plugin/.gitignore create mode 100755 dnf5-plugin/update-patch.sh diff --git a/.gitignore b/.gitignore index b4345b1..ba3b2b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /gnome-software-*.tar.xz +/gnome-software-*-build diff --git a/dnf5-plugin/.gitignore b/dnf5-plugin/.gitignore new file mode 100644 index 0000000..b86f2cd --- /dev/null +++ b/dnf5-plugin/.gitignore @@ -0,0 +1 @@ +checkout-gs-dnf5 diff --git a/dnf5-plugin/update-patch.sh b/dnf5-plugin/update-patch.sh new file mode 100755 index 0000000..bf03fdd --- /dev/null +++ b/dnf5-plugin/update-patch.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ ! -d checkout-gs-dnf5 ]; then + git clone --branch wip/dnf5daemon https://gitlab.gnome.org/mcrha/gnome-software.git checkout-gs-dnf5 +fi + +PATCH_PATH=../0001-dnf5-plugin.patch +FIRST_COMMIT=f8b6ed17c592922c2e80667c5f122747f9c2d4ab + +cd checkout-gs-dnf5 && \ +echo "Updating gs-dnf5 git repository" && \ +git pull --rebase && \ +echo -n "at " >../${PATCH_PATH} && \ +git log HEAD | head -n 5 | grep -E "commit|Date" >>../${PATCH_PATH} && \ +echo "" >>../${PATCH_PATH} && \ +git diff ${FIRST_COMMIT}^ >>../${PATCH_PATH} && \ +cd - >/dev/null + +if [ "$?" = "0" ]; then + echo "Patch '${PATCH_PATH}' updated" +else + echo "Failed to update patch '${PATCH_PATH}'" + exit 1 +fi diff --git a/gnome-software.spec b/gnome-software.spec index 664794e..3894347 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -36,6 +36,8 @@ License: GPL-2.0-or-later URL: https://apps.gnome.org/Software Source0: https://download.gnome.org/sources/gnome-software/49/%{name}-%{tarball_version}.tar.xz +# to update the patch enter the ./dnf5-plugin/ directory and run from +# it the ./update-patch.sh script Patch: 0001-dnf5-plugin.patch # ostree and flatpak not on i686 for Fedora and RHEL 10 From cb80ea5cd1d87952fb27e0e3f7a4b69685ed8ae8 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 30 Jun 2025 17:15:57 +0200 Subject: [PATCH 26/44] dnf5-plugin: Update script to generate patch to not depend on exact commit It'll mean less changes, and less work, to keep it up to date with the repository changes. --- dnf5-plugin/update-patch.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/dnf5-plugin/update-patch.sh b/dnf5-plugin/update-patch.sh index bf03fdd..481ffcf 100755 --- a/dnf5-plugin/update-patch.sh +++ b/dnf5-plugin/update-patch.sh @@ -1,24 +1,35 @@ #!/bin/bash if [ ! -d checkout-gs-dnf5 ]; then - git clone --branch wip/dnf5daemon https://gitlab.gnome.org/mcrha/gnome-software.git checkout-gs-dnf5 + git clone --branch main https://gitlab.gnome.org/mcrha/gnome-software.git checkout-gs-dnf5 && \ + cd checkout-gs-dnf5 && \ + git checkout -b wip/dnf5daemon origin/wip/dnf5daemon && \ + cd - >/dev/null + + if [ "$?" != "0" ]; then + echo "Failed to clone dnf5-plugin repository" 1>&2 + exit 1; + fi fi PATCH_PATH=../0001-dnf5-plugin.patch -FIRST_COMMIT=f8b6ed17c592922c2e80667c5f122747f9c2d4ab cd checkout-gs-dnf5 && \ -echo "Updating gs-dnf5 git repository" && \ +echo "Updating gs-dnf5 git 'main' repository" && \ +git checkout main && \ +git pull --rebase && \ +echo "Updating gs-dnf5 git 'dnf5-pugin' repository" && \ +git checkout wip/dnf5daemon && \ git pull --rebase && \ echo -n "at " >../${PATCH_PATH} && \ git log HEAD | head -n 5 | grep -E "commit|Date" >>../${PATCH_PATH} && \ echo "" >>../${PATCH_PATH} && \ -git diff ${FIRST_COMMIT}^ >>../${PATCH_PATH} && \ +git diff main >>../${PATCH_PATH} && \ cd - >/dev/null if [ "$?" = "0" ]; then echo "Patch '${PATCH_PATH}' updated" else - echo "Failed to update patch '${PATCH_PATH}'" + echo "Failed to update patch '${PATCH_PATH}'" 1>&2 exit 1 fi From 9baa1e6cb69167623a41174901b16ab14ded738a Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Fri, 27 Jun 2025 12:56:22 -0400 Subject: [PATCH 27/44] Fix files list for RHEL builds gnome-software-local-file-packagekit.desktop is installed only when one or more of these options are enabled. --- gnome-software.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gnome-software.spec b/gnome-software.spec index 3894347..f18c59b 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -235,7 +235,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_bindir}/gnome-software %{_datadir}/applications/gnome-software-local-file-flatpak.desktop %{_datadir}/applications/gnome-software-local-file-fwupd.desktop +%if %{with packagekit} || %{with rpmostree} || %{with dnf5} %{_datadir}/applications/gnome-software-local-file-packagekit.desktop +%endif %{_datadir}/applications/org.gnome.Software.desktop %{_datadir}/bash-completion/completions/gnome-software %{_mandir}/man1/gnome-software.1* From 47f4fc83f39e1d952ac8912d7360c781da4e2ee2 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 23 Jul 2025 22:48:57 +0000 Subject: [PATCH 28/44] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild --- gnome-software.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gnome-software.spec b/gnome-software.spec index f18c59b..32061b0 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,7 +29,7 @@ Name: gnome-software Version: 49~alpha -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -324,4 +324,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog +* Wed Jul 23 2025 Fedora Release Engineering - 49~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + %autochangelog From 3a3519584163c2c69ead75a0f10e00112318d82e Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 25 Jul 2025 11:34:05 +0200 Subject: [PATCH 29/44] Resolves: #2377094 (Update dnf5 plugin with fixes for rhbz#2377094) --- 0001-dnf5-plugin.patch | 164 ++++++++++++++++++++++++++++++----------- gnome-software.spec | 5 +- 2 files changed, 120 insertions(+), 49 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index 70ad781..c6cf3dc 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,5 +1,5 @@ -at commit 87d6d722ec47390682581162d5c14d9b26d4a65e -Date: Mon Jun 30 14:44:52 2025 +0200 +at commit f78a6ba2af27d06c77a052e7499422da22c2cd06 +Date: Fri Jul 25 11:23:46 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt index cd49cf529..27fa15e8b 100644 @@ -15,10 +15,10 @@ index cd49cf529..27fa15e8b 100644 option('fwupd', type : 'boolean', value : true, description : 'enable fwupd support') diff --git a/plugins/dnf5/gs-dnf5-progress-helper.c b/plugins/dnf5/gs-dnf5-progress-helper.c new file mode 100644 -index 000000000..d720adc87 +index 000000000..cd8b861df --- /dev/null +++ b/plugins/dnf5/gs-dnf5-progress-helper.c -@@ -0,0 +1,532 @@ +@@ -0,0 +1,552 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -41,7 +41,8 @@ index 000000000..d720adc87 + + GsDnf5RpmRpm *rpm_proxy; /* (owned) */ + GsDnf5Base *base_proxy; /* (owned) */ -+ GsApp *app; /* (owned) */ ++ GsApp *app; /* (owned) (nullable) */ ++ GsAppList *list; /* (owned) (nullable) */ + gchar *session_object_path; /* (owned) */ + GHashTable *ongoing_downloads; /* (owned); gchar * (download_id) ~> DownloadInfo * */ + GsAppState recover_download_state; @@ -73,10 +74,20 @@ index 000000000..d720adc87 +G_DEFINE_TYPE (GsDnf5ProgressHelper, gs_dnf5_progress_helper, G_TYPE_OBJECT) + +static void ++gs_dnf5_helper_set_progress (GsDnf5ProgressHelper *self, ++ guint progress) ++{ ++ if (self->app != NULL) ++ gs_app_set_progress (self->app, progress); ++ if (self->list != NULL) ++ gs_app_list_override_progress (self->list, progress); ++} ++ ++static void +gs_dnf5_update_download_progress (GsDnf5ProgressHelper *self) +{ + if (self->to_download_total > 0) -+ gs_app_set_progress (self->app, 100 * self->downloaded_total / self->to_download_total); ++ gs_dnf5_helper_set_progress (self, 100 * self->downloaded_total / self->to_download_total); +} + +typedef struct { @@ -104,10 +115,12 @@ index 000000000..d720adc87 + return; + + if (g_hash_table_size (self->ongoing_downloads) == 0) { -+ self->recover_download_state = gs_app_get_state (self->app); ++ if (self->app != NULL) { ++ self->recover_download_state = gs_app_get_state (self->app); ++ gs_app_set_state (self->app, GS_APP_STATE_DOWNLOADING); ++ } + self->to_download_total = 0; + self->downloaded_total = 0; -+ gs_app_set_state (self->app, GS_APP_STATE_DOWNLOADING); + } + + download_info = g_new0 (DownloadInfo, 1); @@ -116,7 +129,8 @@ index 000000000..d720adc87 + g_hash_table_insert (self->ongoing_downloads, g_strdup (arg_download_id), download_info); + self->to_download_total += arg_total_to_download; + -+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); ++ if (self->app != NULL) ++ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); + gs_dnf5_update_download_progress (self); +} + @@ -147,9 +161,11 @@ index 000000000..d720adc87 + + /* it's the last package to be downloaded */ + if (g_hash_table_size (self->ongoing_downloads) == 0) { -+ gs_app_set_state (self->app, self->recover_download_state); -+ gs_app_set_progress (self->app, GS_APP_PROGRESS_UNKNOWN); -+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_UNKNOWN, 0); ++ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN); ++ if (self->app != NULL) { ++ gs_app_set_state (self->app, self->recover_download_state); ++ gs_app_set_size_download (self->app, GS_SIZE_TYPE_UNKNOWN, 0); ++ } + } else { + gs_dnf5_update_download_progress (self); + } @@ -194,7 +210,8 @@ index 000000000..d720adc87 + self->to_download_total -= download_info->total; + download_info->total = arg_total_to_download; + self->to_download_total += download_info->total; -+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); ++ if (self->app != NULL) ++ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); + } + gs_dnf5_update_download_progress (self); + } @@ -208,7 +225,7 @@ index 000000000..d720adc87 +{ + if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) + return; -+ gs_app_set_progress (self->app, 0); ++ gs_dnf5_helper_set_progress (self, 0); + g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); +} + @@ -221,7 +238,7 @@ index 000000000..d720adc87 + if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) + return; + g_debug ("%s: success:%d", G_STRFUNC, arg_success); -+ gs_app_set_progress (self->app, 100); ++ gs_dnf5_helper_set_progress (self, 100); +} + +static void @@ -236,7 +253,7 @@ index 000000000..d720adc87 + return; + g_debug ("%s: nevra:'%s' progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_processed, arg_total); + if (arg_total != 0) -+ gs_app_set_progress (self->app, arg_processed * 100 / arg_total); ++ gs_dnf5_helper_set_progress (self, arg_processed * 100 / arg_total); +} + +static void @@ -432,7 +449,7 @@ index 000000000..d720adc87 + + #undef clear_signal_handler + -+ gs_app_set_progress (self->app, GS_APP_PROGRESS_UNKNOWN); ++ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN); + + G_OBJECT_CLASS (gs_dnf5_progress_helper_parent_class)->dispose (object); +} @@ -445,6 +462,7 @@ index 000000000..d720adc87 + g_clear_object (&self->base_proxy); + g_clear_object (&self->rpm_proxy); + g_clear_object (&self->app); ++ g_clear_object (&self->list); + g_clear_pointer (&self->session_object_path, g_free); + g_clear_pointer (&self->ongoing_downloads, g_hash_table_destroy); + @@ -470,22 +488,24 @@ index 000000000..d720adc87 +gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy, + GsDnf5RpmRpm *rpm_proxy, + GsApp *progress_app, ++ GsAppList *progress_list, + const gchar *session_object_path) +{ + GsDnf5ProgressHelper *self; + + g_return_val_if_fail (GS_DNF5_IS_BASE (base_proxy), NULL); + g_return_val_if_fail (GS_DNF5_IS_RPM_RPM (rpm_proxy), NULL); -+ g_return_val_if_fail (GS_IS_APP (progress_app), NULL); ++ g_return_val_if_fail (GS_IS_APP (progress_app) || GS_IS_APP_LIST (progress_list), NULL); + g_return_val_if_fail (session_object_path != NULL, NULL); + + self = g_object_new (GS_TYPE_DNF5_PROGRESS_HELPER, NULL); + self->base_proxy = g_object_ref (base_proxy); + self->rpm_proxy = g_object_ref (rpm_proxy); -+ self->app = g_object_ref (progress_app); ++ self->app = progress_app ? g_object_ref (progress_app) : NULL; ++ self->list = progress_list ? g_object_ref (progress_list) : NULL; + self->session_object_path = g_strdup (session_object_path); + -+ gs_app_set_progress (self->app, GS_APP_PROGRESS_UNKNOWN); ++ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN); + + self->download_add_new_id = + g_signal_connect_object (base_proxy, "download_add_new", @@ -553,10 +573,10 @@ index 000000000..d720adc87 +} diff --git a/plugins/dnf5/gs-dnf5-progress-helper.h b/plugins/dnf5/gs-dnf5-progress-helper.h new file mode 100644 -index 000000000..73d528d54 +index 000000000..5d522468c --- /dev/null +++ b/plugins/dnf5/gs-dnf5-progress-helper.h -@@ -0,0 +1,30 @@ +@@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -584,15 +604,16 @@ index 000000000..73d528d54 + gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy, + GsDnf5RpmRpm *rpm_proxy, + GsApp *progress_app, ++ GsAppList *progress_list, + const gchar *session_object_path); + +G_END_DECLS diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c new file mode 100644 -index 000000000..bae3bde28 +index 000000000..fefad6961 --- /dev/null +++ b/plugins/dnf5/gs-plugin-dnf5.c -@@ -0,0 +1,3770 @@ +@@ -0,0 +1,3823 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -693,6 +714,7 @@ index 000000000..bae3bde28 +gs_dnf5_report_error (GsPluginDnf5 *self, + GsPluginEventCallback event_callback, + void *event_user_data, ++ GsApp *app, + const GError *error, + gboolean interactive) +{ @@ -702,6 +724,7 @@ index 000000000..bae3bde28 + return; + + event = gs_plugin_event_new ("error", error, ++ app ? "app" : NULL, app, + NULL); + if (interactive) + gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INTERACTIVE); @@ -1337,6 +1360,8 @@ index 000000000..bae3bde28 + GsAppState set_state; + GHashTable *nevra_to_app; /* (nullable): gchar *nevra ~> GsApp * */ + GHashTable *replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> gchar *old_version */ ++ GHashTable *used_replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> NULL */ ++ gboolean cover_unused_replaces; +} ReadPackageData; + +static gboolean @@ -1803,7 +1828,8 @@ index 000000000..bae3bde28 +typedef enum { + GS_DNF5_TRANSACTION_FLAG_NONE = 0, + GS_DNF5_TRANSACTION_FLAG_OFFLINE = 1 << 0, -+ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY = 1 << 1 ++ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY = 1 << 1, ++ GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING = 1 << 2 /* negative flag, to not have it everywhere */ +} GsDnf5TransactionFlags; + +static gboolean @@ -1811,6 +1837,7 @@ index 000000000..bae3bde28 + const gchar *session_path, + GsDnf5RpmRpm *rpm_proxy, + GsApp *progress_app, ++ GsAppList *progress_list, + GsDnf5TransactionFlags flags, + GVariant **out_transaction, + GCancellable *cancellable, @@ -1870,8 +1897,11 @@ index 000000000..bae3bde28 + + options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + -+ if (progress_app != NULL) -+ helper = gs_dnf5_progress_helper_new (base_proxy, rpm_proxy, progress_app, session_path); ++ g_variant_builder_add (options_builder, "{sv}", "allow_erasing", ++ g_variant_new_boolean ((flags & GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING) == 0)); ++ ++ if (progress_app != NULL || progress_list != NULL) ++ helper = gs_dnf5_progress_helper_new (base_proxy, rpm_proxy, progress_app, progress_list, session_path); + + repo_key_import_data.self = self; + repo_key_import_data.session_path = session_path; @@ -2039,6 +2069,7 @@ index 000000000..bae3bde28 +typedef struct _UpdateReplacesData { + GsApp *app; + GHashTable *replaces; ++ GHashTable *used_replaces; +} UpdateReplacesData; + +static gboolean @@ -2051,8 +2082,10 @@ index 000000000..bae3bde28 + UpdateReplacesData *upd = user_data; + gint32 replaces_id = g_variant_get_int32 (item_array); + const gchar *old_version = g_hash_table_lookup (upd->replaces, GINT_TO_POINTER (replaces_id)); -+ if (old_version != NULL) ++ if (old_version != NULL) { + gs_app_set_version (upd->app, old_version); ++ g_hash_table_add (upd->used_replaces, GINT_TO_POINTER (replaces_id)); ++ } + + return TRUE; +} @@ -2073,13 +2106,27 @@ index 000000000..bae3bde28 + g_autoptr(GString) nevra = NULL; + GsApp *known_app; + -+ if (op_kind == OP_KIND_UNKNOWN || op_kind == OP_KIND_REPLACED) ++ if (op_kind == OP_KIND_UNKNOWN || (!rpd->cover_unused_replaces && op_kind == OP_KIND_REPLACED) || ++ (rpd->cover_unused_replaces && op_kind != OP_KIND_REPLACED)) + return TRUE; + + value = g_variant_get_child_value (item_array, 4); + dict = g_variant_dict_new (value); + g_clear_pointer (&value, g_variant_unref); + ++ if (rpd->cover_unused_replaces) { ++ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_INT32); ++ if (value != NULL) { ++ gint32 id = g_variant_get_int32 (value); ++ if (g_hash_table_contains (rpd->used_replaces, GINT_TO_POINTER (id))) ++ return TRUE; ++ } else { ++ return TRUE; ++ } ++ ++ g_clear_pointer (&value, g_variant_unref); ++ } ++ + value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); + if (value == NULL) + return TRUE; @@ -2132,6 +2179,7 @@ index 000000000..bae3bde28 + + upd.app = upd_app; + upd.replaces = rpd->replaces; ++ upd.used_replaces = rpd->used_replaces; + + if (!gs_dnf5_foreach_item (self, value, gs_dnf5_gather_update_replaced_pkgs_cb, &upd, cancellable, error)) + return FALSE; @@ -2170,9 +2218,10 @@ index 000000000..bae3bde28 + gs_app_set_state (upd_app, GS_APP_STATE_UPDATABLE); + break; + case OP_KIND_REMOVE: -+ gs_app_set_state (upd_app, GS_APP_STATE_UNAVAILABLE); -+ break; + case OP_KIND_REPLACED: ++ gs_app_set_state (upd_app, GS_APP_STATE_UNAVAILABLE); ++ gs_app_set_size_download (upd_app, GS_SIZE_TYPE_VALID, 0); ++ break; + case OP_KIND_UNKNOWN: + default: + g_warn_if_reached (); @@ -2274,15 +2323,17 @@ index 000000000..bae3bde28 + if (success) { + g_autoptr(GHashTable) nevra_to_app = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + g_autoptr(GHashTable) replaces = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); ++ g_autoptr(GHashTable) used_replaces = g_hash_table_new (g_direct_hash, g_direct_equal); + ReadPackageData rpd = { 0, }; + rpd.list = list; + rpd.set_state = GS_APP_STATE_UPDATABLE; + rpd.nevra_to_app = nevra_to_app; + rpd.replaces = replaces; ++ rpd.used_replaces = used_replaces; + + success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error); + -+ if (success && g_hash_table_size (nevra_to_app) > 0) { ++ if (success) { + g_autoptr(GVariantBuilder) options_builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_autoptr(GVariant) transaction = NULL; + const gchar * const packages[2] = { NULL, NULL }; @@ -2296,13 +2347,19 @@ index 000000000..bae3bde28 + cancellable, + &local_error); + success = success && -+ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, ++ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, NULL, + GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY, + &transaction, cancellable, &local_error); + if (success && transaction != NULL) { + success = gs_dnf5_foreach_item (self, transaction, + gs_dnf5_gather_update_replaces_cb, &rpd, + cancellable, &local_error); ++ rpd.cover_unused_replaces = FALSE; ++ success = success && ++ gs_dnf5_foreach_item (self, transaction, ++ gs_dnf5_gather_updates_from_transaction_cb, &rpd, ++ cancellable, &local_error); ++ rpd.cover_unused_replaces = TRUE; + success = success && + gs_dnf5_foreach_item (self, transaction, + gs_dnf5_gather_updates_from_transaction_cb, &rpd, @@ -2697,7 +2754,7 @@ index 000000000..bae3bde28 + cancellable, + &local_error); + success = success && -+ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, ++ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, NULL, + GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY, + &transaction, cancellable, &local_error); + if (success && transaction != NULL) @@ -3120,6 +3177,7 @@ index 000000000..bae3bde28 + g_autoptr(GsDnf5SessionManager) session_manager = NULL; + g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; + g_autoptr(GsApp) progress_app = NULL; ++ GsAppList *progress_list = is_install ? NULL : list; + gboolean success; + + /* queue for install if installation needs the network */ @@ -3154,14 +3212,18 @@ index 000000000..bae3bde28 + else + g_ptr_array_add (packages, g_strdup (source)); + gs_app_list_add (used_apps, app); -+ if (progress_app == NULL) ++ if (progress_list == NULL && progress_app == NULL) + progress_app = g_object_ref (app); + } + } else if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_PROXY)) { + GsAppList *related = gs_app_get_related (app); ++ gboolean is_os_update = gs_app_get_special_kind (app) == GS_APP_SPECIAL_KIND_OS_UPDATE; + for (guint j = 0; j < gs_app_list_length (related); j++) { + GsApp *rel_app = gs_app_list_index (related, j); -+ if (gs_app_has_management_plugin (rel_app, plugin)) { ++ /* add only updatable packages into the list when it's an OS Update, otherwise ++ dnf5 claims "Packages for argument '$pkgname' available, but not installed." */ ++ if (gs_app_has_management_plugin (rel_app, plugin) && (!is_install || !is_os_update || ++ gs_app_get_state (rel_app) == GS_APP_STATE_UPDATABLE)) { + const gchar *source = gs_app_get_default_source (rel_app); + if (source != NULL) { + GFile *local_file = gs_app_get_local_file (rel_app); @@ -3173,7 +3235,7 @@ index 000000000..bae3bde28 + } + } + } -+ if (progress_app == NULL) ++ if (progress_list == NULL && progress_app == NULL) + progress_app = g_object_ref (app); + } + } @@ -3244,13 +3306,16 @@ index 000000000..bae3bde28 + cancellable, + error); + } else if (success) { ++ /* ignore package list for update, it's an offline action, which should update ++ everything possible, not only a specific package */ ++ const gchar * const all_packages[2] = { NULL, NULL }; + success = gs_dnf5_rpm_rpm_call_upgrade_sync (rpm_proxy, -+ (const gchar * const *) packages->pdata, ++ all_packages, + g_variant_builder_end (options_builder), + cancellable, + error); + } -+ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, ++ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, progress_list, + is_install ? GS_DNF5_TRANSACTION_FLAG_NONE : GS_DNF5_TRANSACTION_FLAG_OFFLINE, + NULL, cancellable, error); + if (success && !is_install) { @@ -3296,8 +3361,13 @@ index 000000000..bae3bde28 + } else { + gs_dnf5_convert_error (error); + -+ if (error != NULL && *error != NULL) -+ gs_dnf5_report_error (self, event_callback, event_user_data, *error, interactive); ++ if (error != NULL && *error != NULL) { ++ GsApp *app = NULL; ++ if (gs_app_list_length (list) == 1) ++ app = gs_app_list_index (list, 0); ++ ++ gs_dnf5_report_error (self, event_callback, event_user_data, app, *error, interactive); ++ } + } + + for (guint i = 0; i < gs_app_list_length (used_apps); i++) { @@ -3505,7 +3575,7 @@ index 000000000..bae3bde28 + g_variant_builder_end (options_builder), + cancellable, + &local_error); -+ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, GS_DNF5_TRANSACTION_FLAG_NONE, NULL, cancellable, &local_error); ++ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, GS_DNF5_TRANSACTION_FLAG_NONE, NULL, cancellable, &local_error); + } else { + gs_dnf5_convert_error (&local_error); + g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); @@ -3517,8 +3587,12 @@ index 000000000..bae3bde28 + if (success) { + gs_dnf5_mark_session_needs_reset (self); + } else { ++ GsApp *app = NULL; ++ if (gs_app_list_length (data->apps) == 1) ++ app = gs_app_list_index (data->apps, 0); ++ + gs_dnf5_convert_error (&local_error); -+ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, ++ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, app, + local_error, (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0); + } + @@ -4088,7 +4162,7 @@ index 000000000..bae3bde28 + error); + + if (success) { -+ success = gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, GS_DNF5_TRANSACTION_FLAG_OFFLINE, NULL, cancellable, error); ++ success = gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, GS_DNF5_TRANSACTION_FLAG_OFFLINE, NULL, cancellable, error); + + if (success && !download_only) { + g_autofree gchar *op_error_msg = NULL; @@ -4139,7 +4213,7 @@ index 000000000..bae3bde28 + } else { + gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE); + gs_dnf5_convert_error (&local_error); -+ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, ++ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, data->app, + local_error, (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0); + g_task_return_error (task, g_steal_pointer (&local_error)); + } diff --git a/gnome-software.spec b/gnome-software.spec index 32061b0..abe7752 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,7 +29,7 @@ Name: gnome-software Version: 49~alpha -Release: 2%{?dist} +Release: 3%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -324,7 +324,4 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog -* Wed Jul 23 2025 Fedora Release Engineering - 49~alpha-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild - %autochangelog From 06b236a7381b97d04c1cb6fa5e829582bbb24dd2 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 1 Aug 2025 09:54:42 +0200 Subject: [PATCH 30/44] Update to 49.beta --- 0001-dnf5-plugin.patch | 4 ++-- gnome-software.spec | 9 ++++++--- sources | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index c6cf3dc..c2353ab 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,4 +1,4 @@ -at commit f78a6ba2af27d06c77a052e7499422da22c2cd06 +at commit 238692b2cfb9cf3db85e1a0b2df369da97d7c639 Date: Fri Jul 25 11:23:46 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt @@ -5670,7 +5670,7 @@ index 71aa486e2..d3e909dee 100644 plugins/epiphany/gs-plugin-epiphany.c plugins/epiphany/org.gnome.Software.Plugin.Epiphany.metainfo.xml.in diff --git a/src/meson.build b/src/meson.build -index 43e1b58e9..e8af8b058 100644 +index f8b918539..7df81d130 100644 --- a/src/meson.build +++ b/src/meson.build @@ -245,7 +245,7 @@ if get_option('fwupd') diff --git a/gnome-software.spec b/gnome-software.spec index abe7752..46397ad 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -28,8 +28,8 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 49~alpha -Release: 3%{?dist} +Version: 49~beta +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -83,6 +83,7 @@ BuildRequires: pkgconfig(rpm-ostree-1) %endif BuildRequires: pkgconfig(sysprof-capture-4) BuildRequires: pkgconfig(xmlb) >= %{libxmlb_version} +BuildRequires: systemd Requires: appstream-data Requires: appstream%{?_isa} >= %{appstream_version} @@ -284,7 +285,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_provenance-license.so %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_provenance.so %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_repos.so -%{_sysconfdir}/xdg/autostart/org.gnome.Software.desktop %if %{with webapps} %dir %{_datadir}/swcatalog %dir %{_datadir}/swcatalog/xml @@ -314,6 +314,9 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_rpm-ostree.so %endif +%{_datadir}/dbus-1/services/org.gnome.Software.service +%{_userunitdir}/gnome-software.service + %files devel %{_libdir}/pkgconfig/gnome-software.pc %dir %{_includedir}/gnome-software diff --git a/sources b/sources index dc17852..f212fbd 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-49.alpha.tar.xz) = 7616e9f3969b73651c502874ab20f78390b7284ba289451d68847e9afe47f306ef514f46fd633bc99440d98154329d2872237ffb73ae47222ab1d6273ae78b3d +SHA512 (gnome-software-49.beta.tar.xz) = 9cb6d67216193840b2661871ef43da5e726a284d1164781f13423ee8dd86359334f199efc29f4b3a0b3eddfa8a3811d7dd38cd412fa53aaebb2e1c35ddc1f9df From 7fb63f649d3ade06860bf6fce2e8a0537b482a73 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Fri, 1 Aug 2025 12:08:46 -0700 Subject: [PATCH 31/44] Move the user service file to the main package In the 49-beta bump, this new file was inadvertently put into the rpm-ostree subpackage, it doesn't make any sense for it to be there. --- gnome-software.spec | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 46397ad..198e7c9 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,7 +29,7 @@ Name: gnome-software Version: 49~beta -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -299,6 +299,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/glib-2.0/schemas/org.gnome.software-fedora.gschema.override %{_libexecdir}/gnome-software-cmd %{_libexecdir}/gnome-software-restarter +%{_userunitdir}/gnome-software.service %if %{with dkms} %{_datadir}/polkit-1/actions/org.gnome.software.dkms-helper.policy @@ -314,9 +315,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_rpm-ostree.so %endif -%{_datadir}/dbus-1/services/org.gnome.Software.service -%{_userunitdir}/gnome-software.service - %files devel %{_libdir}/pkgconfig/gnome-software.pc %dir %{_includedir}/gnome-software From e4f244856b78751a56f757d91d247602862429d0 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 11 Aug 2025 17:10:38 +0200 Subject: [PATCH 32/44] dnf5-pugin: Use 'interactive' option, where supported - dnf5-plugin: Add support to provide update history --- 0001-dnf5-plugin.patch | 940 +++++++++++++++++++++++++++++++++++++---- gnome-software.spec | 5 +- 2 files changed, 859 insertions(+), 86 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index c2353ab..3e9a13f 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,5 +1,5 @@ -at commit 238692b2cfb9cf3db85e1a0b2df369da97d7c639 -Date: Fri Jul 25 11:23:46 2025 +0200 +at commit 8a1f0ca6564157dd688c84ffd25af9747c1e038e +Date: Mon Aug 11 17:07:06 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt index cd49cf529..27fa15e8b 100644 @@ -610,10 +610,10 @@ index 000000000..5d522468c +G_END_DECLS diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c new file mode 100644 -index 000000000..fefad6961 +index 000000000..88d7e25d8 --- /dev/null +++ b/plugins/dnf5/gs-plugin-dnf5.c -@@ -0,0 +1,3823 @@ +@@ -0,0 +1,4275 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -642,10 +642,15 @@ index 000000000..fefad6961 + +#include "gs-plugin-dnf5.h" + ++#include "../packagekit/gs-markdown.h" ++ +#define GS_DNF5_RELEASEVER_DEFAULT NULL +#define GS_DNF5_INTERFACE_RPM_DNF "org.rpm.dnf.v0" +#define GS_DNF5_OBJECT_PATH_RPM_DNF "/org/rpm/dnf/v0" + ++#define GS_DNF5_ADVISORIES_KEY "gs-dnf5::advisories" ++#define GS_DNF5_CHANGELOGS_KEY "gs-dnf5::changelogs" ++ +/* for how long a session can be left opened until it's auto-closed */ +#define GS_SESSION_LIFETIME_SECS (5 * 60) + @@ -1302,8 +1307,15 @@ index 000000000..fefad6961 + gs_app_set_state (app, state); +} + ++typedef enum { ++ GS_DNF5_READ_PACKAGE_FLAG_NONE = 0, ++ GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED = 1 << 0, ++ GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS = 1 << 1 ++} GsDnf5ReadPackageFlags; ++ +static void +gs_dnf5_update_app_changelogs (GsApp *app, ++ GsDnf5ReadPackageFlags flags, + GVariantDict *dict) +{ + GVariantIter iter; @@ -1351,43 +1363,33 @@ index 000000000..fefad6961 + g_variant_unref (child); + } + -+ if (changes != NULL) -+ gs_app_set_update_details_text (app, changes->str); ++ if (changes != NULL) { ++ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS) != 0) ++ gs_app_set_update_details_text (app, changes->str); ++ else ++ gs_app_set_metadata (app, GS_DNF5_CHANGELOGS_KEY, changes->str); ++ } +} + -+typedef struct { -+ GsAppList *list; -+ GsAppState set_state; -+ GHashTable *nevra_to_app; /* (nullable): gchar *nevra ~> GsApp * */ -+ GHashTable *replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> gchar *old_version */ -+ GHashTable *used_replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> NULL */ -+ gboolean cover_unused_replaces; -+} ReadPackageData; -+ -+static gboolean -+gs_dnf5_read_package_cb (GsPluginDnf5 *self, -+ GVariant *package_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) ++static GsApp * /* (transfer full) */ ++gs_dnf5_read_package_from_dict (GsPluginDnf5 *self, ++ GVariantDict *dict, ++ GsDnf5ReadPackageFlags flags, ++ GsAppState set_state) +{ -+ ReadPackageData *rpd = user_data; -+ GsAppList *list = rpd->list; -+ GHashTable *nevra_to_app = rpd->nevra_to_app; -+ g_autofree gchar *name = NULL; + g_autoptr(GsApp) app = NULL; -+ g_autoptr(GVariantDict) dict = NULL; + g_autoptr(GVariant) value = NULL; ++ g_autofree gchar *name = NULL; + -+ dict = g_variant_dict_new (package_array); -+ -+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); -+ if (value != NULL) { -+ const gchar *name_const = g_variant_get_string (value, NULL); -+ app = gs_plugin_cache_lookup (GS_PLUGIN (self), name_const); -+ if (app == NULL) -+ name = g_strdup (name_const); -+ g_clear_pointer (&value, g_variant_unref); ++ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED) != 0) { ++ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); ++ if (value != NULL) { ++ const gchar *name_const = g_variant_get_string (value, NULL); ++ app = gs_plugin_cache_lookup (GS_PLUGIN (self), name_const); ++ if (app == NULL) ++ name = g_strdup (name_const); ++ g_clear_pointer (&value, g_variant_unref); ++ } + } + + if (app == NULL) { @@ -1414,21 +1416,58 @@ index 000000000..fefad6961 + g_clear_pointer (&value, g_variant_unref); + } + -+ gs_plugin_cache_add (GS_PLUGIN (self), name, app); ++ value = g_variant_dict_lookup_value (dict, "buildtime", G_VARIANT_TYPE_UINT64); ++ if (value != NULL) { ++ gs_app_set_install_date (app, (gint64) g_variant_get_uint64 (value)); ++ g_clear_pointer (&value, g_variant_unref); ++ } ++ ++ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED) != 0) ++ gs_plugin_cache_add (GS_PLUGIN (self), name, app); + } + -+ gs_dnf5_update_app_changelogs (app, dict); -+ gs_dnf5_update_app_state (app, rpd->set_state, dict); ++ gs_dnf5_update_app_changelogs (app, flags, dict); ++ gs_dnf5_update_app_state (app, set_state, dict); + -+ if (rpd->set_state == GS_APP_STATE_UPDATABLE) { ++ if (set_state == GS_APP_STATE_UPDATABLE) { + gs_dnf5_app_set_version (dict, app, gs_app_set_update_version); + gs_app_add_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT); + } else { + gs_dnf5_app_set_version (dict, app, gs_app_set_version); + } + ++ return g_steal_pointer (&app); ++} ++ ++typedef struct { ++ GsAppList *list; ++ GsAppState set_state; ++ GHashTable *nevra_to_app; /* (nullable): gchar *nevra ~> GsApp * */ ++ GHashTable *replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> gchar *old_version */ ++ GHashTable *used_replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> NULL */ ++ gboolean cover_unused_replaces; ++ gint64 timestamp; ++} ReadPackageData; ++ ++static gboolean ++gs_dnf5_read_package_cb (GsPluginDnf5 *self, ++ GVariant *package_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ ReadPackageData *rpd = user_data; ++ GsAppList *list = rpd->list; ++ GHashTable *nevra_to_app = rpd->nevra_to_app; ++ g_autoptr(GsApp) app = NULL; ++ g_autoptr(GVariantDict) dict = NULL; ++ ++ dict = g_variant_dict_new (package_array); ++ ++ app = gs_dnf5_read_package_from_dict (self, dict, GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED | GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS, rpd->set_state); ++ + if (nevra_to_app != NULL) { -+ g_clear_pointer (&value, g_variant_unref); ++ g_autoptr(GVariant) value = NULL; + value = g_variant_dict_lookup_value (dict, "nevra", G_VARIANT_TYPE_STRING); + if (value != NULL) { + const gchar *nevra_const = g_variant_get_string (value, NULL); @@ -1600,6 +1639,7 @@ index 000000000..fefad6961 + g_variant_builder_add (attrs_builder, "s", "url"); + g_variant_builder_add (attrs_builder, "s", "license"); + g_variant_builder_add (attrs_builder, "s", "description"); ++ g_variant_builder_add (attrs_builder, "s", "buildtime"); + g_variant_builder_add (attrs_builder, "s", "changelogs"); + + return g_variant_builder_end (attrs_builder); @@ -1633,6 +1673,7 @@ index 000000000..fefad6961 + gchar *key_id; + GsPluginDnf5 *self; /* (owned) */ + GWeakRef repo_proxy_weakref; /* GsDnf5RpmRepo */ ++ gboolean interactive; +} QuestionData; + +static void @@ -1658,11 +1699,22 @@ index 000000000..fefad6961 + + repo_proxy = g_weak_ref_get (&data->repo_proxy_weakref); + if (repo_proxy != NULL) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; + gboolean confirmed; + + confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label); + -+ gs_dnf5_rpm_repo_call_confirm_key (repo_proxy, data->key_id, confirmed, NULL, gs_dnf5_confirm_key_response_cb, NULL); ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean (data->interactive)); ++ ++ gs_dnf5_rpm_repo_call_confirm_key_with_options (repo_proxy, ++ data->key_id, ++ confirmed, ++ g_variant_builder_end (options_builder), ++ NULL, ++ gs_dnf5_confirm_key_response_cb, ++ NULL); + } + + return G_SOURCE_REMOVE; @@ -1672,6 +1724,7 @@ index 000000000..fefad6961 + GsPluginDnf5 *self; /* (not owned) */ + const gchar *session_path; /* (not owned) */ + GsDnf5RpmRepo *repo_proxy; /* (not owned) */ ++ gboolean interactive; +} RepoKeyImportData; + +static void @@ -1746,6 +1799,7 @@ index 000000000..fefad6961 + data->accept_label = g_strdup (_("_Import Key")); + data->key_id = g_strdup (arg_key_id); + data->self = g_object_ref (repo_key_import_data->self); ++ data->interactive = repo_key_import_data->interactive; + g_weak_ref_init (&data->repo_proxy_weakref, repo_key_import_data->repo_proxy); + + g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free); @@ -1829,7 +1883,8 @@ index 000000000..fefad6961 + GS_DNF5_TRANSACTION_FLAG_NONE = 0, + GS_DNF5_TRANSACTION_FLAG_OFFLINE = 1 << 0, + GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY = 1 << 1, -+ GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING = 1 << 2 /* negative flag, to not have it everywhere */ ++ GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING = 1 << 2, /* negative flag, to not have it everywhere */ ++ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE = 1 << 3 +} GsDnf5TransactionFlags; + +static gboolean @@ -1855,6 +1910,7 @@ index 000000000..fefad6961 + gulong repo_key_import_id; + gulong cancellable_id = 0; + guint result = 0; ++ gboolean interactive = (flags & GS_DNF5_TRANSACTION_FLAG_INTERACTIVE) != 0; + gboolean success; + + base_proxy = gs_dnf5_base_proxy_new_sync (self->connection, @@ -1906,6 +1962,7 @@ index 000000000..fefad6961 + repo_key_import_data.self = self; + repo_key_import_data.session_path = session_path; + repo_key_import_data.repo_proxy = repo_proxy; ++ repo_key_import_data.interactive = interactive; + repo_key_import_id = g_signal_connect (base_proxy, "repo_key_import_request", + G_CALLBACK (gs_dnf5_repo_key_import_request_cb), &repo_key_import_data); + @@ -1958,6 +2015,8 @@ index 000000000..fefad6961 + g_variant_builder_add (options_builder, "{sv}", "offline", + g_variant_new_boolean (TRUE)); + } ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean (interactive)); + + run_cancellable = g_cancellable_new (); + if (cancellable != NULL) { @@ -2231,6 +2290,91 @@ index 000000000..fefad6961 + return TRUE; +} + ++static gboolean ++gs_dnf5_read_history_packages_cb (GsPluginDnf5 *self, ++ GVariant *array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ ReadPackageData *rpd = user_data; ++ g_autoptr(GsApp) app = NULL; ++ g_autoptr(GVariantDict) dict = NULL; ++ ++ dict = g_variant_dict_new (array); ++ ++ app = gs_dnf5_read_package_from_dict (self, dict, GS_DNF5_READ_PACKAGE_FLAG_NONE, rpd->set_state); ++ if (app != NULL) { ++ g_autoptr(GVariant) value = NULL; ++ value = g_variant_dict_lookup_value (dict, "original_evr", G_VARIANT_TYPE_STRING); ++ if (value != NULL) { ++ const gchar *str = g_variant_get_string (value, NULL); ++ if (str != NULL) { ++ const gchar *delim = strchr (str, ':'); ++ /* skip the epoch, if set, because it's skipped in the current version too */ ++ gs_app_set_version (app, delim ? delim + 1 : str); ++ } ++ } ++ ++ g_clear_pointer (&value, g_variant_unref); ++ value = g_variant_dict_lookup_value (dict, "advisories", G_VARIANT_TYPE_ARRAY); ++ if (value != NULL) { ++ gs_app_set_metadata_variant (app, GS_DNF5_ADVISORIES_KEY, value); ++ } ++ ++ /* do not know the exact change date/time, it uses the build time and the one from the settings as a fallback */ ++ if (!gs_app_get_install_date (app)) ++ gs_app_set_install_date (app, rpd->timestamp); ++ gs_app_list_add (rpd->list, app); ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_read_history_items_cb (GsPluginDnf5 *self, ++ GVariant *array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ struct _known_changes { ++ const gchar *type_of_change; ++ GsAppState set_state; ++ } known_changes[] = { ++ { "installed", GS_APP_STATE_UPDATABLE }, ++ { "removed", GS_APP_STATE_UNAVAILABLE }, ++ { "upgraded", GS_APP_STATE_UPDATABLE }, ++ { "downgraded", GS_APP_STATE_UPDATABLE } ++ }; ++ ReadPackageData *rpd = user_data; ++ g_autoptr(GVariant) value = NULL; ++ const gchar *type_of_change; ++ guint ii; ++ ++ value = g_variant_get_child_value (array, 0); ++ if (value == NULL) ++ return TRUE; ++ ++ type_of_change = g_variant_get_string (value, NULL); ++ if (!type_of_change) ++ return TRUE; ++ ++ for (ii = 0; ii < G_N_ELEMENTS (known_changes); ii++) { ++ if (g_strcmp0 (type_of_change, known_changes[ii].type_of_change) == 0) { ++ g_autoptr(GVariant) packages = NULL; ++ packages = g_variant_get_child_value (array, 1); ++ if (packages != NULL) { ++ rpd->set_state = known_changes[ii].set_state; ++ gs_dnf5_foreach_item (self, packages, gs_dnf5_read_history_packages_cb, rpd, cancellable, error); ++ } ++ break; ++ } ++ } ++ ++ return TRUE; ++} ++ +/* Run in @worker. */ +static void +gs_dnf5_list_apps_thread_cb (GTask *task, @@ -2240,6 +2384,7 @@ index 000000000..fefad6961 +{ + GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); + GsAppQueryTristate is_for_update = GS_APP_QUERY_TRISTATE_UNSET; ++ GsAppQueryTristate is_historical_update = GS_APP_QUERY_TRISTATE_UNSET; + const AsComponentKind *component_kinds = NULL; + GsPluginListAppsData *data = task_data; + const gchar * const *provides_files = NULL; @@ -2256,16 +2401,19 @@ index 000000000..fefad6961 + + if (data->query != NULL) { + is_for_update = gs_app_query_get_is_for_update (data->query); ++ is_historical_update = gs_app_query_get_is_historical_update (data->query); + component_kinds = gs_app_query_get_component_kinds (data->query); + provides_files = gs_app_query_get_provides_files (data->query); + provides_type = gs_app_query_get_provides (data->query, &provides_tag); + } + + if ((is_for_update == GS_APP_QUERY_TRISTATE_UNSET && ++ is_historical_update == GS_APP_QUERY_TRISTATE_UNSET && + component_kinds == NULL && + provides_tag == NULL && + provides_files == NULL) || + is_for_update == GS_APP_QUERY_TRISTATE_FALSE || ++ is_historical_update == GS_APP_QUERY_TRISTATE_FALSE || + (component_kinds != NULL && !gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) || + gs_app_query_get_n_properties_set (data->query) != 1) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, @@ -2421,6 +2569,57 @@ index 000000000..fefad6961 + gs_dnf5_convert_error (&local_error); + g_prefix_error_literal (&local_error, "Failed to call Rpm::list: "); + } ++ } else if (is_historical_update == GS_APP_QUERY_TRISTATE_TRUE) { ++ g_autoptr(GsDnf5History) history_proxy = NULL; ++ ++ history_proxy = gs_dnf5_history_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = history_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create History proxy: "); ++ } ++ ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ g_autoptr(GSettings) settings; ++ gint64 timestamp; ++ ++ /* use the option the GUI part uses */ ++ settings = g_settings_new ("org.gnome.software"); ++ timestamp = g_settings_get_int64 (settings, "install-timestamp"); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "since", ++ g_variant_new_int64 (timestamp)); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ gs_dnf5_dup_package_attrs ()); ++ ++ success = gs_dnf5_history_call_recent_changes_sync (history_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ &local_error); ++ ++ if (success) { ++ ReadPackageData rpd = { 0, }; ++ ++ rpd.list = list; ++ rpd.set_state = GS_APP_STATE_UNKNOWN; ++ rpd.nevra_to_app = NULL; ++ rpd.timestamp = timestamp; ++ ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_history_items_cb, &rpd, cancellable, &local_error); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call recent_changes: "); ++ } ++ } + } else if (gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) { + g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; + @@ -2867,6 +3066,87 @@ index 000000000..fefad6961 + return TRUE; +} + ++static gboolean ++gs_dnf5_add_advisories_cb (GsPluginDnf5 *self, ++ GVariant *value, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GHashTable *advisories = user_data; /* gchar *name ~> gchar *description */ ++ g_autofree gchar *str = NULL; ++ ++ str = g_variant_dup_string (value, NULL); ++ if (str != NULL && *str != '\0') ++ g_hash_table_insert (advisories, g_steal_pointer (&str), NULL); ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_gather_update_description_for_advisory_cb (GsPluginDnf5 *self, ++ GVariant *value, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GHashTable *advisories = user_data; /* gchar *name ~> gchar *description */ ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) name = NULL; ++ g_autoptr(GVariant) description = NULL; ++ const gchar *name_str, *description_str; ++ ++ dict = g_variant_dict_new (value); ++ ++ name = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); ++ if (name == NULL) ++ return TRUE; ++ ++ description = g_variant_dict_lookup_value (dict, "description", G_VARIANT_TYPE_STRING); ++ if (description == NULL) ++ return TRUE; ++ ++ name_str = g_variant_get_string (name, NULL); ++ description_str = g_variant_get_string (description, NULL); ++ ++ if (name_str != NULL && description_str != NULL && *description_str != '\0') ++ g_hash_table_replace (advisories, g_strdup (name_str), g_strdup (description_str)); ++ ++ return TRUE; ++} ++ ++typedef struct _AdvisoryUpdateDescriptionData { ++ GHashTable *advisories; /* gchar *name ~> gchar *description */ ++ GString *description; ++} AdvisoryUpdateDescriptionData; ++ ++static gboolean ++gs_dnf5_refine_update_description_from_advisory_cb (GsPluginDnf5 *self, ++ GVariant *value, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ AdvisoryUpdateDescriptionData *adv_data = user_data; ++ const gchar *name = NULL; ++ ++ name = g_variant_get_string (value, NULL); ++ if (name != NULL && *name != '\0') { ++ const gchar *description = g_hash_table_lookup (adv_data->advisories, name); ++ if (description != NULL && *description != '\0') { ++ if (adv_data->description == NULL) { ++ adv_data->description = g_string_new (description); ++ } else { ++ g_string_append (adv_data->description, "\n\n"); ++ g_string_append (adv_data->description, description); ++ } ++ } ++ } ++ ++ return TRUE; ++} ++ ++ +/* Run in @worker. */ +static void +gs_dnf5_refine_thread_cb (GTask *task, @@ -2891,6 +3171,7 @@ index 000000000..fefad6961 + GsApp *app = gs_app_list_index (data->list, i); + if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && ( + gs_app_get_state (app) == GS_APP_STATE_UNKNOWN || ++ (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS) != 0 || + ((data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_SIZE_DATA) != 0 && + gs_app_list_length (gs_app_get_related (app)) == 0))) { + if (gs_app_get_default_source (app) != NULL && @@ -2926,7 +3207,127 @@ index 000000000..fefad6961 + g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); + } + -+ if (success) { ++ if (success && (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS) != 0) { ++ g_autoptr(GHashTable) advisories = NULL; /* gchar *name ~> gchar *description */ ++ GHashTableIter iter; ++ gpointer value = NULL; ++ ++ data->require_flags = data->require_flags & (~GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS); ++ ++ advisories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); ++ ++ g_hash_table_iter_init (&iter, apps); ++ while (success && g_hash_table_iter_next (&iter, NULL, &value)) { ++ GsApp *app = value; ++ GVariant *advisories_array; ++ ++ advisories_array = gs_app_get_metadata_variant (app, GS_DNF5_ADVISORIES_KEY); ++ if (advisories_array != NULL) ++ success = gs_dnf5_foreach_item (self, advisories_array, gs_dnf5_add_advisories_cb, advisories, cancellable, &local_error); ++ } ++ ++ if (success && g_hash_table_size (advisories) > 0) { ++ g_autoptr(GsDnf5Advisory) advisory_proxy = NULL; ++ ++ advisory_proxy = gs_dnf5_advisory_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = advisory_proxy != NULL; ++ ++ if (success) { ++ g_autoptr(GVariantBuilder) adv_options_builder = NULL; ++ g_autoptr(GVariantBuilder) attrs_builder = NULL; ++ g_autoptr(GVariantBuilder) names_builder = NULL; ++ g_autoptr(GVariant) adv_result = NULL; ++ gpointer key = NULL; ++ ++ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (attrs_builder, "s", "name"); ++ g_variant_builder_add (attrs_builder, "s", "description"); ++ ++ names_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_hash_table_iter_init (&iter, advisories); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) { ++ const gchar *name = key; ++ g_variant_builder_add (names_builder, "s", name); ++ } ++ ++ adv_options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (adv_options_builder, "{sv}", "advisory_attrs", ++ g_variant_builder_end (attrs_builder)); ++ g_variant_builder_add (adv_options_builder, "{sv}", "names", ++ g_variant_builder_end (names_builder)); ++ g_variant_builder_add (adv_options_builder, "{sv}", "availability", ++ g_variant_new_string ("all")); ++ ++ success = gs_dnf5_advisory_call_list_sync (advisory_proxy, ++ g_variant_builder_end (adv_options_builder), ++ &adv_result, ++ cancellable, ++ &local_error); ++ if (success) { ++ success = gs_dnf5_foreach_item (self, adv_result, ++ gs_dnf5_gather_update_description_for_advisory_cb, advisories, ++ cancellable, &local_error); ++ if (success) { ++ g_hash_table_iter_init (&iter, apps); ++ while (success && g_hash_table_iter_next (&iter, NULL, &value)) { ++ GsApp *app = value; ++ GVariant *advisories_array; ++ ++ advisories_array = gs_app_get_metadata_variant (app, GS_DNF5_ADVISORIES_KEY); ++ if (advisories_array != NULL) { ++ AdvisoryUpdateDescriptionData adv_data = { NULL, }; ++ adv_data.advisories = advisories; ++ adv_data.description = NULL; ++ ++ success = gs_dnf5_foreach_item (self, advisories_array, ++ gs_dnf5_refine_update_description_from_advisory_cb, ++ &adv_data, cancellable, &local_error); ++ ++ if (adv_data.description) { ++ g_autoptr(GsMarkdown) markdown = gs_markdown_new (GS_MARKDOWN_OUTPUT_PANGO); ++ g_autofree gchar *markup = gs_markdown_parse (markdown, adv_data.description->str); ++ if (markup != NULL && *markup != '\0') ++ gs_app_set_update_details_markup (app, markup); ++ else ++ gs_app_set_update_details_text (app, adv_data.description->str); ++ g_string_free (adv_data.description, TRUE); ++ } ++ } ++ } ++ ++ if (!success) ++ g_debug ("Failed to refine update description from advisory: %s", local_error->message); ++ } else { ++ g_debug ("Failed to gather update description for advisory: %s", local_error->message); ++ } ++ } else { ++ g_debug ("Failed to call Advisory::list: %s", local_error->message); ++ } ++ } else { ++ g_debug ("Failed to create Advisory proxy: %s", local_error->message); ++ } ++ } ++ ++ /* fallback to changelogs where advisory was not known */ ++ g_hash_table_iter_init (&iter, apps); ++ while (success && g_hash_table_iter_next (&iter, NULL, &value)) { ++ GsApp *app = value; ++ if (!gs_app_get_update_details_markup (app)) { ++ const gchar *changelogs = gs_app_get_metadata_item (app, GS_DNF5_CHANGELOGS_KEY); ++ if (changelogs != NULL && *changelogs != '\0') { ++ gs_app_set_update_details_text (app, changelogs); ++ gs_app_set_metadata (app, GS_DNF5_CHANGELOGS_KEY, NULL); ++ } ++ } ++ } ++ } ++ ++ if (success && data->require_flags != 0) { + g_autoptr(GVariantBuilder) attrs_builder = NULL; + g_autoptr(GVariantBuilder) options_builder = NULL; + g_autoptr(GVariantBuilder) patterns_builder = NULL; @@ -3041,6 +3442,7 @@ index 000000000..fefad6961 + GCancellable *cancellable) +{ + GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPluginRefreshMetadataData *data = task_data; + g_autofree gchar *session_path = NULL; + g_autoptr(GsDnf5SessionManager) session_manager = NULL; + g_autoptr(GsDnf5Base) base_proxy = NULL; @@ -3068,16 +3470,23 @@ index 000000000..fefad6961 + g_prefix_error_literal (&local_error, "Failed to create Base proxy: "); + } + -+ if (success && GPOINTER_TO_INT (task_data) == 1) { ++ if (success && data->cache_age_secs < 60) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; + g_autofree gchar *op_error_msg = NULL; + gboolean op_success = FALSE; + -+ success = gs_dnf5_base_call_clean_sync (base_proxy, -+ "expire-cache", -+ &op_success, -+ &op_error_msg, -+ cancellable, -+ &local_error); ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean ((data->flags & GS_PLUGIN_REFRESH_METADATA_FLAGS_INTERACTIVE) != 0)); ++ ++ success = gs_dnf5_base_call_clean_with_options_sync (base_proxy, ++ "expire-cache", ++ g_variant_builder_end (options_builder), ++ &op_success, ++ &op_error_msg, ++ cancellable, ++ &local_error); + if (!success) + g_debug ("Failed to call expire-cache: %s", local_error->message); + else if (!op_success) @@ -3130,9 +3539,9 @@ index 000000000..fefad6961 + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_dnf5_refresh_metadata_async); -+ /* no need to save whole structure, the seconds are not used at all, -+ thus make it only a flag whether to invalidate the cache or not first */ -+ g_task_set_task_data (task, GINT_TO_POINTER (cache_age_secs < 60 ? 1 : 0), NULL); ++ g_task_set_task_data (task, ++ gs_plugin_refresh_metadata_data_new (cache_age_secs, flags, event_callback, event_user_data), ++ (GDestroyNotify) gs_plugin_refresh_metadata_data_free); + + gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), + gs_dnf5_refresh_metadata_thread_cb, g_steal_pointer (&task)); @@ -3289,10 +3698,13 @@ index 000000000..fefad6961 + success = rpm_proxy != NULL; + if (success) { + g_autoptr(GVariantBuilder) options_builder = NULL; ++ GsDnf5TransactionFlags flags = interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE; + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); + + options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean (interactive)); + + /* NULL-terminate the array, to be like strv */ + g_ptr_array_add (packages, NULL); @@ -3316,7 +3728,7 @@ index 000000000..fefad6961 + error); + } + success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, progress_list, -+ is_install ? GS_DNF5_TRANSACTION_FLAG_NONE : GS_DNF5_TRANSACTION_FLAG_OFFLINE, ++ flags | (is_install ? 0 : GS_DNF5_TRANSACTION_FLAG_OFFLINE), + NULL, cancellable, error); + if (success && !is_install) { + /* ensure the update is finished with the 'reboot' action */ @@ -3333,7 +3745,19 @@ index 000000000..fefad6961 + g_autofree gchar *op_error_msg = NULL; + gboolean op_success = FALSE; + -+ success = gs_dnf5_offline_call_set_finish_action_sync (offline_proxy, "reboot", &op_success, &op_error_msg, cancellable, error); ++ g_clear_pointer (&options_builder, g_variant_builder_unref); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean (interactive)); ++ ++ success = gs_dnf5_offline_call_set_finish_action_with_options_sync (offline_proxy, ++ "reboot", ++ g_variant_builder_end (options_builder), ++ &op_success, ++ &op_error_msg, ++ cancellable, ++ error); + if (success && !op_success) { + success = FALSE; + if (op_error_msg != NULL) { @@ -3556,6 +3980,8 @@ index 000000000..fefad6961 + &local_error); + success = rpm_proxy != NULL; + if (success) { ++ GsDnf5TransactionFlags flags = (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0 ? ++ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE; + g_autoptr(GVariantBuilder) options_builder = NULL; + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); @@ -3575,7 +4001,7 @@ index 000000000..fefad6961 + g_variant_builder_end (options_builder), + cancellable, + &local_error); -+ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, GS_DNF5_TRANSACTION_FLAG_NONE, NULL, cancellable, &local_error); ++ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, flags, NULL, cancellable, &local_error); + } else { + gs_dnf5_convert_error (&local_error); + g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); @@ -3718,6 +4144,7 @@ index 000000000..fefad6961 +gs_dnf5_manage_repository_sync (GsPluginDnf5 *self, + GsApp *repository, + gboolean enable, ++ gboolean interactive, + GCancellable *cancellable, + GError **error) +{ @@ -3758,15 +4185,20 @@ index 000000000..fefad6961 + + if (success) { + const gchar *ids[2] = { NULL, NULL }; ++ g_autoptr(GVariantBuilder) options_builder = NULL; + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (repo_proxy), G_MAXINT); + + ids[0] = gs_app_get_id (repository); + ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean (interactive)); ++ + if (enable) -+ success = gs_dnf5_rpm_repo_call_enable_sync (repo_proxy, ids, cancellable, error); ++ success = gs_dnf5_rpm_repo_call_enable_with_options_sync (repo_proxy, ids, g_variant_builder_end (options_builder), cancellable, error); + else -+ success = gs_dnf5_rpm_repo_call_disable_sync (repo_proxy, ids, cancellable, error); ++ success = gs_dnf5_rpm_repo_call_disable_with_options_sync (repo_proxy, ids, g_variant_builder_end (options_builder), cancellable, error); + + if (success) { + if (enable) @@ -3797,7 +4229,9 @@ index 000000000..fefad6961 + + assert_in_worker (self); + -+ if (gs_dnf5_manage_repository_sync (self, data->repository, TRUE, cancellable, &local_error)) { ++ if (gs_dnf5_manage_repository_sync (self, data->repository, TRUE, ++ (data->flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0, ++ cancellable, &local_error)) { + g_task_return_boolean (task, TRUE); + } else { + gs_dnf5_convert_error (&local_error); @@ -3848,7 +4282,9 @@ index 000000000..fefad6961 + + assert_in_worker (self); + -+ if (gs_dnf5_manage_repository_sync (self, data->repository, FALSE, cancellable, &local_error)) { ++ if (gs_dnf5_manage_repository_sync (self, data->repository, FALSE, ++ (data->flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0, ++ cancellable, &local_error)) { + g_task_return_boolean (task, TRUE); + } else { + gs_dnf5_convert_error (&local_error); @@ -4085,6 +4521,7 @@ index 000000000..fefad6961 + const gchar *releasever, + GsApp *progress_app, + gboolean download_only, ++ gboolean interactive, + GCancellable *cancellable, + GError **error) +{ @@ -4152,6 +4589,8 @@ index 000000000..fefad6961 + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); + + options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean (interactive)); + + success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, error); + @@ -4162,14 +4601,23 @@ index 000000000..fefad6961 + error); + + if (success) { -+ success = gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, GS_DNF5_TRANSACTION_FLAG_OFFLINE, NULL, cancellable, error); ++ GsDnf5TransactionFlags flags = interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE; ++ ++ success = gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, flags | GS_DNF5_TRANSACTION_FLAG_OFFLINE, NULL, cancellable, error); + + if (success && !download_only) { + g_autofree gchar *op_error_msg = NULL; + gboolean op_success = FALSE; + ++ g_clear_pointer (&options_builder, g_variant_builder_unref); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean (interactive)); ++ + /* ensure the upgrade is finished with the 'reboot' action */ -+ success = gs_dnf5_offline_call_set_finish_action_sync (offline_proxy, "reboot", &op_success, &op_error_msg, cancellable, error); ++ success = gs_dnf5_offline_call_set_finish_action_with_options_sync (offline_proxy, "reboot", g_variant_builder_end (options_builder), ++ &op_success, &op_error_msg, cancellable, error); + if (success && !op_success) { + success = FALSE; + if (op_error_msg != NULL) { @@ -4207,7 +4655,9 @@ index 000000000..fefad6961 + + gs_app_set_state (data->app, GS_APP_STATE_DOWNLOADING); + -+ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, TRUE, cancellable, &local_error)) { ++ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, TRUE, ++ (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0, ++ cancellable, &local_error)) { + gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE); + g_task_return_boolean (task, TRUE); + } else { @@ -4275,7 +4725,9 @@ index 000000000..fefad6961 + + gs_app_set_state (data->app, GS_APP_STATE_PENDING_INSTALL); + -+ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, FALSE, cancellable, &local_error)) { ++ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, FALSE, ++ (data->flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0, ++ cancellable, &local_error)) { + gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE); + g_task_return_boolean (task, TRUE); + } else { @@ -4465,17 +4917,19 @@ index 000000000..319d5843c +G_END_DECLS diff --git a/plugins/dnf5/meson.build b/plugins/dnf5/meson.build new file mode 100644 -index 000000000..6059a9da1 +index 000000000..12b2137e1 --- /dev/null +++ b/plugins/dnf5/meson.build -@@ -0,0 +1,39 @@ +@@ -0,0 +1,42 @@ +cargs = ['-DG_LOG_DOMAIN="GsDnf5"'] + +dnf5_generated = gnome.gdbus_codegen( + 'gs-dnf5-generated', + sources: [ 'org.rpm.dnf.v0.Advisory.xml', + 'org.rpm.dnf.v0.Base.xml', ++ 'org.rpm.dnf.v0.comps.Group.xml', + 'org.rpm.dnf.v0.Goal.xml', ++ 'org.rpm.dnf.v0.History.xml', + 'org.rpm.dnf.v0.Offline.xml', + 'org.rpm.dnf.v0.rpm.Repo.xml', + 'org.rpm.dnf.v0.rpm.Rpm.xml', @@ -4497,7 +4951,8 @@ index 000000000..6059a9da1 + dnf5_generated, + sources : [ + 'gs-dnf5-progress-helper.c', -+ 'gs-plugin-dnf5.c' ++ 'gs-plugin-dnf5.c', ++ '../packagekit/gs-markdown.c', + ], + include_directories : [ + include_directories('../..'), @@ -4622,10 +5077,10 @@ index 000000000..6aecee9fa + diff --git a/plugins/dnf5/org.rpm.dnf.v0.Base.xml b/plugins/dnf5/org.rpm.dnf.v0.Base.xml new file mode 100644 -index 000000000..dfdc1745a +index 000000000..e596e60b9 --- /dev/null +++ b/plugins/dnf5/org.rpm.dnf.v0.Base.xml -@@ -0,0 +1,153 @@ +@@ -0,0 +1,176 @@ + + @@ -4664,13 +5119,36 @@ index 000000000..dfdc1745a + + + ++ ++ ++ ++ ++ ++ ++ ++ + + @@ -4680,7 +5158,7 @@ index 000000000..dfdc1745a + + @@ -4883,7 +5365,7 @@ index 000000000..f1ba08953 + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ diff --git a/plugins/dnf5/org.rpm.dnf.v0.Offline.xml b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml new file mode 100644 -index 000000000..083b5b17c +index 000000000..8d84c22aa --- /dev/null +++ b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml -@@ -0,0 +1,82 @@ +@@ -0,0 +1,151 @@ + + @@ -4948,6 +5520,25 @@ index 000000000..083b5b17c + + + ++ ++ ++ ++ ++ ++ + + + + @@ -4962,13 +5555,58 @@ index 000000000..083b5b17c + + + ++ ++ ++ ++ ++ ++ + ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + @@ -4981,6 +5619,9 @@ index 000000000..083b5b17c + + Set the action that should be performed after the offline transaction is applied. If the `action` is "poweroff", the system will be powered off, otherwise it will be rebooted (which is default). + The call might fail in case there is no scheduled offline transaction, or the transaction was not scheduled using libdnf5. ++ ++ ++ This is equivalent to call "set_finish_action_with_options()" with empty options. + --> + + @@ -5066,12 +5707,79 @@ index 000000000..a0facd692 + + + +diff --git a/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml b/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml +new file mode 100644 +index 000000000..b12e0ac6d +--- /dev/null ++++ b/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml +@@ -0,0 +1,61 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml new file mode 100644 -index 000000000..13c4cf1f8 +index 000000000..50b2ba3bf --- /dev/null +++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml -@@ -0,0 +1,85 @@ +@@ -0,0 +1,147 @@ + + @@ -5123,11 +5831,33 @@ index 000000000..13c4cf1f8 + + + ++ ++ ++ ++ ++ ++ ++ + + @@ -5135,20 +5865,60 @@ index 000000000..13c4cf1f8 + + + ++ ++ ++ ++ ++ ++ + + + + + ++ ++ ++ ++ ++ ++ + + @@ -5159,10 +5929,10 @@ index 000000000..13c4cf1f8 + diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml new file mode 100644 -index 000000000..5df68cd91 +index 000000000..f24b14d39 --- /dev/null +++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml -@@ -0,0 +1,468 @@ +@@ -0,0 +1,470 @@ + + @@ -5388,6 +6158,8 @@ index 000000000..5df68cd91 + + - mode: string (one of "distrosync", "upgrade", default is "distrosync") + By default the system_upgrade behaves like `dnf distro-sync`, always installing packages from the new release, even if they are older than the currently installed version. If set to "upgrade", packages from the new release are not installed if they are older than what is currently installed (behave like `dnf upgrade`). ++ - interactive: boolean, default true ++ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done. + + Unknown options are ignored. + --> diff --git a/gnome-software.spec b/gnome-software.spec index 198e7c9..10c8d74 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -7,6 +7,7 @@ %global libadwaita_version 1.6.0 %global libxmlb_version 0.3.4 %global packagekit_version 1.2.5 +%global dnf5_version 5.2.16 # Disable WebApps for RHEL builds %bcond webapps %[!0%{?rhel}] @@ -29,7 +30,7 @@ Name: gnome-software Version: 49~beta -Release: 2%{?dist} +Release: 3%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -91,7 +92,7 @@ Requires: appstream%{?_isa} >= %{appstream_version} Requires: epiphany-runtime%{?_isa} %endif %if %{with dnf5} -Requires: dnf5daemon-server%{?_isa} +Requires: dnf5daemon-server%{?_isa} >= %{dnf5_version} Requires: dnf5daemon-server-polkit Requires: libdnf5-plugin-appstream%{?_isa} Requires: rpm-plugin-dbus-announce%{?_isa} From eaee792f9f386eb90797895cb9f6637e2b0ff6bc Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Wed, 13 Aug 2025 18:32:50 +0200 Subject: [PATCH 33/44] dnf5-plugin: Auto-accept new RPM keys --- 0001-dnf5-plugin.patch | 12 +++++++----- gnome-software.spec | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index 3e9a13f..0ba8881 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,5 +1,5 @@ -at commit 8a1f0ca6564157dd688c84ffd25af9747c1e038e -Date: Mon Aug 11 17:07:06 2025 +0200 +at commit d7f516e25742e1c0631194df5829bf73668f7df5 +Date: Wed Aug 13 18:23:59 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt index cd49cf529..27fa15e8b 100644 @@ -610,10 +610,10 @@ index 000000000..5d522468c +G_END_DECLS diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c new file mode 100644 -index 000000000..88d7e25d8 +index 000000000..36d10163a --- /dev/null +++ b/plugins/dnf5/gs-plugin-dnf5.c -@@ -0,0 +1,4275 @@ +@@ -0,0 +1,4277 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -1702,7 +1702,9 @@ index 000000000..88d7e25d8 + g_autoptr(GVariantBuilder) options_builder = NULL; + gboolean confirmed; + -+ confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label); ++ /* auto-accept new keys, just as PacakgeKit plugin used to do */ ++ confirmed = TRUE; ++ /* confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label); */ + + options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (options_builder, "{sv}", "interactive", diff --git a/gnome-software.spec b/gnome-software.spec index 10c8d74..21466da 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -30,7 +30,7 @@ Name: gnome-software Version: 49~beta -Release: 3%{?dist} +Release: 4%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later From 446ce42aa8d01622a25810693ed527f115afddbe Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 25 Aug 2025 12:05:46 +0200 Subject: [PATCH 34/44] dnf5-plugin: Auto-accept new RPM keys only when installed from repos - dnf5-plugin: Download offline updates also by regular users --- 0001-dnf5-plugin.patch | 327 +++++++++++++++++++++++++++++++++++++++-- gnome-software.spec | 2 +- 2 files changed, 317 insertions(+), 12 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index 0ba8881..088ad51 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,5 +1,5 @@ -at commit d7f516e25742e1c0631194df5829bf73668f7df5 -Date: Wed Aug 13 18:23:59 2025 +0200 +at commit 55ca6d4d49d5edd682b8ca15904ec556eb5d0874 +Date: Tue Aug 19 13:13:48 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt index cd49cf529..27fa15e8b 100644 @@ -610,10 +610,10 @@ index 000000000..5d522468c +G_END_DECLS diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c new file mode 100644 -index 000000000..36d10163a +index 000000000..c8d9ed83d --- /dev/null +++ b/plugins/dnf5/gs-plugin-dnf5.c -@@ -0,0 +1,4277 @@ +@@ -0,0 +1,4582 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -1671,9 +1671,13 @@ index 000000000..36d10163a + gchar *details; + gchar *accept_label; + gchar *key_id; ++ gchar *key_filename; ++ gchar *session_path; ++ GCancellable *cancellable; /* (owned) */ + GsPluginDnf5 *self; /* (owned) */ + GWeakRef repo_proxy_weakref; /* GsDnf5RpmRepo */ + gboolean interactive; ++ gboolean confirmed_by_repo; +} QuestionData; + +static void @@ -1685,6 +1689,9 @@ index 000000000..36d10163a + g_free (data->details); + g_free (data->accept_label); + g_free (data->key_id); ++ g_free (data->key_filename); ++ g_free (data->session_path); ++ g_clear_object (&data->cancellable); + g_clear_object (&data->self); + g_weak_ref_clear (&data->repo_proxy_weakref); + g_free (data); @@ -1703,8 +1710,9 @@ index 000000000..36d10163a + gboolean confirmed; + + /* auto-accept new keys, just as PacakgeKit plugin used to do */ -+ confirmed = TRUE; -+ /* confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label); */ ++ confirmed = data->confirmed_by_repo; ++ if (!confirmed) ++ confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label); + + options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (options_builder, "{sv}", "interactive", @@ -1714,7 +1722,7 @@ index 000000000..36d10163a + data->key_id, + confirmed, + g_variant_builder_end (options_builder), -+ NULL, ++ data->cancellable, + gs_dnf5_confirm_key_response_cb, + NULL); + } @@ -1722,10 +1730,184 @@ index 000000000..36d10163a + return G_SOURCE_REMOVE; +} + ++static gboolean ++gs_dnf5_read_package_repo_ids_cb (GsPluginDnf5 *self, ++ GVariant *package_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GPtrArray **out_repo_ids = user_data; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) value = NULL; ++ ++ dict = g_variant_dict_new (package_array); ++ ++ value = g_variant_dict_lookup_value (dict, "from_repo_id", G_VARIANT_TYPE_STRING); ++ if (value != NULL) { ++ const gchar *repo_id_const = g_variant_get_string (value, NULL); ++ if (repo_id_const != NULL && *repo_id_const != '\0') { ++ if (*out_repo_ids == NULL) ++ *out_repo_ids = g_ptr_array_new_with_free_func (g_free); ++ g_ptr_array_add (*out_repo_ids, g_strdup (repo_id_const)); ++ } ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++gs_dnf5_read_repo_gpg_info_cb (GsPluginDnf5 *self, ++ GVariant *package_array, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ guint *out_n_good = user_data; ++ g_autoptr(GVariantDict) dict = NULL; ++ g_autoptr(GVariant) id_value = NULL; ++ g_autoptr(GVariant) gpgcheck_value = NULL; ++ ++ dict = g_variant_dict_new (package_array); ++ ++ id_value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_STRING); ++ gpgcheck_value = g_variant_dict_lookup_value (dict, "gpgcheck", G_VARIANT_TYPE_BOOLEAN); ++ ++ if (id_value != NULL && gpgcheck_value != NULL) { ++ gboolean gpgcheck = g_variant_get_boolean (gpgcheck_value); ++ ++ if (gpgcheck) ++ *out_n_good = (*out_n_good) + 1; ++ } ++ ++ return TRUE; ++} ++ ++static gpointer ++gs_dnf5_check_key_source_thread (gpointer user_data) ++{ ++ QuestionData *data = user_data; ++ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; ++ g_autoptr(GPtrArray) repo_ids = NULL; ++ g_autoptr(GError) local_error = NULL; ++ gboolean success; ++ ++ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (data->self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ data->session_path, ++ data->cancellable, ++ &local_error); ++ success = rpm_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); ++ } ++ ++ /* try to get whether the key comes from any package */ ++ if (success) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariantBuilder) package_attrs_builder = NULL; ++ g_autoptr(GVariantBuilder) whatprovides_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ ++ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); ++ ++ package_attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (package_attrs_builder, "s", "from_repo_id"); ++ ++ whatprovides_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (whatprovides_builder, "s", data->key_filename); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ g_variant_builder_end (package_attrs_builder)); ++ g_variant_builder_add (options_builder, "{sv}", "whatprovides", ++ g_variant_builder_end (whatprovides_builder)); ++ g_variant_builder_add (options_builder, "{sv}", "scope", ++ g_variant_new_string ("installed")); ++ ++ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ data->cancellable, ++ &local_error); ++ ++ if (success) { ++ success = gs_dnf5_foreach_item (data->self, result, gs_dnf5_read_package_repo_ids_cb, &repo_ids, data->cancellable, &local_error); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call rpm::list: "); ++ } ++ } ++ ++ if (success && repo_ids != NULL) { ++ g_autoptr(GVariantBuilder) patterns_builder = NULL; ++ guint n_verify = 0; ++ ++ for (guint i = 0; i < repo_ids->len; i++) { ++ const gchar *repo_id = g_ptr_array_index (repo_ids, i); ++ /* those are special repos, like `@commandline` */ ++ if (*repo_id != '@') { ++ if (patterns_builder == NULL) ++ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (patterns_builder, "s", repo_id); ++ n_verify++; ++ } ++ } ++ ++ if (patterns_builder != NULL) { ++ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; ++ ++ repo_proxy = g_weak_ref_get (&data->repo_proxy_weakref); ++ if (repo_proxy != NULL) { ++ g_autoptr(GVariantBuilder) repo_attrs_builder = NULL; ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) result = NULL; ++ ++ repo_attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); ++ g_variant_builder_add (repo_attrs_builder, "s", "id"); ++ g_variant_builder_add (repo_attrs_builder, "s", "gpgcheck"); ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "patterns", ++ g_variant_builder_end (patterns_builder)); ++ g_variant_builder_add (options_builder, "{sv}", "repo_attrs", ++ g_variant_builder_end (repo_attrs_builder)); ++ ++ success = gs_dnf5_rpm_repo_call_list_sync (repo_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ data->cancellable, ++ &local_error); ++ ++ if (success) { ++ guint n_good = 0; ++ ++ success = gs_dnf5_foreach_item (data->self, result, gs_dnf5_read_repo_gpg_info_cb, &n_good, data->cancellable, &local_error); ++ ++ data->confirmed_by_repo = success && n_verify == n_good; ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call repo::list: "); ++ } ++ } else { ++ success = FALSE; ++ g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, "Repo proxy vanished"); ++ } ++ } ++ } ++ ++ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free); ++ ++ return NULL; ++} ++ +typedef struct { + GsPluginDnf5 *self; /* (not owned) */ + const gchar *session_path; /* (not owned) */ + GsDnf5RpmRepo *repo_proxy; /* (not owned) */ ++ GCancellable *cancellable; /* (not owned) */ + gboolean interactive; +} RepoKeyImportData; + @@ -1800,11 +1982,18 @@ index 000000000..36d10163a + data->details = g_string_free (details, FALSE); + data->accept_label = g_strdup (_("_Import Key")); + data->key_id = g_strdup (arg_key_id); ++ data->key_filename = g_steal_pointer (&key_filename); ++ data->session_path = g_strdup (repo_key_import_data->session_path); + data->self = g_object_ref (repo_key_import_data->self); + data->interactive = repo_key_import_data->interactive; ++ data->confirmed_by_repo = FALSE; ++ g_set_object (&data->cancellable, repo_key_import_data->cancellable); + g_weak_ref_init (&data->repo_proxy_weakref, repo_key_import_data->repo_proxy); + -+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free); ++ if (data->key_filename != NULL) ++ g_thread_unref (g_thread_new ("gs-dnf5-check-key-source_thread", gs_dnf5_check_key_source_thread, data)); ++ else ++ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free); +} + +static void @@ -1886,7 +2075,8 @@ index 000000000..36d10163a + GS_DNF5_TRANSACTION_FLAG_OFFLINE = 1 << 0, + GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY = 1 << 1, + GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING = 1 << 2, /* negative flag, to not have it everywhere */ -+ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE = 1 << 3 ++ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE = 1 << 3, ++ GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY = 1 << 4 +} GsDnf5TransactionFlags; + +static gboolean @@ -1965,6 +2155,7 @@ index 000000000..36d10163a + repo_key_import_data.session_path = session_path; + repo_key_import_data.repo_proxy = repo_proxy; + repo_key_import_data.interactive = interactive; ++ repo_key_import_data.cancellable = cancellable; + repo_key_import_id = g_signal_connect (base_proxy, "repo_key_import_request", + G_CALLBACK (gs_dnf5_repo_key_import_request_cb), &repo_key_import_data); + @@ -2017,6 +2208,10 @@ index 000000000..36d10163a + g_variant_builder_add (options_builder, "{sv}", "offline", + g_variant_new_boolean (TRUE)); + } ++ if ((flags & GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY) != 0) { ++ g_variant_builder_add (options_builder, "{sv}", "downloadonly", ++ g_variant_new_boolean (TRUE)); ++ } + g_variant_builder_add (options_builder, "{sv}", "interactive", + g_variant_new_boolean (interactive)); + @@ -3576,6 +3771,7 @@ index 000000000..36d10163a + GsAppList *list, + gboolean is_install, + gboolean interactive, ++ gboolean can_apply, + GsPluginEventCallback event_callback, + void *event_user_data, + GCancellable *cancellable, @@ -3700,7 +3896,8 @@ index 000000000..36d10163a + success = rpm_proxy != NULL; + if (success) { + g_autoptr(GVariantBuilder) options_builder = NULL; -+ GsDnf5TransactionFlags flags = interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE; ++ GsDnf5TransactionFlags flags = (interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE) | ++ (!can_apply ? GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY : GS_DNF5_TRANSACTION_FLAG_NONE); + + g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); + @@ -3732,7 +3929,7 @@ index 000000000..36d10163a + success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, progress_list, + flags | (is_install ? 0 : GS_DNF5_TRANSACTION_FLAG_OFFLINE), + NULL, cancellable, error); -+ if (success && !is_install) { ++ if (success && !is_install && can_apply) { + /* ensure the update is finished with the 'reboot' action */ + g_autoptr(GsDnf5Offline) offline_proxy = NULL; + @@ -3852,6 +4049,7 @@ index 000000000..36d10163a + + success = gs_dnf5_install_update (plugin, data->apps, TRUE, + (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0, ++ (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_NO_APPLY) == 0, + data->event_callback, data->event_user_data, + cancellable, &local_error); + @@ -4095,6 +4293,7 @@ index 000000000..36d10163a + + success = gs_dnf5_install_update (plugin, data->apps, FALSE, + (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0, ++ (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_NO_APPLY) == 0, + data->event_callback, data->event_user_data, + cancellable, &local_error); + @@ -4780,6 +4979,110 @@ index 000000000..36d10163a + return g_task_propagate_boolean (G_TASK (result), error); +} + ++/* Run in @worker. */ ++static void ++gs_dnf5_cancel_offline_update_thread_cb (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); ++ GsPluginCancelOfflineUpdateData *data = task_data; ++ g_autofree gchar *session_path = NULL; ++ g_autoptr(GsAppList) list = NULL; ++ g_autoptr(GsDnf5SessionManager) session_manager = NULL; ++ g_autoptr(GsDnf5Offline) offline_proxy = NULL; ++ gboolean success; ++ g_autoptr(GError) local_error = NULL; ++ ++ assert_in_worker (self); ++ ++ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); ++ if (session_path == NULL) { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ return; ++ } ++ ++ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = offline_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create Offline proxy: "); ++ } ++ ++ if (success) { ++ gboolean pending = FALSE; ++ g_autoptr(GVariant) transaction_status = NULL; ++ ++ if (gs_dnf5_offline_call_get_status_sync (offline_proxy, &pending, &transaction_status, cancellable, &local_error) && ++ pending && transaction_status != NULL) { ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autofree gchar *op_error_msg = NULL; ++ gboolean op_success = FALSE; ++ ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "interactive", ++ g_variant_new_boolean ((data->flags & GS_PLUGIN_CANCEL_OFFLINE_UPDATE_FLAGS_INTERACTIVE) != 0)); ++ ++ success = gs_dnf5_offline_call_cancel_with_options_sync (offline_proxy, g_variant_builder_end (options_builder), ++ &op_success, &op_error_msg, cancellable, &local_error); ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call OfflineProxy::cancel: "); ++ } else if (!op_success) { ++ success = FALSE; ++ if (op_error_msg != NULL) { ++ g_set_error_literal (&local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg); ++ g_prefix_error_literal (&local_error, "Failed to call OfflineProxy::cancel op: "); ++ } else { ++ g_set_error_literal (&local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, "Failed to call OfflineProxy::cancel op"); ++ } ++ } ++ } ++ } ++ ++ gs_dnf5_close_session (self, session_manager, session_path); ++ ++ if (success) { ++ g_task_return_boolean (task, TRUE); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_task_return_error (task, g_steal_pointer (&local_error)); ++ } ++} ++ ++static void ++gs_plugin_dnf5_cancel_offline_update_async (GsPlugin *plugin, ++ GsPluginCancelOfflineUpdateFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); ++ g_autoptr(GTask) task = NULL; ++ gboolean interactive = (flags & GS_PLUGIN_CANCEL_OFFLINE_UPDATE_FLAGS_INTERACTIVE) != 0; ++ ++ task = gs_plugin_cancel_offline_update_data_new_task (plugin, flags, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gs_plugin_dnf5_cancel_offline_update_async); ++ ++ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), ++ gs_dnf5_cancel_offline_update_thread_cb, g_steal_pointer (&task)); ++} ++ ++static gboolean ++gs_plugin_dnf5_cancel_offline_update_finish (GsPlugin *plugin, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ +static void +gs_plugin_dnf5_dispose (GObject *object) +{ @@ -4865,6 +5168,8 @@ index 000000000..36d10163a + plugin_class->download_upgrade_finish = gs_plugin_dnf5_download_upgrade_finish; + plugin_class->trigger_upgrade_async = gs_plugin_dnf5_trigger_upgrade_async; + plugin_class->trigger_upgrade_finish = gs_plugin_dnf5_trigger_upgrade_finish; ++ plugin_class->cancel_offline_update_async = gs_plugin_dnf5_cancel_offline_update_async; ++ plugin_class->cancel_offline_update_finish = gs_plugin_dnf5_cancel_offline_update_finish; +} + +static void diff --git a/gnome-software.spec b/gnome-software.spec index 21466da..2cce046 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -30,7 +30,7 @@ Name: gnome-software Version: 49~beta -Release: 4%{?dist} +Release: 5%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later From e95633c7038667767acefda20b67d7f50d928c98 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 26 Aug 2025 19:02:10 +0200 Subject: [PATCH 35/44] dnf5-plugin: Skip historical updates search when no last date is set --- 0001-dnf5-plugin.patch | 97 ++++++++++++++++++++++-------------------- gnome-software.spec | 2 +- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index 088ad51..43864c1 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,5 +1,5 @@ -at commit 55ca6d4d49d5edd682b8ca15904ec556eb5d0874 -Date: Tue Aug 19 13:13:48 2025 +0200 +at commit 73640bc9127fb2200189433f935445208bf0b472 +Date: Tue Aug 26 18:59:16 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt index cd49cf529..27fa15e8b 100644 @@ -610,10 +610,10 @@ index 000000000..5d522468c +G_END_DECLS diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c new file mode 100644 -index 000000000..c8d9ed83d +index 000000000..b9b698a81 --- /dev/null +++ b/plugins/dnf5/gs-plugin-dnf5.c -@@ -0,0 +1,4582 @@ +@@ -0,0 +1,4589 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * @@ -2767,55 +2767,62 @@ index 000000000..c8d9ed83d + g_prefix_error_literal (&local_error, "Failed to call Rpm::list: "); + } + } else if (is_historical_update == GS_APP_QUERY_TRISTATE_TRUE) { -+ g_autoptr(GsDnf5History) history_proxy = NULL; ++ g_autoptr(GSettings) settings = NULL; ++ gint64 timestamp; ++ gboolean interactive = (data->flags & GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE) != 0; + -+ history_proxy = gs_dnf5_history_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = history_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create History proxy: "); -+ } ++ /* use the option the GUI part uses */ ++ settings = g_settings_new ("org.gnome.software"); ++ timestamp = g_settings_get_int64 (settings, "install-timestamp"); + -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ g_autoptr(GSettings) settings; -+ gint64 timestamp; ++ if (timestamp > 0 || interactive) { ++ g_autoptr(GsDnf5History) history_proxy = NULL; + -+ /* use the option the GUI part uses */ -+ settings = g_settings_new ("org.gnome.software"); -+ timestamp = g_settings_get_int64 (settings, "install-timestamp"); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "since", -+ g_variant_new_int64 (timestamp)); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ gs_dnf5_dup_package_attrs ()); -+ -+ success = gs_dnf5_history_call_recent_changes_sync (history_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ &local_error); ++ history_proxy = gs_dnf5_history_proxy_new_sync (self->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, ++ GS_DNF5_INTERFACE_RPM_DNF, ++ session_path, ++ cancellable, ++ &local_error); ++ success = history_proxy != NULL; ++ if (!success) { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to create History proxy: "); ++ } + + if (success) { -+ ReadPackageData rpd = { 0, }; ++ g_autoptr(GVariantBuilder) options_builder = NULL; ++ g_autoptr(GVariant) result = NULL; + -+ rpd.list = list; -+ rpd.set_state = GS_APP_STATE_UNKNOWN; -+ rpd.nevra_to_app = NULL; -+ rpd.timestamp = timestamp; ++ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); ++ g_variant_builder_add (options_builder, "{sv}", "since", ++ g_variant_new_int64 (timestamp)); ++ g_variant_builder_add (options_builder, "{sv}", "package_attrs", ++ gs_dnf5_dup_package_attrs ()); + -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_history_items_cb, &rpd, cancellable, &local_error); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call recent_changes: "); ++ success = gs_dnf5_history_call_recent_changes_sync (history_proxy, ++ g_variant_builder_end (options_builder), ++ &result, ++ cancellable, ++ &local_error); ++ ++ if (success) { ++ ReadPackageData rpd = { 0, }; ++ ++ rpd.list = list; ++ rpd.set_state = GS_APP_STATE_UNKNOWN; ++ rpd.nevra_to_app = NULL; ++ rpd.timestamp = timestamp; ++ ++ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_history_items_cb, &rpd, cancellable, &local_error); ++ } else { ++ gs_dnf5_convert_error (&local_error); ++ g_prefix_error_literal (&local_error, "Failed to call recent_changes: "); ++ } + } ++ } else { ++ /* initialize the value to today */ ++ g_settings_set (settings, "install-timestamp", "x", g_get_real_time () / G_USEC_PER_SEC); + } + } else if (gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) { + g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; diff --git a/gnome-software.spec b/gnome-software.spec index 2cce046..ad7833b 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -30,7 +30,7 @@ Name: gnome-software Version: 49~beta -Release: 5%{?dist} +Release: 6%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later From 4a71393113c82fe2afd330153fd30851504aa0da Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 29 Aug 2025 09:11:51 +0200 Subject: [PATCH 36/44] Update to 49.rc --- gnome-software.spec | 4 ++-- sources | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index ad7833b..c6f4ac8 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,8 +29,8 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 49~beta -Release: 6%{?dist} +Version: 49~rc +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later diff --git a/sources b/sources index f212fbd..c3ec757 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-49.beta.tar.xz) = 9cb6d67216193840b2661871ef43da5e726a284d1164781f13423ee8dd86359334f199efc29f4b3a0b3eddfa8a3811d7dd38cd412fa53aaebb2e1c35ddc1f9df +SHA512 (gnome-software-49.rc.tar.xz) = cecebbff1163f0179c5699ecc8325e2c4f68c811ec7633e6cf32e18ad2f03592b55daa16b9b5497a117cbfbf63f3d9162dd8e8191e6a6213d35261fa1bad15a5 From 90da5737b3044a752d2f4d7ebc5513123b46113d Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 1 Sep 2025 18:28:21 +0200 Subject: [PATCH 37/44] Resolves: #2392057 (dnf5-pugin: No update notifications shown) --- 0001-dnf5-plugin.patch | 18 +++++++++--------- gnome-software.spec | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch index 43864c1..3b4b355 100644 --- a/0001-dnf5-plugin.patch +++ b/0001-dnf5-plugin.patch @@ -1,5 +1,5 @@ -at commit 73640bc9127fb2200189433f935445208bf0b472 -Date: Tue Aug 26 18:59:16 2025 +0200 +at commit 786bab5f1928920251ee9e93d51953ff9dd9faa4 +Date: Mon Sep 1 18:23:09 2025 +0200 diff --git a/meson_options.txt b/meson_options.txt index cd49cf529..27fa15e8b 100644 @@ -610,7 +610,7 @@ index 000000000..5d522468c +G_END_DECLS diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c new file mode 100644 -index 000000000..b9b698a81 +index 000000000..82f3e19a8 --- /dev/null +++ b/plugins/dnf5/gs-plugin-dnf5.c @@ -0,0 +1,4589 @@ @@ -2769,13 +2769,12 @@ index 000000000..b9b698a81 + } else if (is_historical_update == GS_APP_QUERY_TRISTATE_TRUE) { + g_autoptr(GSettings) settings = NULL; + gint64 timestamp; -+ gboolean interactive = (data->flags & GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE) != 0; + + /* use the option the GUI part uses */ + settings = g_settings_new ("org.gnome.software"); -+ timestamp = g_settings_get_int64 (settings, "install-timestamp"); ++ timestamp = (gint64) g_settings_get_uint64 (settings, "packagekit-historical-updates-timestamp"); + -+ if (timestamp > 0 || interactive) { ++ if (timestamp > 0) { + g_autoptr(GsDnf5History) history_proxy = NULL; + + history_proxy = gs_dnf5_history_proxy_new_sync (self->connection, @@ -2820,9 +2819,6 @@ index 000000000..b9b698a81 + g_prefix_error_literal (&local_error, "Failed to call recent_changes: "); + } + } -+ } else { -+ /* initialize the value to today */ -+ g_settings_set (settings, "install-timestamp", "x", g_get_real_time () / G_USEC_PER_SEC); + } + } else if (gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) { + g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; @@ -3973,6 +3969,10 @@ index 000000000..b9b698a81 + } else if (!success) { + gs_dnf5_convert_error (error); + g_prefix_error_literal (error, "Failed to set finish action: "); ++ } else { ++ g_autoptr(GSettings) settings = g_settings_new ("org.gnome.software"); ++ /* to check anything installed/updated after today */ ++ g_settings_set (settings, "packagekit-historical-updates-timestamp", "t", g_get_real_time () / G_USEC_PER_SEC); + } + } else { + gs_dnf5_convert_error (error); diff --git a/gnome-software.spec b/gnome-software.spec index c6f4ac8..09c8546 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -30,7 +30,7 @@ Name: gnome-software Version: 49~rc -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later From ac8dbc92e832cb6c72963e90cd49f7d92a6b05ee Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 12 Sep 2025 10:01:34 +0200 Subject: [PATCH 38/44] Update to 49.0 --- gnome-software.spec | 8 ++++---- sources | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 09c8546..34328cf 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,17 +29,19 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 49~rc -Release: 2%{?dist} +Version: 49.0 +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later URL: https://apps.gnome.org/Software Source0: https://download.gnome.org/sources/gnome-software/49/%{name}-%{tarball_version}.tar.xz +%if %{with dnf5} # to update the patch enter the ./dnf5-plugin/ directory and run from # it the ./update-patch.sh script Patch: 0001-dnf5-plugin.patch +%endif # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 @@ -177,8 +179,6 @@ This package includes the rpm-ostree backend. %endif %if %{with dnf5} -Ddnf5=true \ -%else - -Ddnf5=false \ %endif -Dexternal_appstream=false \ %if %{with rpmostree} diff --git a/sources b/sources index c3ec757..469fb06 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-49.rc.tar.xz) = cecebbff1163f0179c5699ecc8325e2c4f68c811ec7633e6cf32e18ad2f03592b55daa16b9b5497a117cbfbf63f3d9162dd8e8191e6a6213d35261fa1bad15a5 +SHA512 (gnome-software-49.0.tar.xz) = 7c63b0bd59e6ebe7ef526b438c6d0b234897edacfa72ab4846358ffa9e3d63ced2945f52a4d3111307e349b846be80cb4039b4cec25f7bc5ceba135d0a4fe343 From 2c29600426d9ef7d1ef04b90d8b3af73ba1c07c1 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 19 Sep 2025 13:25:37 +0200 Subject: [PATCH 39/44] Resolves: #2395811 (Packages not found for "what-provides" searches) --- ...ackages-not-found-for-what-provides-.patch | 28 +++++++++++++++++++ gnome-software.spec | 4 ++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 0002-gs-extras-page-Packages-not-found-for-what-provides-.patch diff --git a/0002-gs-extras-page-Packages-not-found-for-what-provides-.patch b/0002-gs-extras-page-Packages-not-found-for-what-provides-.patch new file mode 100644 index 0000000..5d14caf --- /dev/null +++ b/0002-gs-extras-page-Packages-not-found-for-what-provides-.patch @@ -0,0 +1,28 @@ +From 272cdf10b69a84f773ce203a4d1fa9e7be9cd225 Mon Sep 17 00:00:00 2001 +Date: Fri, 19 Sep 2025 12:52:43 +0200 +Subject: [PATCH] gs-extras-page: Packages not found for "what-provides" + searches + +This is a regression after commit 22926c20b3f062082f689379b0d7b2946c35c958, +accidentally removed "allow-packages" flag for the app listing for one branch. + +Reported downstream at: https://bugzilla.redhat.com/show_bug.cgi?id=2395811 +--- + src/gs-extras-page.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/gs-extras-page.c b/src/gs-extras-page.c +index 9d16c0eb9..0d8d786c1 100644 +--- a/src/gs-extras-page.c ++++ b/src/gs-extras-page.c +@@ -853,6 +853,7 @@ gs_extras_page_load (GsExtrasPage *self, GPtrArray *array_search_data) + + query = gs_app_query_new ("provides-tag", search_data->search, + "provides-type", search_data->search_provides_type, ++ "refine-flags", GS_PLUGIN_REFINE_FLAGS_ALLOW_PACKAGES, + "refine-require-flags", require_flags, + "license-type", gs_page_get_query_license_type (GS_PAGE (self)), + "developer-verified-type", gs_page_get_query_developer_verified_type (GS_PAGE (self)), +-- +2.49.0 + diff --git a/gnome-software.spec b/gnome-software.spec index 34328cf..91e0bd4 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -30,7 +30,7 @@ Name: gnome-software Version: 49.0 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -43,6 +43,8 @@ Source0: https://download.gnome.org/sources/gnome-software/49/%{name}-%{tarbal Patch: 0001-dnf5-plugin.patch %endif +Patch: 0002-gs-extras-page-Packages-not-found-for-what-provides-.patch + # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 %if 0%{?fedora} || 0%{?rhel} >= 10 From 3364ed6d8c4f5b3536689bb260aac812ad3c6134 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 10 Oct 2025 08:47:16 +0200 Subject: [PATCH 40/44] Update to 49.1 --- ...ackages-not-found-for-what-provides-.patch | 28 ------------------- gnome-software.spec | 8 ++---- sources | 2 +- 3 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 0002-gs-extras-page-Packages-not-found-for-what-provides-.patch diff --git a/0002-gs-extras-page-Packages-not-found-for-what-provides-.patch b/0002-gs-extras-page-Packages-not-found-for-what-provides-.patch deleted file mode 100644 index 5d14caf..0000000 --- a/0002-gs-extras-page-Packages-not-found-for-what-provides-.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 272cdf10b69a84f773ce203a4d1fa9e7be9cd225 Mon Sep 17 00:00:00 2001 -Date: Fri, 19 Sep 2025 12:52:43 +0200 -Subject: [PATCH] gs-extras-page: Packages not found for "what-provides" - searches - -This is a regression after commit 22926c20b3f062082f689379b0d7b2946c35c958, -accidentally removed "allow-packages" flag for the app listing for one branch. - -Reported downstream at: https://bugzilla.redhat.com/show_bug.cgi?id=2395811 ---- - src/gs-extras-page.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/gs-extras-page.c b/src/gs-extras-page.c -index 9d16c0eb9..0d8d786c1 100644 ---- a/src/gs-extras-page.c -+++ b/src/gs-extras-page.c -@@ -853,6 +853,7 @@ gs_extras_page_load (GsExtrasPage *self, GPtrArray *array_search_data) - - query = gs_app_query_new ("provides-tag", search_data->search, - "provides-type", search_data->search_provides_type, -+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_ALLOW_PACKAGES, - "refine-require-flags", require_flags, - "license-type", gs_page_get_query_license_type (GS_PAGE (self)), - "developer-verified-type", gs_page_get_query_developer_verified_type (GS_PAGE (self)), --- -2.49.0 - diff --git a/gnome-software.spec b/gnome-software.spec index 91e0bd4..e62923e 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,8 +29,8 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 49.0 -Release: 2%{?dist} +Version: 49.1 +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -43,8 +43,6 @@ Source0: https://download.gnome.org/sources/gnome-software/49/%{name}-%{tarbal Patch: 0001-dnf5-plugin.patch %endif -Patch: 0002-gs-extras-page-Packages-not-found-for-what-provides-.patch - # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 %if 0%{?fedora} || 0%{?rhel} >= 10 @@ -162,7 +160,7 @@ This package includes the rpm-ostree backend. %endif %prep -%autosetup -p1 -S git -n %{name}-%{tarball_version} +%autosetup -p1 -S gendiff -n %{name}-%{tarball_version} %build %meson \ diff --git a/sources b/sources index 469fb06..f738461 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-49.0.tar.xz) = 7c63b0bd59e6ebe7ef526b438c6d0b234897edacfa72ab4846358ffa9e3d63ced2945f52a4d3111307e349b846be80cb4039b4cec25f7bc5ceba135d0a4fe343 +SHA512 (gnome-software-49.1.tar.xz) = 3475ea7d7062679192d4317a6ae278601acd5316d20eb6db9512407058e3b39bf6804443a272016d206cd97d12c8c9fdd883db96297e6b5866d80300ff48503a From 93f854207d7f0b44f2019701a3c1a2b854319f8f Mon Sep 17 00:00:00 2001 From: Yanko Kaneti Date: Fri, 10 Oct 2025 10:04:05 +0300 Subject: [PATCH 41/44] Add NEWS to docs --- gnome-software.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnome-software.spec b/gnome-software.spec index e62923e..78d502f 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -232,7 +232,7 @@ FOE desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %files -f %{name}.lang -%doc AUTHORS README.md +%doc AUTHORS NEWS README.md %license COPYING %{_bindir}/gnome-software %{_datadir}/applications/gnome-software-local-file-flatpak.desktop From 2a31982d3e20fec1a199d87dbb4013d39de293a9 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 21 Nov 2025 11:10:15 +0100 Subject: [PATCH 42/44] Update to 49.2 --- gnome-software.spec | 2 +- sources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gnome-software.spec b/gnome-software.spec index 78d502f..3552763 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,7 +29,7 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 49.1 +Version: 49.2 Release: 1%{?dist} Summary: A software center for GNOME diff --git a/sources b/sources index f738461..0e06aac 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-49.1.tar.xz) = 3475ea7d7062679192d4317a6ae278601acd5316d20eb6db9512407058e3b39bf6804443a272016d206cd97d12c8c9fdd883db96297e6b5866d80300ff48503a +SHA512 (gnome-software-49.2.tar.xz) = 9ce33e2046b776f2a56c96d17eec85817e04bada9af9590125530c84bcc529f278bcba0c0ccdabcb459f92b326e0837cd63a9e316551ec313ac2f8720bf69835 From 095b81906e30c01f354627060e7229af28eafb36 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 24 Nov 2025 16:20:33 +0100 Subject: [PATCH 43/44] Add patch for bug #2416542 (Crash under gs_flatpak_refine_wildcard()) --- ...ash-under-gs_flatpak_refine_wildcard.patch | 155 ++++++++++++++++++ gnome-software.spec | 5 +- 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch diff --git a/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch b/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch new file mode 100644 index 0000000..0150264 --- /dev/null +++ b/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch @@ -0,0 +1,155 @@ +diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c +index b409475c7..a40ab54fb 100644 +--- a/plugins/flatpak/gs-flatpak.c ++++ b/plugins/flatpak/gs-flatpak.c +@@ -3748,16 +3748,19 @@ gboolean + gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + GsAppList *list, GsPluginRefineRequireFlags require_flags, + gboolean interactive, ++ XbSilo **inout_silo, ++ gchar **inout_silo_filename, ++ GHashTable **inout_installed_by_desktopid, + GHashTable **inout_components_by_id, + GHashTable **inout_components_by_bundle, + GCancellable *cancellable, GError **error) + { + const gchar *id; + GPtrArray* components = NULL; ++ XbSilo *silo; ++ GHashTable *silo_installed_by_desktopid; ++ const gchar *silo_filename; + g_autoptr(GError) error_local = NULL; +- g_autoptr(XbSilo) silo = NULL; +- g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; +- g_autofree gchar *silo_filename = NULL; + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); + +@@ -3766,10 +3769,15 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + if (id == NULL) + return TRUE; + +- silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); +- if (silo == NULL) ++ if (*inout_silo == NULL) ++ *inout_silo = gs_flatpak_ref_silo (self, interactive, inout_silo_filename, inout_installed_by_desktopid, cancellable, error); ++ if (*inout_silo == NULL) + return FALSE; + ++ silo = *inout_silo; ++ silo_filename = *inout_silo_filename; ++ silo_installed_by_desktopid = *inout_installed_by_desktopid; ++ + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardQuerySilo, "Flatpak (query silo)", NULL); + + if (*inout_components_by_id != NULL) { +diff --git a/plugins/flatpak/gs-flatpak.h b/plugins/flatpak/gs-flatpak.h +index 66ff59967..34e1416b5 100644 +--- a/plugins/flatpak/gs-flatpak.h ++++ b/plugins/flatpak/gs-flatpak.h +@@ -11,6 +11,7 @@ + + #include + #include ++#include + + G_BEGIN_DECLS + +@@ -108,6 +109,9 @@ gboolean gs_flatpak_refine_wildcard (GsFlatpak *self, + GsAppList *list, + GsPluginRefineRequireFlags require_flags, + gboolean interactive, ++ XbSilo **inout_silo, ++ gchar **inout_silo_filename, ++ GHashTable **inout_installed_by_desktopid, + GHashTable **inout_components_by_id, + GHashTable **inout_components_by_bundle, + GCancellable *cancellable, +diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c +index 3d0503d46..f5d4a1d67 100644 +--- a/plugins/flatpak/gs-plugin-flatpak.c ++++ b/plugins/flatpak/gs-plugin-flatpak.c +@@ -667,12 +667,29 @@ gs_plugin_flatpak_refine_app (GsPluginFlatpak *self, + return gs_flatpak_refine_app (flatpak, app, require_flags, interactive, FALSE, event_callback, event_user_data, cancellable, error); + } + ++typedef struct { ++ XbSilo *silo; ++ gchar *silo_filename; ++ GHashTable *installed_by_desktopid; ++ GHashTable *components_by_id; ++ GHashTable *components_by_bundle; ++} RefineInstallationData; ++ + static void +-unref_nonnull_hash_table (gpointer ptr) ++refine_installation_data_free (gpointer ptr) + { +- GHashTable *hash_table = ptr; +- if (hash_table != NULL) +- g_hash_table_unref (hash_table); ++ RefineInstallationData *data = ptr; ++ ++ if (data == NULL) ++ return; ++ ++ g_clear_pointer (&data->silo_filename, g_free); ++ g_clear_pointer (&data->installed_by_desktopid, g_hash_table_unref); ++ g_clear_pointer (&data->components_by_id, g_hash_table_unref); ++ g_clear_pointer (&data->components_by_bundle, g_hash_table_unref); ++ /* free the silo as the last, just in case, because the data from it is in the above hash tables */ ++ g_clear_object (&data->silo); ++ g_free (data); + } + + static gboolean +@@ -761,8 +778,7 @@ refine_thread_cb (GTask *task, + gboolean interactive = (data->job_flags & GS_PLUGIN_REFINE_FLAGS_INTERACTIVE) != 0; + GsPluginEventCallback event_callback = data->event_callback; + void *event_user_data = data->event_user_data; +- g_autoptr(GPtrArray) array_components_by_id = NULL; /* (element-type GHashTable) */ +- g_autoptr(GPtrArray) array_components_by_bundle = NULL; /* (element-type GHashTable) */ ++ g_autoptr(GPtrArray) installation_data = NULL; /* (element-type RefineInstallationData) */ + g_autoptr(GsAppList) app_list = NULL; + g_autoptr(GError) local_error = NULL; + +@@ -783,10 +799,9 @@ refine_thread_cb (GTask *task, + * (e.g. inserting an app in the list on every call results in + * an infinite loop) */ + app_list = gs_app_list_copy (list); +- array_components_by_id = g_ptr_array_new_full (self->installations->len, unref_nonnull_hash_table); +- g_ptr_array_set_size (array_components_by_id, self->installations->len); +- array_components_by_bundle = g_ptr_array_new_full (self->installations->len, unref_nonnull_hash_table); +- g_ptr_array_set_size (array_components_by_bundle, self->installations->len); ++ ++ installation_data = g_ptr_array_new_full (self->installations->len, refine_installation_data_free); ++ g_ptr_array_set_size (installation_data, self->installations->len); + + for (guint j = 0; j < gs_app_list_length (app_list); j++) { + GsApp *app = gs_app_list_index (app_list, j); +@@ -796,16 +811,20 @@ refine_thread_cb (GTask *task, + + for (guint i = 0; i < self->installations->len; i++) { + GsFlatpak *flatpak = g_ptr_array_index (self->installations, i); +- GHashTable *components_by_id = array_components_by_id->pdata[i]; +- GHashTable *components_by_bundle = array_components_by_bundle->pdata[i]; ++ RefineInstallationData *inst_data = g_ptr_array_index (installation_data, i); ++ ++ if (inst_data == NULL) { ++ inst_data = g_new0 (RefineInstallationData, 1); ++ installation_data->pdata[i] = inst_data; ++ } + +- if (!gs_flatpak_refine_wildcard (flatpak, app, list, require_flags, interactive, &components_by_id, &components_by_bundle, ++ if (!gs_flatpak_refine_wildcard (flatpak, app, list, require_flags, interactive, &inst_data->silo, ++ &inst_data->silo_filename, &inst_data->installed_by_desktopid, ++ &inst_data->components_by_id, &inst_data->components_by_bundle, + cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +- array_components_by_id->pdata[i] = components_by_id; +- array_components_by_bundle->pdata[i] = components_by_bundle; + } + } + diff --git a/gnome-software.spec b/gnome-software.spec index 3552763..6830ae0 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -30,7 +30,7 @@ Name: gnome-software Version: 49.2 -Release: 1%{?dist} +Release: 2%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later @@ -43,6 +43,9 @@ Source0: https://download.gnome.org/sources/gnome-software/49/%{name}-%{tarbal Patch: 0001-dnf5-plugin.patch %endif +# https://bugzilla.redhat.com/show_bug.cgi?id=2416542 +Patch: 0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch + # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 %if 0%{?fedora} || 0%{?rhel} >= 10 From a10b3cf10fff31405ee2087b87c1a7d8d139fd29 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 12 Jan 2026 17:23:11 +0100 Subject: [PATCH 44/44] Update to 49.3 --- ...ash-under-gs_flatpak_refine_wildcard.patch | 531 ++++++++++++++++++ gnome-software.spec | 4 +- sources | 2 +- 3 files changed, 534 insertions(+), 3 deletions(-) diff --git a/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch b/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch index 0150264..33140df 100644 --- a/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch +++ b/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch @@ -1,3 +1,26 @@ +From 0c79d9fb08a348a45a18f15427c945a63b44982e Mon Sep 17 00:00:00 2001 +Date: Mon, 24 Nov 2025 16:06:51 +0100 +Subject: [PATCH 1/3] flatpak: Fix XbSilo lifetime in + gs_flatpak_refine_wildcard() + +The function uses pre-cached data in the hash tables, consisting of XbNode-s +from an XbSilo. The nodes are referenced, but the silos the node belongs to +is not referenced (by the XbNode). + +This change keeps the XbSilo alive as long as it's needed for the nodes +in the pre-cached data. It also does not call gs_flatpak_ref_silo() multiple +times for the same GsFlatpak instance, which could invalidate the XbNode-s +in the pre-cached data when a change in the silo or in the installation +had been received while the function was still processing the apps. + +Related downstream bug https://bugzilla.redhat.com/show_bug.cgi?id=2416542 +Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/2900 +--- + plugins/flatpak/gs-flatpak.c | 18 ++++++++--- + plugins/flatpak/gs-flatpak.h | 4 +++ + plugins/flatpak/gs-plugin-flatpak.c | 49 ++++++++++++++++++++--------- + 3 files changed, 51 insertions(+), 20 deletions(-) + diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c index b409475c7..a40ab54fb 100644 --- a/plugins/flatpak/gs-flatpak.c @@ -153,3 +176,511 @@ index 3d0503d46..f5d4a1d67 100644 } } +-- +GitLab + + +From 546c6ee17b5959d2a696a6cb346a1f8b94aba303 Mon Sep 17 00:00:00 2001 +Date: Wed, 7 Jan 2026 18:37:52 +0100 +Subject: [PATCH 2/3] flatpak: Simplify wildcard app lookup function + +By flipping the order of traversing app and installation the code +can be simplified to not have too many in/out arguments. + +Suggested by Philip Withnall +--- + plugins/flatpak/gs-flatpak.c | 251 ++++++++++++++-------------- + plugins/flatpak/gs-flatpak.h | 9 +- + plugins/flatpak/gs-plugin-flatpak.c | 61 +------ + 3 files changed, 130 insertions(+), 191 deletions(-) + +diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c +index a40ab54fb..782b0bb48 100644 +--- a/plugins/flatpak/gs-flatpak.c ++++ b/plugins/flatpak/gs-flatpak.c +@@ -3745,167 +3745,158 @@ gs_flatpak_refine_app (GsFlatpak *self, + } + + gboolean +-gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, +- GsAppList *list, GsPluginRefineRequireFlags require_flags, +- gboolean interactive, +- XbSilo **inout_silo, +- gchar **inout_silo_filename, +- GHashTable **inout_installed_by_desktopid, +- GHashTable **inout_components_by_id, +- GHashTable **inout_components_by_bundle, +- GCancellable *cancellable, GError **error) ++gs_flatpak_refine_wildcards (GsFlatpak *self, ++ GPtrArray *wildcard_apps, ++ GsAppList *list, ++ GsPluginRefineRequireFlags require_flags, ++ gboolean interactive, ++ GCancellable *cancellable, ++ GError **error) + { +- const gchar *id; +- GPtrArray* components = NULL; +- XbSilo *silo; +- GHashTable *silo_installed_by_desktopid; +- const gchar *silo_filename; + g_autoptr(GError) error_local = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autoptr(GHashTable) components_by_id = NULL; ++ g_autoptr(GHashTable) components_by_bundle = NULL; ++ g_autoptr(GPtrArray) components_with_id = NULL; ++ g_autoptr(GPtrArray) bundles = NULL; ++ g_autofree gchar *silo_filename = NULL; + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); + +- /* not enough info to find */ +- id = gs_app_get_id (app); +- if (id == NULL) +- return TRUE; +- +- if (*inout_silo == NULL) +- *inout_silo = gs_flatpak_ref_silo (self, interactive, inout_silo_filename, inout_installed_by_desktopid, cancellable, error); +- if (*inout_silo == NULL) ++ silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); ++ if (silo == NULL) + return FALSE; + +- silo = *inout_silo; +- silo_filename = *inout_silo_filename; +- silo_installed_by_desktopid = *inout_installed_by_desktopid; +- + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardQuerySilo, "Flatpak (query silo)", NULL); + +- if (*inout_components_by_id != NULL) { +- components = g_hash_table_lookup (*inout_components_by_id, gs_app_get_id (app)); +- } else { +- g_autoptr(GPtrArray) components_with_id = NULL; +- *inout_components_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); +- components_with_id = xb_silo_query (silo, "components/component/id", 0, &error_local); +- if (components_with_id == NULL) { +- if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) +- return TRUE; +- g_propagate_error (error, g_steal_pointer (&error_local)); +- return FALSE; ++ components_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); ++ components_with_id = xb_silo_query (silo, "components/component/id", 0, &error_local); ++ if (components_with_id == NULL) { ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) ++ return TRUE; ++ g_propagate_error (error, g_steal_pointer (&error_local)); ++ return FALSE; ++ } ++ ++ for (guint i = 0; i < components_with_id->len; i++) { ++ XbNode *node = g_ptr_array_index (components_with_id, i); ++ XbNode *comp_node = xb_node_get_parent (node); ++ const gchar *comp_id = xb_node_get_text (node); ++ GPtrArray *comps = g_hash_table_lookup (components_by_id, comp_id); ++ if (comps == NULL) { ++ comps = g_ptr_array_new_with_free_func (g_object_unref); ++ g_hash_table_insert (components_by_id, g_strdup (comp_id), comps); + } +- for (guint i = 0; i < components_with_id->len; i++) { +- XbNode *node = g_ptr_array_index (components_with_id, i); +- XbNode *comp_node = xb_node_get_parent (node); +- const gchar *comp_id = xb_node_get_text (node); +- GPtrArray *comps = g_hash_table_lookup (*inout_components_by_id, comp_id); +- if (comps == NULL) { +- comps = g_ptr_array_new_with_free_func (g_object_unref); +- g_hash_table_insert (*inout_components_by_id, g_strdup (comp_id), comps); ++ g_ptr_array_add (comps, comp_node); ++ } ++ ++ components_by_bundle = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); ++ bundles = xb_silo_query (silo, "/components/component/bundle[@type='flatpak']", 0, NULL); ++ for (guint b = 0; bundles != NULL && b < bundles->len; b++) { ++ XbNode *bundle_node = g_ptr_array_index (bundles, b); ++ g_autoptr(XbNode) component_node = xb_node_get_parent (bundle_node); ++ g_autoptr(XbNode) components_node = xb_node_get_parent (component_node); ++ const gchar *origin = xb_node_get_attr (components_node, "origin"); ++ if (origin != NULL) { ++ const gchar *bundle = xb_node_get_text (bundle_node); ++ if (bundle != NULL) { ++ g_autofree gchar *key = g_strconcat (origin, "\n", bundle, NULL); ++ g_hash_table_insert (components_by_bundle, g_steal_pointer (&key), g_steal_pointer (&component_node)); + } +- g_ptr_array_add (comps, comp_node); +- if (components == NULL && g_strcmp0 (id, comp_id) == 0) +- components = comps; + } + } + + GS_PROFILER_END_SCOPED (FlatpakRefineWildcardQuerySilo); + +- if (components == NULL) +- return TRUE; +- + gs_flatpak_ensure_remote_title (self, interactive, cancellable); + +- if (*inout_components_by_bundle == NULL) { +- g_autoptr(GPtrArray) bundles = NULL; +- +- *inout_components_by_bundle = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); +- bundles = xb_silo_query (silo, "/components/component/bundle[@type='flatpak']", 0, NULL); +- for (guint b = 0; bundles != NULL && b < bundles->len; b++) { +- XbNode *bundle_node = g_ptr_array_index (bundles, b); +- g_autoptr(XbNode) component_node = xb_node_get_parent (bundle_node); +- g_autoptr(XbNode) components_node = xb_node_get_parent (component_node); +- const gchar *origin = xb_node_get_attr (components_node, "origin"); +- if (origin != NULL) { +- const gchar *bundle = xb_node_get_text (bundle_node); +- if (bundle != NULL) { +- g_autofree gchar *key = g_strconcat (origin, "\n", bundle, NULL); +- g_hash_table_insert (*inout_components_by_bundle, g_steal_pointer (&key), g_steal_pointer (&component_node)); +- } +- } +- } +- } ++ for (guint j = 0; j < wildcard_apps->len; j++) { ++ GsApp *app = g_ptr_array_index (wildcard_apps, j); ++ GPtrArray *components = NULL; ++ const gchar *id; + ++ /* not enough info to find */ ++ id = gs_app_get_id (app); ++ if (id == NULL) ++ continue; + +- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardGenerateApps, "Flatpak (create app)", NULL); +- for (guint i = 0; i < components->len; i++) { +- XbNode *component = g_ptr_array_index (components, i); +- g_autoptr(GsApp) new = NULL; ++ components = g_hash_table_lookup (components_by_id, id); ++ if (components == NULL) ++ continue; + +- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardCreateAppstreamApp, "Flatpak (create Appstream app)", NULL); +- new = gs_appstream_create_app (self->plugin, silo, component, silo_filename ? silo_filename : "", +- self->scope, error); +- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardCreateAppstreamApp); ++ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardGenerateApps, "Flatpak (create app)", NULL); ++ for (guint i = 0; i < components->len; i++) { ++ XbNode *component = g_ptr_array_index (components, i); ++ g_autoptr(GsApp) new = NULL; + +- if (new == NULL) +- return FALSE; ++ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardCreateAppstreamApp, "Flatpak (create Appstream app)", NULL); ++ new = gs_appstream_create_app (self->plugin, silo, component, silo_filename ? silo_filename : "", ++ self->scope, error); ++ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardCreateAppstreamApp); + +- gs_flatpak_claim_app (self, new); +- +- /* The appstream plugin did not find the component in the plugin's cache, +- thus read the required info from the 'bundle' element. */ +- if (gs_flatpak_app_get_ref_name (new) == NULL || +- gs_flatpak_app_get_ref_arch (new) == NULL) { +- const gchar *xref_str = NULL; +- g_autoptr(XbNode) child = NULL; +- g_autoptr(XbNode) next = NULL; +- for (child = xb_node_get_child (component); child != NULL && xref_str == NULL; +- g_object_unref (child), child = g_steal_pointer (&next)) { +- next = xb_node_get_next (child); +- if (g_strcmp0 (xb_node_get_element (child), "bundle") == 0 && +- g_strcmp0 (xb_node_get_attr (child, "type"), "flatpak") == 0) { +- xref_str = xb_node_get_text (child); +- break; ++ if (new == NULL) ++ return FALSE; ++ ++ gs_flatpak_claim_app (self, new); ++ ++ /* The appstream plugin did not find the component in the plugin's cache, ++ thus read the required info from the 'bundle' element. */ ++ if (gs_flatpak_app_get_ref_name (new) == NULL || ++ gs_flatpak_app_get_ref_arch (new) == NULL) { ++ const gchar *xref_str = NULL; ++ g_autoptr(XbNode) child = NULL; ++ g_autoptr(XbNode) next = NULL; ++ for (child = xb_node_get_child (component); child != NULL && xref_str == NULL; ++ g_object_unref (child), child = g_steal_pointer (&next)) { ++ next = xb_node_get_next (child); ++ if (g_strcmp0 (xb_node_get_element (child), "bundle") == 0 && ++ g_strcmp0 (xb_node_get_attr (child, "type"), "flatpak") == 0) { ++ xref_str = xb_node_get_text (child); ++ break; ++ } + } +- } +- if (xref_str != NULL) { +- g_auto(GStrv) split = NULL; +- +- /* get the kind/name/arch/branch */ +- split = g_strsplit (xref_str, "/", -1); +- if (g_strv_length (split) == 4) { +- const gchar *comp_type = xb_node_get_attr (component, "type"); +- AsComponentKind kind = as_component_kind_from_string (comp_type); +- if (kind != AS_COMPONENT_KIND_UNKNOWN) +- gs_app_set_kind (new, kind); +- else if (g_ascii_strcasecmp (split[0], "app") == 0) +- gs_app_set_kind (new, AS_COMPONENT_KIND_DESKTOP_APP); +- else if (g_ascii_strcasecmp (split[0], "runtime") == 0) +- gs_flatpak_set_runtime_kind_from_id (new); +- gs_flatpak_app_set_ref_name (new, split[1]); +- gs_flatpak_app_set_ref_arch (new, split[2]); +- gs_app_set_branch (new, split[3]); +- gs_app_set_metadata (new, "GnomeSoftware::packagename-value", xref_str); ++ if (xref_str != NULL) { ++ g_auto(GStrv) split = NULL; ++ ++ /* get the kind/name/arch/branch */ ++ split = g_strsplit (xref_str, "/", -1); ++ if (g_strv_length (split) == 4) { ++ const gchar *comp_type = xb_node_get_attr (component, "type"); ++ AsComponentKind kind = as_component_kind_from_string (comp_type); ++ if (kind != AS_COMPONENT_KIND_UNKNOWN) ++ gs_app_set_kind (new, kind); ++ else if (g_ascii_strcasecmp (split[0], "app") == 0) ++ gs_app_set_kind (new, AS_COMPONENT_KIND_DESKTOP_APP); ++ else if (g_ascii_strcasecmp (split[0], "runtime") == 0) ++ gs_flatpak_set_runtime_kind_from_id (new); ++ gs_flatpak_app_set_ref_name (new, split[1]); ++ gs_flatpak_app_set_ref_arch (new, split[2]); ++ gs_app_set_branch (new, split[3]); ++ gs_app_set_metadata (new, "GnomeSoftware::packagename-value", xref_str); ++ } + } + } +- } + +- if (gs_flatpak_app_get_ref_name (new) == NULL || +- gs_flatpak_app_get_ref_arch (new) == NULL) { +- g_debug ("Failed to get ref info for '%s' from wildcard '%s', skipping it...", gs_app_get_id (new), id); +- } else { +- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardRefineNewApp, "Flatpak (refine new app)", NULL); +- if (!gs_flatpak_refine_app_internal (self, new, require_flags, interactive, FALSE, *inout_components_by_bundle, +- silo, silo_filename, silo_installed_by_desktopid, cancellable, error)) +- return FALSE; +- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardRefineNewApp); ++ if (gs_flatpak_app_get_ref_name (new) == NULL || ++ gs_flatpak_app_get_ref_arch (new) == NULL) { ++ g_debug ("Failed to get ref info for '%s' from wildcard '%s', skipping it...", gs_app_get_id (new), id); ++ } else { ++ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardRefineNewApp, "Flatpak (refine new app)", NULL); ++ if (!gs_flatpak_refine_app_internal (self, new, require_flags, interactive, FALSE, components_by_bundle, ++ silo, silo_filename, silo_installed_by_desktopid, cancellable, error)) ++ return FALSE; ++ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardRefineNewApp); + +- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardSubsumeMetadata, "Flatpak (subsume metadata)", NULL); +- gs_app_subsume_metadata (new, app); +- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardSubsumeMetadata); ++ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardSubsumeMetadata, "Flatpak (subsume metadata)", NULL); ++ gs_app_subsume_metadata (new, app); ++ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardSubsumeMetadata); + +- gs_app_list_add (list, new); ++ gs_app_list_add (list, new); ++ } + } ++ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardGenerateApps); + } +- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardGenerateApps); + + GS_PROFILER_END_SCOPED (FlatpakRefineWildcard); + +diff --git a/plugins/flatpak/gs-flatpak.h b/plugins/flatpak/gs-flatpak.h +index 34e1416b5..1f7d35a8f 100644 +--- a/plugins/flatpak/gs-flatpak.h ++++ b/plugins/flatpak/gs-flatpak.h +@@ -104,16 +104,11 @@ gboolean gs_flatpak_refine_app_state (GsFlatpak *self, + void *event_user_data, + GCancellable *cancellable, + GError **error); +-gboolean gs_flatpak_refine_wildcard (GsFlatpak *self, +- GsApp *app, ++gboolean gs_flatpak_refine_wildcards (GsFlatpak *self, ++ GPtrArray *wildcard_apps, + GsAppList *list, + GsPluginRefineRequireFlags require_flags, + gboolean interactive, +- XbSilo **inout_silo, +- gchar **inout_silo_filename, +- GHashTable **inout_installed_by_desktopid, +- GHashTable **inout_components_by_id, +- GHashTable **inout_components_by_bundle, + GCancellable *cancellable, + GError **error); + gboolean gs_flatpak_launch (GsFlatpak *self, +diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c +index f5d4a1d67..c06b1fb5f 100644 +--- a/plugins/flatpak/gs-plugin-flatpak.c ++++ b/plugins/flatpak/gs-plugin-flatpak.c +@@ -667,31 +667,6 @@ gs_plugin_flatpak_refine_app (GsPluginFlatpak *self, + return gs_flatpak_refine_app (flatpak, app, require_flags, interactive, FALSE, event_callback, event_user_data, cancellable, error); + } + +-typedef struct { +- XbSilo *silo; +- gchar *silo_filename; +- GHashTable *installed_by_desktopid; +- GHashTable *components_by_id; +- GHashTable *components_by_bundle; +-} RefineInstallationData; +- +-static void +-refine_installation_data_free (gpointer ptr) +-{ +- RefineInstallationData *data = ptr; +- +- if (data == NULL) +- return; +- +- g_clear_pointer (&data->silo_filename, g_free); +- g_clear_pointer (&data->installed_by_desktopid, g_hash_table_unref); +- g_clear_pointer (&data->components_by_id, g_hash_table_unref); +- g_clear_pointer (&data->components_by_bundle, g_hash_table_unref); +- /* free the silo as the last, just in case, because the data from it is in the above hash tables */ +- g_clear_object (&data->silo); +- g_free (data); +-} +- + static gboolean + refine_app (GsPluginFlatpak *self, + GsApp *app, +@@ -778,8 +753,7 @@ refine_thread_cb (GTask *task, + gboolean interactive = (data->job_flags & GS_PLUGIN_REFINE_FLAGS_INTERACTIVE) != 0; + GsPluginEventCallback event_callback = data->event_callback; + void *event_user_data = data->event_user_data; +- g_autoptr(GPtrArray) installation_data = NULL; /* (element-type RefineInstallationData) */ +- g_autoptr(GsAppList) app_list = NULL; ++ g_autoptr(GPtrArray) wildcard_apps = g_ptr_array_new_with_free_func (g_object_unref); /* (element-type GsApp) (owned) */ + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); +@@ -790,38 +764,17 @@ refine_thread_cb (GTask *task, + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +- } +- +- /* Refine wildcards. +- * +- * Use a copy of the list for the loop because a function called +- * on the plugin may affect the list which can lead to problems +- * (e.g. inserting an app in the list on every call results in +- * an infinite loop) */ +- app_list = gs_app_list_copy (list); +- +- installation_data = g_ptr_array_new_full (self->installations->len, refine_installation_data_free); +- g_ptr_array_set_size (installation_data, self->installations->len); +- +- for (guint j = 0; j < gs_app_list_length (app_list); j++) { +- GsApp *app = gs_app_list_index (app_list, j); + +- if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD)) +- continue; ++ if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && gs_app_get_id (app) != NULL) ++ g_ptr_array_add (wildcard_apps, g_object_ref (app)); ++ } + ++ /* Refine wildcards. */ ++ if (wildcard_apps->len > 0) { + for (guint i = 0; i < self->installations->len; i++) { + GsFlatpak *flatpak = g_ptr_array_index (self->installations, i); +- RefineInstallationData *inst_data = g_ptr_array_index (installation_data, i); +- +- if (inst_data == NULL) { +- inst_data = g_new0 (RefineInstallationData, 1); +- installation_data->pdata[i] = inst_data; +- } + +- if (!gs_flatpak_refine_wildcard (flatpak, app, list, require_flags, interactive, &inst_data->silo, +- &inst_data->silo_filename, &inst_data->installed_by_desktopid, +- &inst_data->components_by_id, &inst_data->components_by_bundle, +- cancellable, &local_error)) { ++ if (!gs_flatpak_refine_wildcards (flatpak, wildcard_apps, list, require_flags, interactive, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +-- +GitLab + + +From 8472d717356a801e6c0171156738ea48480fa695 Mon Sep 17 00:00:00 2001 +Date: Wed, 7 Jan 2026 18:45:47 +0100 +Subject: [PATCH 3/3] flatpak: Hold silo lock when refining wildcard apps + +The refine can take a long time, thus hold the silo lock to not have +it invalidated from another thread. Object reference is not enough +for the XbSilo, it breaks as soon as the underlying file changes. +--- + plugins/flatpak/gs-flatpak.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c +index 782b0bb48..c5437125d 100644 +--- a/plugins/flatpak/gs-flatpak.c ++++ b/plugins/flatpak/gs-flatpak.c +@@ -59,7 +59,7 @@ struct _GsFlatpak { + AsComponentScope scope; + GsPlugin *plugin; + XbSilo *silo; +- GMutex silo_lock; ++ GRecMutex silo_lock; + gchar *silo_filename; + GHashTable *silo_installed_by_desktopid; + gint silo_change_stamp; +@@ -1187,11 +1187,11 @@ gs_flatpak_ref_silo (GsFlatpak *self, + g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) xremotes = NULL; + g_autoptr(GPtrArray) desktop_paths = NULL; +- g_autoptr(GMutexLocker) locker = NULL; ++ g_autoptr(GRecMutexLocker) locker = NULL; + g_autoptr(XbBuilder) builder = NULL; + g_autoptr(GMainContext) old_thread_default = NULL; + +- locker = g_mutex_locker_new (&self->silo_lock); ++ locker = g_rec_mutex_locker_new (&self->silo_lock); + /* everything is okay */ + if (self->silo != NULL && xb_silo_is_valid (self->silo) && + g_atomic_int_get (&self->silo_change_stamp_current) == g_atomic_int_get (&self->silo_change_stamp)) { +@@ -3754,6 +3754,7 @@ gs_flatpak_refine_wildcards (GsFlatpak *self, + GError **error) + { + g_autoptr(GError) error_local = NULL; ++ g_autoptr(GRecMutexLocker) silo_locker = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; + g_autoptr(GHashTable) components_by_id = NULL; +@@ -3764,6 +3765,11 @@ gs_flatpak_refine_wildcards (GsFlatpak *self, + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); + ++ /* the refine can take a long time, thus hold the silo lock to not have ++ it invalidated from another thread; object reference is not enough ++ for the XbSilo, it breaks as soon as the underlying file changes */ ++ silo_locker = g_rec_mutex_locker_new (&self->silo_lock); ++ + silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); + if (silo == NULL) + return FALSE; +@@ -4810,7 +4816,7 @@ gs_flatpak_finalize (GObject *object) + g_object_unref (self->plugin); + g_hash_table_unref (self->broken_remotes); + g_mutex_clear (&self->broken_remotes_mutex); +- g_mutex_clear (&self->silo_lock); ++ g_rec_mutex_clear (&self->silo_lock); + g_hash_table_unref (self->app_silos); + g_mutex_clear (&self->app_silos_mutex); + g_clear_pointer (&self->remote_title, g_hash_table_unref); +@@ -4831,7 +4837,7 @@ gs_flatpak_init (GsFlatpak *self) + { + /* XbSilo needs external locking as we destroy the silo and build a new + * one when something changes */ +- g_mutex_init (&self->silo_lock); ++ g_rec_mutex_init (&self->silo_lock); + + g_mutex_init (&self->installed_refs_mutex); + self->installed_refs = NULL; +-- +GitLab + diff --git a/gnome-software.spec b/gnome-software.spec index 6830ae0..c080ede 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -29,8 +29,8 @@ %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 49.2 -Release: 2%{?dist} +Version: 49.3 +Release: 1%{?dist} Summary: A software center for GNOME License: GPL-2.0-or-later diff --git a/sources b/sources index 0e06aac..c24e704 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-49.2.tar.xz) = 9ce33e2046b776f2a56c96d17eec85817e04bada9af9590125530c84bcc529f278bcba0c0ccdabcb459f92b326e0837cd63a9e316551ec313ac2f8720bf69835 +SHA512 (gnome-software-49.3.tar.xz) = 0414ea55ad3b83bcd50514985e3d2026207801c040934805bb26d162e88f0619a9d70aa6ba324a29acfab02573490b928c83594b54f57ec2a8ad00e6c127f657