[gnome-software] snap: Expose snap channels as app alternates



commit b8b35b97d059047f42ad07f80917a1c140be90c4
Author: Robert Ancell <robert ancell canonical com>
Date:   Mon Oct 1 17:04:57 2018 +1300

    snap: Expose snap channels as app alternates

 plugins/snap/gs-plugin-snap.c | 193 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 173 insertions(+), 20 deletions(-)
---
diff --git a/plugins/snap/gs-plugin-snap.c b/plugins/snap/gs-plugin-snap.c
index 57ca8e77..db9a1de1 100644
--- a/plugins/snap/gs-plugin-snap.c
+++ b/plugins/snap/gs-plugin-snap.c
@@ -21,6 +21,27 @@ struct GsPluginData {
        GHashTable              *store_snaps;
 };
 
+typedef struct {
+       SnapdSnap *snap;
+       gboolean full_details;
+} CacheEntry;
+
+static CacheEntry *
+cache_entry_new (SnapdSnap *snap, gboolean full_details)
+{
+       CacheEntry *entry = g_slice_new (CacheEntry);
+       entry->snap = g_object_ref (snap);
+       entry->full_details = full_details;
+       return entry;
+}
+
+static void
+cache_entry_free (CacheEntry *entry)
+{
+       g_object_unref (entry->snap);
+       g_slice_free (CacheEntry, entry);
+}
+
 static SnapdClient *
 get_client (GsPlugin *plugin, GError **error)
 {
@@ -55,7 +76,7 @@ gs_plugin_initialize (GsPlugin *plugin)
        }
 
        priv->store_snaps = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                  g_free, (GDestroyNotify) g_object_unref);
+                                                  g_free, (GDestroyNotify) cache_entry_free);
 
        gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "desktop-categories");
        gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_BETTER_THAN, "packagekit");
@@ -174,21 +195,24 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
 }
 
 static SnapdSnap *
-store_snap_cache_lookup (GsPlugin *plugin, const gchar *name)
+store_snap_cache_lookup (GsPlugin *plugin, const gchar *name, gboolean need_details)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
+       CacheEntry *entry;
        g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->store_snaps_lock);
-       SnapdSnap *snap;
 
-       snap = g_hash_table_lookup (priv->store_snaps, name);
-       if (snap == NULL)
+       entry = g_hash_table_lookup (priv->store_snaps, name);
+       if (entry == NULL)
                return NULL;
 
-       return g_object_ref (snap);
+       if (need_details && !entry->full_details)
+               return NULL;
+
+       return g_object_ref (entry->snap);
 }
 
 static void
-store_snap_cache_update (GsPlugin *plugin, GPtrArray *snaps)
+store_snap_cache_update (GsPlugin *plugin, GPtrArray *snaps, gboolean full_details)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->store_snaps_lock);
@@ -196,7 +220,7 @@ store_snap_cache_update (GsPlugin *plugin, GPtrArray *snaps)
 
        for (i = 0; i < snaps->len; i++) {
                SnapdSnap *snap = snaps->pdata[i];
-               g_hash_table_insert (priv->store_snaps, g_strdup (snapd_snap_get_name (snap)), g_object_ref 
(snap));
+               g_hash_table_insert (priv->store_snaps, g_strdup (snapd_snap_get_name (snap)), 
cache_entry_new (snap, full_details));
        }
 }
 
@@ -216,7 +240,7 @@ find_snaps (GsPlugin *plugin, SnapdFindFlags flags, const gchar *section, const
                return NULL;
        }
 
-       store_snap_cache_update (plugin, snaps);
+       store_snap_cache_update (plugin, snaps, flags & SNAPD_FIND_FLAGS_MATCH_NAME);
 
        return g_steal_pointer (&snaps);
 }
@@ -529,13 +553,13 @@ gs_plugin_add_search (GsPlugin *plugin,
 }
 
 static SnapdSnap *
