diff options
author | Matthew Leeds <matthew.leeds@endlessm.com> | 2020-07-30 17:05:43 -0700 |
---|---|---|
committer | Alexander Larsson <alexander.larsson@gmail.com> | 2020-08-31 16:29:03 +0200 |
commit | d2d5397cc15fb60faf515e444d9cc34b9e5af16d (patch) | |
tree | db169fdb7a5da5c35d92977e13ea8e58200c626a | |
parent | f24b1fdc1aff9787d219a87a4ca15f0eed4d6420 (diff) | |
download | flatpak-d2d5397cc15fb60faf515e444d9cc34b9e5af16d.tar.gz |
Add pin command to keep unused runtimes
As discussed here [1], we want a way to mark runtimes to be kept even
when they are unused by any apps and we are removing such runtimes.
Currently this is a command that can be run manually; a subsequent
commit will pin runtimes automatically if they are installed
independently of any app.
A unit test is included.
[1] https://github.com/flatpak/flatpak/issues/2639#issuecomment-662311756
-rw-r--r-- | app/Makefile.am.inc | 1 | ||||
-rw-r--r-- | app/flatpak-builtins-mask.c | 4 | ||||
-rw-r--r-- | app/flatpak-builtins-pin.c | 185 | ||||
-rw-r--r-- | app/flatpak-builtins.h | 1 | ||||
-rw-r--r-- | app/flatpak-main.c | 1 | ||||
-rw-r--r-- | common/flatpak-dir-private.h | 2 | ||||
-rw-r--r-- | common/flatpak-dir.c | 66 | ||||
-rw-r--r-- | common/flatpak-installation.c | 7 | ||||
-rw-r--r-- | common/flatpak-utils-private.h | 2 | ||||
-rw-r--r-- | common/flatpak-utils.c | 25 | ||||
-rw-r--r-- | doc/Makefile.am | 1 | ||||
-rw-r--r-- | doc/flatpak-pin.xml | 151 | ||||
-rw-r--r-- | doc/flatpak.xml | 7 | ||||
-rw-r--r-- | system-helper/flatpak-system-helper.c | 3 | ||||
-rw-r--r-- | tests/test-repo.sh | 26 |
15 files changed, 472 insertions, 10 deletions
diff --git a/app/Makefile.am.inc b/app/Makefile.am.inc index 0f5868d4..0175b1c6 100644 --- a/app/Makefile.am.inc +++ b/app/Makefile.am.inc @@ -81,6 +81,7 @@ flatpak_SOURCES = \ app/flatpak-builtins-update.c \ app/flatpak-builtins-uninstall.c \ app/flatpak-builtins-mask.c \ + app/flatpak-builtins-pin.c \ app/flatpak-builtins-list.c \ app/flatpak-builtins-info.c \ app/flatpak-builtins-config.c \ diff --git a/app/flatpak-builtins-mask.c b/app/flatpak-builtins-mask.c index 68b0b0db..401bb1c0 100644 --- a/app/flatpak-builtins-mask.c +++ b/app/flatpak-builtins-mask.c @@ -138,7 +138,9 @@ flatpak_builtin_mask (int argc, char **argv, GCancellable *cancellable, GError * { g_autofree char *regexp; - regexp = flatpak_filter_glob_to_regexp (pattern, error); + regexp = flatpak_filter_glob_to_regexp (pattern, + FALSE, /* match apps or runtimes */ + error); if (regexp == NULL) return FALSE; diff --git a/app/flatpak-builtins-pin.c b/app/flatpak-builtins-pin.c new file mode 100644 index 00000000..64da2c18 --- /dev/null +++ b/app/flatpak-builtins-pin.c @@ -0,0 +1,185 @@ +/* + * Copyright © 2020 Endless OS Foundation LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Matthew Leeds <matthew.leeds@endlessm.com> + */ + +#include "config.h" + +#include <locale.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <glib/gi18n.h> + +#include "libglnx/libglnx.h" + +#include "flatpak-builtins.h" +#include "flatpak-builtins-utils.h" +#include "flatpak-cli-transaction.h" +#include "flatpak-quiet-transaction.h" +#include "flatpak-utils-private.h" +#include "flatpak-error.h" + +/* Note: the code here is copied from flatpak-builtins-mask.c */ + +static gboolean opt_remove; + +static GOptionEntry options[] = { + { "remove", 0, 0, G_OPTION_ARG_NONE, &opt_remove, N_("Remove matching pins"), NULL }, + { NULL } +}; + +static GPtrArray * +get_old_patterns (FlatpakDir *dir) +{ + g_autoptr(GPtrArray) patterns = NULL; + g_autofree char *pinned = NULL; + int i; + + patterns = g_ptr_array_new_with_free_func (g_free); + + pinned = flatpak_dir_get_config (dir, "pinned", NULL); + if (pinned) + { + g_auto(GStrv) oldv = g_strsplit (pinned, ";", -1); + + for (i = 0; oldv[i] != NULL; i++) + { + const char *old = oldv[i]; + + if (*old != 0 && !flatpak_g_ptr_array_contains_string (patterns, old)) + g_ptr_array_add (patterns, g_strdup (old)); + } + } + + return g_steal_pointer (&patterns); +} + + +gboolean +flatpak_builtin_pin (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(GPtrArray) dirs = NULL; + FlatpakDir *dir; + g_autofree char *merged_patterns = NULL; + g_autoptr(GPtrArray) patterns = NULL; + int i; + + context = g_option_context_new (_("[PATTERN…] - disable automatic removal of runtimes matching patterns")); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + + if (!flatpak_option_context_parse (context, options, &argc, &argv, + FLATPAK_BUILTIN_FLAG_ONE_DIR, + &dirs, cancellable, error)) + return FALSE; + + dir = g_ptr_array_index (dirs, 0); + + patterns = get_old_patterns (dir); + + if (argc == 1) + { + if (patterns->len == 0) + { + g_print (_("No pinned patterns\n")); + } + else + { + g_print (_("Pinned patterns:\n")); + + for (i = 0; i < patterns->len; i++) + { + const char *old = g_ptr_array_index (patterns, i); + g_print (" %s\n", old); + } + } + } + else + { + for (i = 1; i < argc; i++) + { + const char *pattern = argv[i]; + + if (opt_remove) + { + int j; + + for (j = 0; j < patterns->len; j++) + { + if (strcmp (g_ptr_array_index (patterns, j), pattern) == 0) + break; + } + + if (j == patterns->len) + return flatpak_fail (error, _("No current pin matching %s"), pattern); + else + g_ptr_array_remove_index (patterns, j); + } + else + { + g_autofree char *regexp; + + regexp = flatpak_filter_glob_to_regexp (pattern, + TRUE, /* only match runtimes */ + error); + if (regexp == NULL) + return FALSE; + + if (!flatpak_g_ptr_array_contains_string (patterns, pattern)) + g_ptr_array_add (patterns, g_strdup (pattern)); + } + } + + g_ptr_array_sort (patterns, flatpak_strcmp0_ptr); + + g_ptr_array_add (patterns, NULL); + merged_patterns = g_strjoinv (";", (char **)patterns->pdata); + + if (!flatpak_dir_set_config (dir, "pinned", merged_patterns, error)) + return FALSE; + } + + return TRUE; +} + +gboolean +flatpak_complete_pin (FlatpakCompletion *completion) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(GPtrArray) dirs = NULL; + + context = g_option_context_new (""); + if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, + FLATPAK_BUILTIN_FLAG_ONE_DIR | FLATPAK_BUILTIN_FLAG_OPTIONAL_REPO, + &dirs, NULL, NULL)) + return FALSE; + + switch (completion->argc) + { + case 0: + case 1: /* PATTERN */ + flatpak_complete_options (completion, global_entries); + flatpak_complete_options (completion, options); + flatpak_complete_options (completion, user_entries); + break; + } + + return TRUE; +} diff --git a/app/flatpak-builtins.h b/app/flatpak-builtins.h index 1fd9c9e5..ec1ac4a4 100644 --- a/app/flatpak-builtins.h +++ b/app/flatpak-builtins.h @@ -86,6 +86,7 @@ BUILTINPROTO (remote_info) BUILTINPROTO (remote_list) BUILTINPROTO (install) BUILTINPROTO (mask) +BUILTINPROTO (pin) BUILTINPROTO (update) BUILTINPROTO (make_current_app) BUILTINPROTO (uninstall) diff --git a/app/flatpak-main.c b/app/flatpak-main.c index 0fe1ce7b..62151e02 100644 --- a/app/flatpak-main.c +++ b/app/flatpak-main.c @@ -80,6 +80,7 @@ static FlatpakCommand commands[] = { /* Alias remove to uninstall to help users of yum/dnf/apt */ { "remove", NULL, flatpak_builtin_uninstall, flatpak_complete_uninstall, TRUE }, { "mask", N_("Mask out updates and automatic installation"), flatpak_builtin_mask, flatpak_complete_mask }, + { "pin", N_("Pin a runtime to prevent automatic removal"), flatpak_builtin_pin, flatpak_complete_pin }, { "list", N_("List installed apps and/or runtimes"), flatpak_builtin_list, flatpak_complete_list }, { "info", N_("Show info for installed app or runtime"), flatpak_builtin_info, flatpak_complete_info }, { "history", N_("Show history"), flatpak_builtin_history, flatpak_complete_history }, diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h index de8d047b..8f5ec83e 100644 --- a/common/flatpak-dir-private.h +++ b/common/flatpak-dir-private.h @@ -474,6 +474,8 @@ char ** flatpak_dir_search_for_dependency (FlatpakDir *self, GError **error); gboolean flatpak_dir_ref_is_masked (FlatpakDir *self, const char *ref); +gboolean flatpak_dir_ref_is_pinned (FlatpakDir *self, + const char *ref); char * flatpak_dir_find_remote_ref (FlatpakDir *self, const char *remote, const char **opt_sideload_repos, diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index 09a390c2..0f325b9c 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -214,6 +214,7 @@ struct FlatpakDir /* Config cache, protected by config_cache lock */ GRegex *masked; + GRegex *pinned; SoupSession *soup_session; }; @@ -2260,6 +2261,7 @@ flatpak_dir_finalize (GObject *object) g_clear_pointer (&self->summary_cache, g_hash_table_unref); g_clear_pointer (&self->remote_filters, g_hash_table_unref); g_clear_pointer (&self->masked, g_regex_unref); + g_clear_pointer (&self->pinned, g_regex_unref); G_OBJECT_CLASS (flatpak_dir_parent_class)->finalize (object); } @@ -3436,6 +3438,7 @@ flatpak_dir_recreate_repo (FlatpakDir *self, G_LOCK (config_cache); g_clear_pointer (&self->masked, g_regex_unref); + g_clear_pointer (&self->pinned, g_regex_unref); G_UNLOCK (config_cache); @@ -3841,6 +3844,7 @@ _flatpak_dir_reload_config (FlatpakDir *self, G_LOCK (config_cache); g_clear_pointer (&self->masked, g_regex_unref); + g_clear_pointer (&self->pinned, g_regex_unref); G_UNLOCK (config_cache); return TRUE; @@ -13603,7 +13607,7 @@ flatpak_dir_get_mask_regexp (FlatpakDir *self) { g_autofree char *regexp = NULL; - regexp = flatpak_filter_glob_to_regexp (pattern, NULL); + regexp = flatpak_filter_glob_to_regexp (pattern, FALSE, NULL); if (regexp) { if (i != 0) @@ -13635,6 +13639,66 @@ flatpak_dir_ref_is_masked (FlatpakDir *self, return !flatpak_filters_allow_ref (NULL, masked, ref); } +static GRegex * +flatpak_dir_get_pin_regexp (FlatpakDir *self) +{ + GRegex *res = NULL; + + G_LOCK (config_cache); + + if (self->pinned == NULL) + { + g_autofree char *pinned = NULL; + + pinned = flatpak_dir_get_config (self, "pinned", NULL); + if (pinned) + { + g_auto(GStrv) patterns = g_strsplit (pinned, ";", -1); + g_autoptr(GString) deny_regexp = g_string_new ("^("); + int i; + + for (i = 0; patterns[i] != NULL; i++) + { + const char *pattern = patterns[i]; + + if (*pattern != 0) + { + g_autofree char *regexp = NULL; + + regexp = flatpak_filter_glob_to_regexp (pattern, + TRUE, /* only match runtimes */ + NULL); + if (regexp) + { + if (i != 0) + g_string_append (deny_regexp, "|"); + g_string_append (deny_regexp, regexp); + } + } + } + + g_string_append (deny_regexp, ")$"); + self->pinned = g_regex_new (deny_regexp->str, G_REGEX_DOLLAR_ENDONLY|G_REGEX_RAW|G_REGEX_OPTIMIZE, G_REGEX_MATCH_ANCHORED, NULL); + } + } + + if (self->pinned) + res = g_regex_ref (self->pinned); + + G_UNLOCK (config_cache); + + return res; +} + +gboolean +flatpak_dir_ref_is_pinned (FlatpakDir *self, + const char *ref) +{ + g_autoptr(GRegex) pinned = flatpak_dir_get_pin_regexp (self); + + return !flatpak_filters_allow_ref (NULL, pinned, ref); +} + GPtrArray * flatpak_dir_find_remote_related_for_metadata (FlatpakDir *self, FlatpakRemoteState *state, diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c index 32cffd62..8b2f9d69 100644 --- a/common/flatpak-installation.c +++ b/common/flatpak-installation.c @@ -2926,6 +2926,7 @@ find_used_refs (FlatpakDir *dir, * * A reference is used if it is either an application, or an sdk, * or the runtime of a used ref, or an extension of a used ref. + * Pinned runtimes are also considered used; see flatpak-pin(1). * * Returns: (transfer container) (element-type FlatpakInstalledRef): a GPtrArray of * #FlatpakInstalledRef instances @@ -3030,6 +3031,12 @@ flatpak_installation_list_unused_refs (FlatpakInstallation *self, if (arch != NULL && strcmp (parts[2], arch) != 0) continue; + if (flatpak_dir_ref_is_pinned (dir, ref)) + { + g_debug ("Ref %s is pinned, considering as used", ref); + continue; + } + if (!g_hash_table_contains (used_refs, ref)) { if (g_hash_table_add (refs_hash, (gpointer) ref)) diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h index 40fa57c2..ebbf7759 100644 --- a/common/flatpak-utils-private.h +++ b/common/flatpak-utils-private.h @@ -194,7 +194,7 @@ gboolean flatpak_id_has_subref_suffix (const char *id); char **flatpak_decompose_ref (const char *ref, GError **error); -char * flatpak_filter_glob_to_regexp (const char *glob, GError **error); +char * flatpak_filter_glob_to_regexp (const char *glob, gboolean runtime_only, GError **error); gboolean flatpak_parse_filters (const char *data, GRegex **allow_refs_out, GRegex **deny_refs_out, diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c index 16c307e1..4b3079bc 100644 --- a/common/flatpak-utils.c +++ b/common/flatpak-utils.c @@ -1131,7 +1131,9 @@ line_get_word (char **line) } char * -flatpak_filter_glob_to_regexp (const char *glob, GError **error) +flatpak_filter_glob_to_regexp (const char *glob, + gboolean runtime_only, + GError **error) { g_autoptr(GString) regexp = g_string_new (""); int parts = 1; @@ -1139,8 +1141,16 @@ flatpak_filter_glob_to_regexp (const char *glob, GError **error) if (g_str_has_prefix (glob, "app/")) { - glob += strlen ("app/"); - g_string_append (regexp, "app/"); + if (runtime_only) + { + flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Glob can't match apps")); + return NULL; + } + else + { + glob += strlen ("app/"); + g_string_append (regexp, "app/"); + } } else if (g_str_has_prefix (glob, "runtime/")) { @@ -1148,7 +1158,12 @@ flatpak_filter_glob_to_regexp (const char *glob, GError **error) g_string_append (regexp, "runtime/"); } else - g_string_append (regexp, "(app|runtime)/"); + { + if (runtime_only) + g_string_append (regexp, "runtime/"); + else + g_string_append (regexp, "(app|runtime)/"); + } /* We really need an id part, the rest is optional */ if (*glob == 0) @@ -1253,7 +1268,7 @@ flatpak_parse_filters (const char *data, if (next != NULL) return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Trailing text on line %d"), i + 1); - ref_regexp = flatpak_filter_glob_to_regexp (glob, error); + ref_regexp = flatpak_filter_glob_to_regexp (glob, FALSE, error); if (ref_regexp == NULL) return glnx_prefix_error (error, _("on line %d"), i + 1); diff --git a/doc/Makefile.am b/doc/Makefile.am index 36ef2783..aa3d186b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -30,6 +30,7 @@ man1 = \ flatpak-update.1 \ flatpak-uninstall.1 \ flatpak-mask.1 \ + flatpak-pin.1 \ flatpak-list.1 \ flatpak-info.1 \ flatpak-make-current.1 \ diff --git a/doc/flatpak-pin.xml b/doc/flatpak-pin.xml new file mode 100644 index 00000000..b086e9b3 --- /dev/null +++ b/doc/flatpak-pin.xml @@ -0,0 +1,151 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<refentry id="flatpak-pin"> + + <refentryinfo> + <title>flatpak pin</title> + <productname>flatpak</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Matthew</firstname> + <surname>Leeds</surname> + <email>matthew.leeds@endlessm.com</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>flatpak pin</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>flatpak-pin</refname> + <refpurpose>Pin runtimes to prevent automatic removal</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>flatpak pin</command> + <arg choice="opt" rep="repeat">OPTION</arg> + <arg choice="plain" rep="repeat">PATTERN</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + Flatpak maintains a list of patterns that define which refs are pinned. + A pinned ref will never be automatically uninstalled (as are unused + runtimes periodically). This can be useful if for example you are using + a runtime for development purposes. + </para> + <para> + The patterns are just a partial ref, with the * character matching anything + within that part of the ref. Only runtimes can be pinned, not apps. Here + are some example patterns: +<programlisting> +org.some.Runtime +org.some.Runtime//unstable +runtime/org.domain.* +org.some.Runtime/arm +</programlisting> + </para> + <para> + To list the current set of pins, run this command without any patterns. + </para> + </refsect1> + + <refsect1> + <title>Options</title> + + <para>The following options are understood:</para> + + <variablelist> + <varlistentry> + <term><option>-h</option></term> + <term><option>--help</option></term> + + <listitem><para> + Show help options and exit. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--remove</option></term> + + <listitem><para> + Instead of adding the patterns, remove matching patterns. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--user</option></term> + + <listitem><para> + Pin refs in a per-user installation. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--system</option></term> + + <listitem><para> + Pin refs in the default system-wide installation. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--installation=NAME</option></term> + + <listitem><para> + Pin refs in a system-wide installation + specified by <arg choice="plain">NAME</arg> among those defined in + <filename>/etc/flatpak/installations.d/</filename>. Using + <option>--installation=default</option> is equivalent to using + <option>--system</option>. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-v</option></term> + <term><option>--verbose</option></term> + + <listitem><para> + Print debug information during command processing. + </para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + <command>$ flatpak pin</command> + </para> + <para> + <command>$ flatpak pin org.freedesktop.Platform//19.08</command> + </para> + <para> + <command>$ flatpak pin --remove org.freedesktop.Platform//19.08</command> + </para> + + </refsect1> + + <refsect1> + <title>See also</title> + + <para> + <citerefentry><refentrytitle>flatpak</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>flatpak-uninstall</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + </para> + + </refsect1> + +</refentry> diff --git a/doc/flatpak.xml b/doc/flatpak.xml index aefa4629..b07aa31c 100644 --- a/doc/flatpak.xml +++ b/doc/flatpak.xml @@ -216,6 +216,13 @@ </para></listitem> </varlistentry> <varlistentry> + <term><citerefentry><refentrytitle>flatpak-pin</refentrytitle><manvolnum>1</manvolnum></citerefentry></term> + + <listitem><para> + Pin runtimes to prevent automatic removal. + </para></listitem> + </varlistentry> + <varlistentry> <term><citerefentry><refentrytitle>flatpak-list</refentrytitle><manvolnum>1</manvolnum></citerefentry></term> <listitem><para> diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c index 0aa9f6a0..137d30b2 100644 --- a/system-helper/flatpak-system-helper.c +++ b/system-helper/flatpak-system-helper.c @@ -1108,7 +1108,8 @@ handle_configure (FlatpakSystemHelper *object, if ((strcmp (arg_key, "languages") != 0) && (strcmp (arg_key, "extra-languages") != 0) && - (strcmp (arg_key, "masked") != 0)) + (strcmp (arg_key, "masked") != 0) && + (strcmp (arg_key, "pinned") != 0)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Unsupported key: %s", arg_key); diff --git a/tests/test-repo.sh b/tests/test-repo.sh index a367a1b1..5c2db337 100644 --- a/tests/test-repo.sh +++ b/tests/test-repo.sh @@ -24,7 +24,7 @@ set -euo pipefail skip_without_bwrap skip_revokefs_without_fuse -echo "1..37" +echo "1..38" #Regular repo setup_repo @@ -587,6 +587,30 @@ assert_not_file_has_content list-log "org\.test\.Platform" ok "uninstall --unused" +${FLATPAK} ${U} install -y test-repo org.test.Platform + +${FLATPAK} ${U} list -a --columns=application > list-log +assert_file_has_content list-log "org\.test\.Platform" + +# Check that the runtime won't be removed if it's pinned +${FLATPAK} ${U} pin org.test.Platform +${FLATPAK} ${U} pin > pins +assert_file_has_content pins "org\.test\.Platform" +rm pins + +${FLATPAK} ${U} uninstall -y --unused + +${FLATPAK} ${U} list -a --columns=application > list-log +assert_file_has_content list-log "org\.test\.Platform" + +# Remove the pin and try again +${FLATPAK} ${U} pin --remove "org.test.Platform" +${FLATPAK} ${U} uninstall -y --unused +${FLATPAK} ${U} list -a --columns=application > list-log +assert_not_file_has_content list-log "org\.test\.Platform" + +ok "uninstall --unused ignores pinned runtimes" + # Test that remote-ls works in all of the following cases: # * system remote, and --system is used # * system remote, and --system is omitted |