-get_store_snap (GsPlugin *plugin, const gchar *name, GCancellable *cancellable, GError **error)
+get_store_snap (GsPlugin *plugin, const gchar *name, gboolean need_details, GCancellable *cancellable, 
GError **error)
 {
        SnapdSnap *snap = NULL;
        g_autoptr(GPtrArray) snaps = NULL;
 
        /* use cached version if available */
-       snap = store_snap_cache_lookup (plugin, name);
+       snap = store_snap_cache_lookup (plugin, name, need_details);
        if (snap != NULL)
                return g_object_ref (snap);
 
@@ -546,6 +570,94 @@ get_store_snap (GsPlugin *plugin, const gchar *name, GCancellable *cancellable,
        return g_object_ref (g_ptr_array_index (snaps, 0));
 }
 
+static int
+track_value (const gchar *track, GStrv tracks)
+{
+       int r = 0;
+       while (tracks[r] != NULL && strcmp (track, tracks[r]) != 0)
+               r++;
+       return r;
+}
+
+static int
+risk_value (const gchar *risk)
+{
+       if (strcmp (risk, "stable") == 0)
+               return 0;
+       else if (strcmp (risk, "candidate") == 0)
+               return 1;
+       else if (strcmp (risk, "beta") == 0)
+               return 2;
+       else if (strcmp (risk, "edge") == 0)
+               return 3;
+       else
+               return 4;
+}
+
+static int
+compare_channel (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+       SnapdChannel *channel_a = *(SnapdChannel **)a, *channel_b = *(SnapdChannel **)b;
+       GStrv tracks = user_data;
+       int r;
+
+       r = track_value (snapd_channel_get_track (channel_a), tracks) - track_value (snapd_channel_get_track 
(channel_b), tracks);
+       if (r != 0)
+               return r;
+
+       r = g_strcmp0 (snapd_channel_get_risk (channel_a), snapd_channel_get_risk (channel_b));
+       if (r != 0) {
+               int r2;
+
+               r2 = risk_value (snapd_channel_get_risk (channel_a)) - risk_value (snapd_channel_get_risk 
(channel_b));
+               if (r2 != 0)
+                       return r2;
+               else
+                       return r;
+       }
+
+       return g_strcmp0 (snapd_channel_get_branch (channel_a), snapd_channel_get_branch (channel_b));
+}
+
+gboolean
+gs_plugin_add_alternates (GsPlugin *plugin,
+                         GsApp *app,
+                         GsAppList *list,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       g_autoptr(SnapdSnap) snap = NULL;
+       GStrv tracks;
+       GPtrArray *channels;
+       g_autoptr(GPtrArray) sorted_channels = NULL;
+
+       snap = get_store_snap (plugin, gs_app_get_metadata_item (app, "snap::name"), TRUE, cancellable, NULL);
+       if (snap == NULL) {
+               g_warning ("Failed to get store snap %s\n", gs_app_get_metadata_item (app, "snap::name"));
+               return TRUE;
+       }
+
+       tracks = snapd_snap_get_tracks (snap);
+       channels = snapd_snap_get_channels (snap);
+       sorted_channels = g_ptr_array_new ();
+       for (guint i = 0; i < channels->len; i++) {
+               SnapdChannel *channel = g_ptr_array_index (channels, i);
+               g_ptr_array_add (sorted_channels, channel);
+       }
+       g_ptr_array_sort_with_data (sorted_channels, compare_channel, tracks);
+
+       for (guint i = 0; i < sorted_channels->len; i++) {
+               SnapdChannel *channel = g_ptr_array_index (sorted_channels, i);
+               g_autoptr(GsApp) a = gs_app_new (NULL);
+               gs_app_set_bundle_kind (a, AS_BUNDLE_KIND_SNAP);
+               gs_app_set_metadata (a, "snap::name", gs_app_get_metadata_item (app, "snap::name"));
+               gs_app_set_branch (a, snapd_channel_get_name (channel));
+               gs_app_list_add (list, a);
+       }
+
+       return TRUE;
+}
+
 static gboolean
 load_snap_icon (GsApp *app, SnapdClient *client, SnapdSnap *snap, GCancellable *cancellable)
 {
@@ -690,7 +802,7 @@ load_icon (GsPlugin *plugin, SnapdClient *client, GsApp *app, const gchar *id, S
        }
 
        if (store_snap == NULL)
-               store_snap = get_store_snap (plugin, gs_app_get_metadata_item (app, "snap::name"), 
cancellable, NULL);
+               store_snap = get_store_snap (plugin, gs_app_get_metadata_item (app, "snap::name"), FALSE, 
cancellable, NULL);
        if (store_snap != NULL)
                return load_store_icon (app, store_snap);
 
@@ -781,7 +893,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        g_autoptr(SnapdClient) client = NULL;
-       const gchar *name;
+       const gchar *snap_name, *channel, *name;
        g_autoptr(SnapdSnap) local_snap = NULL;
        g_autoptr(SnapdSnap) store_snap = NULL;
        SnapdSnap *snap;
@@ -796,14 +908,27 @@ gs_plugin_refine_app (GsPlugin *plugin,
        if (client == NULL)
                return FALSE;
 
+       snap_name = gs_app_get_metadata_item (app, "snap::name");
+       channel = gs_app_get_branch (app);
+
        /* get information from local snaps and store */
-       local_snap = snapd_client_get_snap_sync (client, gs_app_get_metadata_item (app, "snap::name"), 
cancellable, NULL);
-       if (local_snap == NULL || (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SCREENSHOTS) != 0)
-               store_snap = get_store_snap (plugin, gs_app_get_metadata_item (app, "snap::name"), 
cancellable, NULL);
+       local_snap = snapd_client_get_snap_sync (client, snap_name, cancellable, NULL);
+       if (local_snap == NULL || (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SCREENSHOTS) != 0 || channel != 
NULL)
+               store_snap = get_store_snap (plugin, snap_name, channel != NULL, cancellable, NULL);
        if (local_snap == NULL && store_snap == NULL)
                return TRUE;
 
-       if (local_snap != NULL)
+       /* Get default channel to install */
+       if (channel == NULL) {
+               if (local_snap != NULL)
+                       channel = snapd_snap_get_tracking_channel (local_snap);
+               else
+                       channel = snapd_snap_get_channel (store_snap);
+
+               gs_app_set_branch (app, channel);
+       }
+
+       if (local_snap != NULL && g_strcmp0 (snapd_snap_get_tracking_channel (local_snap), channel) == 0)
                gs_app_set_state (app, AS_APP_STATE_INSTALLED);
        else
                gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
@@ -829,6 +954,16 @@ gs_plugin_refine_app (GsPlugin *plugin,
        snap = local_snap != NULL ? local_snap : store_snap;
        gs_app_set_version (app, snapd_snap_get_version (snap));
 
+       if (channel != NULL && store_snap != NULL) {
+               GPtrArray *channels = snapd_snap_get_channels (store_snap);
+               guint i;
+               for (i = 0; i < channels->len; i++) {
+                       SnapdChannel *c = channels->pdata[i];
+                       if (g_strcmp0 (snapd_channel_get_name (c), channel) != 0)
+                               gs_app_set_version (app, snapd_channel_get_version (c));
+               }
+       }
+
        switch (snapd_snap_get_snap_type (snap)) {
        case SNAPD_SNAP_TYPE_APP:
                gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
@@ -838,7 +973,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
        case SNAPD_SNAP_TYPE_OS:
                gs_app_set_kind (app, AS_APP_KIND_RUNTIME);
                break;
-        default:
+       default:
        case SNAPD_SNAP_TYPE_UNKNOWN:
                gs_app_set_kind (app, AS_APP_KIND_UNKNOWN);
                break;
@@ -873,7 +1008,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
 
        /* load icon if requested */
        if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON && gs_app_get_pixbuf (app) == NULL)
-               load_icon (plugin, client, app, gs_app_get_metadata_item (app, "snap::name"), local_snap, 
store_snap, cancellable);
+               load_icon (plugin, client, app, snap_name, local_snap, store_snap, cancellable);
 
        return TRUE;
 }
@@ -903,7 +1038,10 @@ gs_plugin_app_install (GsPlugin *plugin,
                       GError **error)
 {
        g_autoptr(SnapdClient) client = NULL;
+       const gchar *name, *channel;
        SnapdInstallFlags flags = SNAPD_INSTALL_FLAGS_NONE;
+       gboolean result;
+       g_autoptr(GError) error_local = NULL;
 
        /* We can only install apps we know of */
        if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
@@ -913,15 +1051,30 @@ gs_plugin_app_install (GsPlugin *plugin,
        if (client == NULL)
                return FALSE;
 
+       name = gs_app_get_metadata_item (app, "snap::name");
+       channel = gs_app_get_branch (app);
+
        gs_app_set_state (app, AS_APP_STATE_INSTALLING);
+
        if (g_strcmp0 (gs_app_get_metadata_item (app, "snap::confinement"), "classic") == 0)
                flags |= SNAPD_INSTALL_FLAGS_CLASSIC;
-       if (!snapd_client_install2_sync (client, flags, gs_app_get_metadata_item (app, "snap::name"), NULL, 
NULL, progress_cb, app, cancellable, error)) {
+       result = snapd_client_install2_sync (client, flags, name, channel, NULL, progress_cb, app, 
cancellable, &error_local);
+
+       /* if already installed then just try to switch channel */
+       if (!result && g_error_matches (error_local, SNAPD_ERROR, SNAPD_ERROR_ALREADY_INSTALLED)) {
+               g_clear_error (&error_local);
+               result = snapd_client_refresh_sync (client, name, channel, progress_cb, app, cancellable, 
&error_local);
+       }
+
+       if (!result) {
                gs_app_set_state_recover (app);
+               g_propagate_error (error, g_steal_pointer (&error_local));
                snapd_error_convert (error);
                return FALSE;
        }
+
        gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+
        return TRUE;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]