1816 lines
75 KiB
Diff
1816 lines
75 KiB
Diff
From 4d171a522dfef4039adea792f8ff856036f14686 Mon Sep 17 00:00:00 2001
|
||
From: Kalev Lember <klember@redhat.com>
|
||
Date: Tue, 16 Apr 2024 12:18:59 +0200
|
||
Subject: Add initial support for preinstalling flatpaks
|
||
|
||
This adds new FlatpakTransaction API, and a new top level CLI command to
|
||
preinstall flatpaks, that is to install flatpaks that are considered
|
||
part of the operating system.
|
||
|
||
A new drop-in directory /etc/flatpak/preinstall.d/ allows configuring
|
||
what apps should be preinstalled, and a new flatpak preinstall command
|
||
installs and removes apps based on the current configuration.
|
||
|
||
A drop-in loupe.preinstall file can look something like this:
|
||
|
||
[Flatpak Preinstall org.gnome.Loupe]
|
||
Branch=stable
|
||
IsRuntime=false
|
||
|
||
The corresponding API is flatpak_transaction_add_sync_preinstalled()
|
||
which can be implemented by GUI clients to drive the actual installs
|
||
on system startup.
|
||
|
||
Resolves: https://github.com/flatpak/flatpak/issues/5579
|
||
Co-authored-by: Sebastian Wick <sebastian.wick@redhat.com>
|
||
---
|
||
app/flatpak-builtins-preinstall.c | 156 ++++++++++
|
||
app/flatpak-builtins.h | 1 +
|
||
app/flatpak-main.c | 1 +
|
||
app/meson.build | 1 +
|
||
common/flatpak-dir-private.h | 23 +-
|
||
common/flatpak-dir.c | 406 +++++++++++++++++++++++++-
|
||
common/flatpak-installation.c | 2 +-
|
||
common/flatpak-transaction.c | 187 ++++++++++--
|
||
common/flatpak-transaction.h | 3 +
|
||
doc/flatpak-docs.xml.in | 1 +
|
||
doc/flatpak-preinstall.xml | 276 +++++++++++++++++
|
||
doc/meson.build | 1 +
|
||
po/POTFILES.in | 1 +
|
||
system-helper/flatpak-system-helper.c | 5 +-
|
||
tests/libtest.sh | 1 +
|
||
tests/test-basic.sh | 2 +-
|
||
tests/test-matrix/meson.build | 1 +
|
||
tests/test-preinstall.sh | 226 ++++++++++++++
|
||
18 files changed, 1264 insertions(+), 30 deletions(-)
|
||
create mode 100644 app/flatpak-builtins-preinstall.c
|
||
create mode 100644 doc/flatpak-preinstall.xml
|
||
create mode 100755 tests/test-preinstall.sh
|
||
|
||
diff --git a/app/flatpak-builtins-preinstall.c b/app/flatpak-builtins-preinstall.c
|
||
new file mode 100644
|
||
index 00000000..3d52cb8b
|
||
--- /dev/null
|
||
+++ b/app/flatpak-builtins-preinstall.c
|
||
@@ -0,0 +1,156 @@
|
||
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
|
||
+ * Copyright © 2024 Red Hat, Inc
|
||
+ *
|
||
+ * 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:
|
||
+ * Kalev Lember <klember@redhat.com>
|
||
+ */
|
||
+
|
||
+#include "config.h"
|
||
+
|
||
+#include <locale.h>
|
||
+#include <stdlib.h>
|
||
+#include <unistd.h>
|
||
+#include <string.h>
|
||
+
|
||
+#include <glib/gi18n.h>
|
||
+
|
||
+#include <gio/gunixinputstream.h>
|
||
+
|
||
+#include "libglnx.h"
|
||
+
|
||
+#include "flatpak-builtins.h"
|
||
+#include "flatpak-builtins-utils.h"
|
||
+#include "flatpak-transaction-private.h"
|
||
+#include "flatpak-cli-transaction.h"
|
||
+#include "flatpak-quiet-transaction.h"
|
||
+#include "flatpak-utils-http-private.h"
|
||
+#include "flatpak-utils-private.h"
|
||
+#include "flatpak-error.h"
|
||
+#include "flatpak-chain-input-stream-private.h"
|
||
+
|
||
+static char **opt_sideload_repos;
|
||
+static gboolean opt_no_pull;
|
||
+static gboolean opt_no_deploy;
|
||
+static gboolean opt_no_related;
|
||
+static gboolean opt_no_deps;
|
||
+static gboolean opt_no_static_deltas;
|
||
+static gboolean opt_include_sdk;
|
||
+static gboolean opt_include_debug;
|
||
+static gboolean opt_yes;
|
||
+static gboolean opt_reinstall;
|
||
+static gboolean opt_noninteractive;
|
||
+
|
||
+static GOptionEntry options[] = {
|
||
+ { "no-pull", 0, 0, G_OPTION_ARG_NONE, &opt_no_pull, N_("Don't pull, only install from local cache"), NULL },
|
||
+ { "no-deploy", 0, 0, G_OPTION_ARG_NONE, &opt_no_deploy, N_("Don't deploy, only download to local cache"), NULL },
|
||
+ { "no-related", 0, 0, G_OPTION_ARG_NONE, &opt_no_related, N_("Don't install related refs"), NULL },
|
||
+ { "no-deps", 0, 0, G_OPTION_ARG_NONE, &opt_no_deps, N_("Don't verify/install runtime dependencies"), NULL },
|
||
+ { "no-static-deltas", 0, 0, G_OPTION_ARG_NONE, &opt_no_static_deltas, N_("Don't use static deltas"), NULL },
|
||
+ { "include-sdk", 0, 0, G_OPTION_ARG_NONE, &opt_include_sdk, N_("Additionally install the SDK used to build the given refs") },
|
||
+ { "include-debug", 0, 0, G_OPTION_ARG_NONE, &opt_include_debug, N_("Additionally install the debug info for the given refs and their dependencies") },
|
||
+ { "assumeyes", 'y', 0, G_OPTION_ARG_NONE, &opt_yes, N_("Automatically answer yes for all questions"), NULL },
|
||
+ { "reinstall", 0, 0, G_OPTION_ARG_NONE, &opt_reinstall, N_("Uninstall first if already installed"), NULL },
|
||
+ { "noninteractive", 0, 0, G_OPTION_ARG_NONE, &opt_noninteractive, N_("Produce minimal output and don't ask questions"), NULL },
|
||
+ /* Translators: A sideload is when you install from a local USB drive rather than the Internet. */
|
||
+ { "sideload-repo", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_sideload_repos, N_("Use this local repo for sideloads"), N_("PATH") },
|
||
+ { NULL }
|
||
+};
|
||
+
|
||
+gboolean
|
||
+flatpak_builtin_preinstall (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||
+{
|
||
+ g_autoptr(GOptionContext) context = NULL;
|
||
+ g_autoptr(GPtrArray) dirs = NULL;
|
||
+ g_autoptr(FlatpakDir) dir = NULL;
|
||
+ g_autoptr(FlatpakTransaction) transaction = NULL;
|
||
+
|
||
+ context = g_option_context_new (_("- Install flatpaks that are part of the operating system"));
|
||
+ g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
|
||
+
|
||
+ if (!flatpak_option_context_parse (context, options, &argc, &argv,
|
||
+ FLATPAK_BUILTIN_FLAG_ALL_DIRS | FLATPAK_BUILTIN_FLAG_OPTIONAL_REPO,
|
||
+ &dirs, cancellable, error))
|
||
+ return FALSE;
|
||
+
|
||
+ /* Use the default dir */
|
||
+ dir = g_object_ref (g_ptr_array_index (dirs, 0));
|
||
+
|
||
+ if (opt_noninteractive)
|
||
+ opt_yes = TRUE; /* Implied */
|
||
+
|
||
+ if (opt_noninteractive)
|
||
+ transaction = flatpak_quiet_transaction_new (dir, error);
|
||
+ else
|
||
+ transaction = flatpak_cli_transaction_new (dir, opt_yes, TRUE, FALSE, error);
|
||
+ if (transaction == NULL)
|
||
+ return FALSE;
|
||
+
|
||
+ flatpak_transaction_set_no_pull (transaction, opt_no_pull);
|
||
+ flatpak_transaction_set_no_deploy (transaction, opt_no_deploy);
|
||
+ flatpak_transaction_set_disable_static_deltas (transaction, opt_no_static_deltas);
|
||
+ flatpak_transaction_set_disable_dependencies (transaction, opt_no_deps);
|
||
+ flatpak_transaction_set_disable_related (transaction, opt_no_related);
|
||
+ flatpak_transaction_set_reinstall (transaction, opt_reinstall);
|
||
+ flatpak_transaction_set_auto_install_sdk (transaction, opt_include_sdk);
|
||
+ flatpak_transaction_set_auto_install_debug (transaction, opt_include_debug);
|
||
+
|
||
+ for (int i = 0; opt_sideload_repos != NULL && opt_sideload_repos[i] != NULL; i++)
|
||
+ flatpak_transaction_add_sideload_repo (transaction, opt_sideload_repos[i]);
|
||
+
|
||
+ if (!flatpak_transaction_add_sync_preinstalled (transaction, error))
|
||
+ return FALSE;
|
||
+
|
||
+ if (flatpak_transaction_is_empty (transaction))
|
||
+ {
|
||
+ g_print (_("Nothing to do.\n"));
|
||
+
|
||
+ return TRUE;
|
||
+ }
|
||
+
|
||
+ if (!flatpak_transaction_run (transaction, cancellable, error))
|
||
+ {
|
||
+ if (g_error_matches (*error, FLATPAK_ERROR, FLATPAK_ERROR_ABORTED))
|
||
+ g_clear_error (error); /* Don't report on stderr */
|
||
+
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+gboolean
|
||
+flatpak_complete_preinstall (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)
|
||
+ {
|
||
+ default: /* REF */
|
||
+ 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 32a9c952..20eb2272 100644
|
||
--- a/app/flatpak-builtins.h
|
||
+++ b/app/flatpak-builtins.h
|
||
@@ -127,6 +127,7 @@ BUILTINPROTO (repair)
|
||
BUILTINPROTO (create_usb)
|
||
BUILTINPROTO (kill)
|
||
BUILTINPROTO (history)
|
||
+BUILTINPROTO (preinstall)
|
||
|
||
#undef BUILTINPROTO
|
||
|
||
diff --git a/app/flatpak-main.c b/app/flatpak-main.c
|
||
index 45498895..d5794d99 100644
|
||
--- a/app/flatpak-main.c
|
||
+++ b/app/flatpak-main.c
|
||
@@ -89,6 +89,7 @@ static FlatpakCommand commands[] = {
|
||
{ "config", N_("Configure flatpak"), flatpak_builtin_config, flatpak_complete_config },
|
||
{ "repair", N_("Repair flatpak installation"), flatpak_builtin_repair, flatpak_complete_repair },
|
||
{ "create-usb", N_("Put applications or runtimes onto removable media"), flatpak_builtin_create_usb, flatpak_complete_create_usb },
|
||
+ { "preinstall", N_("Install flatpaks that are part of the operating system"), flatpak_builtin_preinstall, flatpak_complete_preinstall },
|
||
|
||
/* translators: please keep the leading newline and space */
|
||
{ N_("\n Find applications and runtimes") },
|
||
diff --git a/app/meson.build b/app/meson.build
|
||
index 258d582a..8e6ef1dd 100644
|
||
--- a/app/meson.build
|
||
+++ b/app/meson.build
|
||
@@ -100,6 +100,7 @@ sources = [
|
||
'flatpak-builtins-permission-set.c',
|
||
'flatpak-builtins-permission-show.c',
|
||
'flatpak-builtins-pin.c',
|
||
+ 'flatpak-builtins-preinstall.c',
|
||
'flatpak-builtins-ps.c',
|
||
'flatpak-builtins-remote-add.c',
|
||
'flatpak-builtins-remote-delete.c',
|
||
diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h
|
||
index ffcff5ff..2c75890c 100644
|
||
--- a/common/flatpak-dir-private.h
|
||
+++ b/common/flatpak-dir-private.h
|
||
@@ -187,6 +187,7 @@ typedef enum {
|
||
FLATPAK_HELPER_DEPLOY_FLAGS_APP_HINT = 1 << 5,
|
||
FLATPAK_HELPER_DEPLOY_FLAGS_INSTALL_HINT = 1 << 6,
|
||
FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PINNED = 1 << 7,
|
||
+ FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PREINSTALLED = 1 << 8,
|
||
} FlatpakHelperDeployFlags;
|
||
|
||
#define FLATPAK_HELPER_DEPLOY_FLAGS_ALL (FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE | \
|
||
@@ -196,18 +197,21 @@ typedef enum {
|
||
FLATPAK_HELPER_DEPLOY_FLAGS_NO_INTERACTION | \
|
||
FLATPAK_HELPER_DEPLOY_FLAGS_APP_HINT | \
|
||
FLATPAK_HELPER_DEPLOY_FLAGS_INSTALL_HINT | \
|
||
- FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PINNED)
|
||
+ FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PINNED | \
|
||
+ FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PREINSTALLED)
|
||
|
||
typedef enum {
|
||
FLATPAK_HELPER_UNINSTALL_FLAGS_NONE = 0,
|
||
FLATPAK_HELPER_UNINSTALL_FLAGS_KEEP_REF = 1 << 0,
|
||
FLATPAK_HELPER_UNINSTALL_FLAGS_FORCE_REMOVE = 1 << 1,
|
||
FLATPAK_HELPER_UNINSTALL_FLAGS_NO_INTERACTION = 1 << 2,
|
||
+ FLATPAK_HELPER_UNINSTALL_FLAGS_UPDATE_PREINSTALLED = 1 << 3,
|
||
} FlatpakHelperUninstallFlags;
|
||
|
||
#define FLATPAK_HELPER_UNINSTALL_FLAGS_ALL (FLATPAK_HELPER_UNINSTALL_FLAGS_KEEP_REF | \
|
||
FLATPAK_HELPER_UNINSTALL_FLAGS_FORCE_REMOVE | \
|
||
- FLATPAK_HELPER_UNINSTALL_FLAGS_NO_INTERACTION)
|
||
+ FLATPAK_HELPER_UNINSTALL_FLAGS_NO_INTERACTION | \
|
||
+ FLATPAK_HELPER_UNINSTALL_FLAGS_UPDATE_PREINSTALLED)
|
||
|
||
typedef enum {
|
||
FLATPAK_HELPER_CONFIGURE_REMOTE_FLAGS_NONE = 0,
|
||
@@ -371,6 +375,19 @@ gboolean flatpak_remove_override_keyfile (const char *app_id,
|
||
gboolean user,
|
||
GError **error);
|
||
|
||
+typedef struct
|
||
+{
|
||
+ FlatpakDecomposed *ref;
|
||
+ char *collection_id;
|
||
+} FlatpakPreinstallConfig;
|
||
+
|
||
+GPtrArray * flatpak_get_preinstall_config (const char *default_arch,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error);
|
||
+gboolean flatpak_dir_uninitialized_mark_preinstalled (FlatpakDir *self,
|
||
+ const GPtrArray *preinstall_config,
|
||
+ GError **error);
|
||
+
|
||
int flatpak_deploy_data_get_version (GBytes *deploy_data);
|
||
const char * flatpak_deploy_data_get_origin (GBytes *deploy_data);
|
||
const char * flatpak_deploy_data_get_commit (GBytes *deploy_data);
|
||
@@ -687,6 +704,7 @@ gboolean flatpak_dir_deploy_install (Fla
|
||
const char **previous_ids,
|
||
gboolean reinstall,
|
||
gboolean pin_on_deploy,
|
||
+ gboolean update_preinstalled_on_deploy,
|
||
GCancellable *cancellable,
|
||
GError **error);
|
||
gboolean flatpak_dir_install (FlatpakDir *self,
|
||
@@ -696,6 +714,7 @@ gboolean flatpak_dir_install (Fla
|
||
gboolean reinstall,
|
||
gboolean app_hint,
|
||
gboolean pin_on_deploy,
|
||
+ gboolean update_preinstalled_on_deploy,
|
||
FlatpakRemoteState *state,
|
||
FlatpakDecomposed *ref,
|
||
const char *opt_commit,
|
||
diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c
|
||
index 1c304525..6300f3cc 100644
|
||
--- a/common/flatpak-dir.c
|
||
+++ b/common/flatpak-dir.c
|
||
@@ -86,6 +86,16 @@
|
||
#define FLATPAK_REMOTES_DIR "remotes.d"
|
||
#define FLATPAK_REMOTES_FILE_EXT ".flatpakrepo"
|
||
|
||
+#define FLATPAK_PREINSTALL_DIR "preinstall.d"
|
||
+#define FLATPAK_PREINSTALL_FILE_EXT ".preinstall"
|
||
+
|
||
+#define FLATPAK_PREINSTALL_GROUP_PREFIX "Flatpak Preinstall "
|
||
+#define FLATPAK_PREINSTALL_IS_RUNTIME_KEY "IsRuntime"
|
||
+#define FLATPAK_PREINSTALL_NAME_KEY "Name"
|
||
+#define FLATPAK_PREINSTALL_BRANCH_KEY "Branch"
|
||
+#define FLATPAK_PREINSTALL_COLLECTION_ID_KEY "CollectionID"
|
||
+#define FLATPAK_PREINSTALL_INSTALL_KEY "Install"
|
||
+
|
||
#define SIDELOAD_REPOS_DIR_NAME "sideload-repos"
|
||
|
||
#define FLATPAK_TRIGGERS_DIR "triggers"
|
||
@@ -2043,6 +2053,381 @@ get_system_locations (GCancellable *cancellable,
|
||
return g_steal_pointer (&locations);
|
||
}
|
||
|
||
+typedef struct
|
||
+{
|
||
+ char *name;
|
||
+ char *branch;
|
||
+ gboolean is_runtime;
|
||
+ char *collection_id;
|
||
+ gboolean install;
|
||
+} PreinstallConfig;
|
||
+
|
||
+static PreinstallConfig *
|
||
+preinstall_config_new (const char *name)
|
||
+{
|
||
+ PreinstallConfig *config = g_new0 (PreinstallConfig, 1);
|
||
+
|
||
+ config->name = g_strdup (name);
|
||
+ config->branch = g_strdup ("master");
|
||
+ config->install = TRUE;
|
||
+
|
||
+ return config;
|
||
+}
|
||
+
|
||
+static void
|
||
+preinstall_config_free (PreinstallConfig *config)
|
||
+{
|
||
+ g_clear_pointer (&config->name, g_free);
|
||
+ g_clear_pointer (&config->branch, g_free);
|
||
+ g_clear_pointer (&config->collection_id, g_free);
|
||
+ g_free (config);
|
||
+}
|
||
+
|
||
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (PreinstallConfig, preinstall_config_free)
|
||
+
|
||
+static void
|
||
+flatpak_preinstall_config_free (FlatpakPreinstallConfig *preinstall)
|
||
+{
|
||
+ g_clear_pointer (&preinstall->ref, flatpak_decomposed_unref);
|
||
+ g_clear_pointer (&preinstall->collection_id, g_free);
|
||
+ g_free (preinstall);
|
||
+}
|
||
+
|
||
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakPreinstallConfig, flatpak_preinstall_config_free)
|
||
+
|
||
+static void
|
||
+flatpak_parse_preinstall_config_file (GKeyFile *keyfile,
|
||
+ GHashTable *configs)
|
||
+{
|
||
+ g_auto(GStrv) groups = NULL;
|
||
+
|
||
+ groups = g_key_file_get_groups (keyfile, NULL);
|
||
+
|
||
+ for (int i = 0; groups[i] != NULL; i++)
|
||
+ {
|
||
+ const char *group_name = groups[i];
|
||
+ const char *name;
|
||
+ g_autoptr(PreinstallConfig) config = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ g_autofree char *owned_name = NULL;
|
||
+ g_autofree char *branch = NULL;
|
||
+ gboolean is_runtime = FALSE;
|
||
+ g_autofree char *collection_id = NULL;
|
||
+ gboolean install = TRUE;
|
||
+
|
||
+ if (!g_str_has_prefix (group_name, FLATPAK_PREINSTALL_GROUP_PREFIX) ||
|
||
+ *(group_name + strlen (FLATPAK_PREINSTALL_GROUP_PREFIX)) == '\0')
|
||
+ {
|
||
+ g_info ("Skipping unknown group %s", group_name);
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ name = group_name + strlen (FLATPAK_PREINSTALL_GROUP_PREFIX);
|
||
+
|
||
+ if (!g_hash_table_steal_extended (configs, name,
|
||
+ (gpointer *)&owned_name,
|
||
+ (gpointer *)&config))
|
||
+ {
|
||
+ config = preinstall_config_new (name);
|
||
+ owned_name = g_strdup (name);
|
||
+ }
|
||
+
|
||
+ branch = g_key_file_get_string (keyfile,
|
||
+ group_name,
|
||
+ FLATPAK_PREINSTALL_BRANCH_KEY,
|
||
+ NULL);
|
||
+ if (branch)
|
||
+ {
|
||
+ g_clear_pointer (&config->branch, g_free);
|
||
+ if (*branch != '\0')
|
||
+ config->branch = g_steal_pointer (&branch);
|
||
+ }
|
||
+
|
||
+ is_runtime = g_key_file_get_boolean (keyfile,
|
||
+ group_name,
|
||
+ FLATPAK_PREINSTALL_IS_RUNTIME_KEY,
|
||
+ &local_error);
|
||
+ if (!local_error)
|
||
+ {
|
||
+ config->is_runtime = is_runtime;
|
||
+ }
|
||
+ else if (!g_error_matches (local_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
|
||
+ {
|
||
+ g_info ("Invalid file format, %s is not a boolean",
|
||
+ FLATPAK_PREINSTALL_IS_RUNTIME_KEY);
|
||
+ continue;
|
||
+ }
|
||
+ g_clear_error (&local_error);
|
||
+
|
||
+ collection_id = g_key_file_get_string (keyfile,
|
||
+ group_name,
|
||
+ FLATPAK_PREINSTALL_COLLECTION_ID_KEY,
|
||
+ NULL);
|
||
+ if (collection_id)
|
||
+ {
|
||
+ g_clear_pointer (&config->collection_id, g_free);
|
||
+ if (*collection_id != '\0')
|
||
+ config->collection_id = g_steal_pointer (&collection_id);
|
||
+ }
|
||
+
|
||
+ install = g_key_file_get_boolean (keyfile,
|
||
+ group_name,
|
||
+ FLATPAK_PREINSTALL_INSTALL_KEY,
|
||
+ &local_error);
|
||
+ if (!local_error)
|
||
+ {
|
||
+ config->install = install;
|
||
+ }
|
||
+ else if (!g_error_matches (local_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
|
||
+ {
|
||
+ g_info ("Invalid file format, %s is not a boolean",
|
||
+ FLATPAK_PREINSTALL_INSTALL_KEY);
|
||
+ continue;
|
||
+ }
|
||
+ g_clear_error (&local_error);
|
||
+
|
||
+ g_hash_table_insert (configs,
|
||
+ g_steal_pointer (&owned_name),
|
||
+ g_steal_pointer (&config));
|
||
+ }
|
||
+}
|
||
+
|
||
+typedef struct
|
||
+{
|
||
+ char *name;
|
||
+ GFile *file;
|
||
+} PreinstallConfigFile;
|
||
+
|
||
+static gint
|
||
+preinstall_config_file_sort (gconstpointer a,
|
||
+ gconstpointer b)
|
||
+{
|
||
+ const PreinstallConfigFile *ca = a;
|
||
+ const PreinstallConfigFile *cb = b;
|
||
+
|
||
+ return g_strcmp0 (ca->name, cb->name);
|
||
+}
|
||
+
|
||
+static void
|
||
+preinstall_config_file_free (PreinstallConfigFile *preinstall_config_file)
|
||
+{
|
||
+ g_clear_pointer (&preinstall_config_file->name, g_free);
|
||
+ g_clear_object (&preinstall_config_file->file);
|
||
+ g_free (preinstall_config_file);
|
||
+}
|
||
+
|
||
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (PreinstallConfigFile, preinstall_config_file_free);
|
||
+
|
||
+static gboolean
|
||
+scan_preinstall_config_files (const char *config_dir,
|
||
+ GHashTable *configs,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GFile) conf_dir = NULL;
|
||
+ g_autoptr(GFileEnumerator) dir_enum = NULL;
|
||
+ g_autoptr(GPtrArray) config_files = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ if (!g_file_test (config_dir, G_FILE_TEST_IS_DIR))
|
||
+ {
|
||
+ g_info ("Skipping missing preinstall config directory %s", config_dir);
|
||
+ return TRUE;
|
||
+ }
|
||
+
|
||
+ conf_dir = g_file_new_for_path (config_dir);
|
||
+ dir_enum = g_file_enumerate_children (conf_dir,
|
||
+ G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||
+ G_FILE_QUERY_INFO_NONE,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ if (local_error != NULL)
|
||
+ {
|
||
+ g_info ("Unexpected error retrieving preinstalls from %s: %s",
|
||
+ config_dir,
|
||
+ local_error->message);
|
||
+
|
||
+ g_propagate_error (error, g_steal_pointer (&local_error));
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ config_files = g_ptr_array_new_with_free_func ((GDestroyNotify)preinstall_config_file_free);
|
||
+
|
||
+ while (TRUE)
|
||
+ {
|
||
+ GFileInfo *file_info;
|
||
+ GFile *path;
|
||
+ const char *name;
|
||
+ guint32 type;
|
||
+ g_autoptr(PreinstallConfigFile) config_file = NULL;
|
||
+
|
||
+ if (!g_file_enumerator_iterate (dir_enum,
|
||
+ &file_info,
|
||
+ &path,
|
||
+ cancellable,
|
||
+ &local_error))
|
||
+ {
|
||
+ g_info ("Unexpected error reading file in %s: %s",
|
||
+ config_dir,
|
||
+ local_error->message);
|
||
+
|
||
+ g_propagate_error (error, g_steal_pointer (&local_error));
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ if (file_info == NULL)
|
||
+ break;
|
||
+
|
||
+ name = g_file_info_get_name (file_info);
|
||
+ type = g_file_info_get_file_type (file_info);
|
||
+
|
||
+ if (type != G_FILE_TYPE_REGULAR ||
|
||
+ !g_str_has_suffix (name, FLATPAK_PREINSTALL_FILE_EXT))
|
||
+ continue;
|
||
+
|
||
+ config_file = g_new0 (PreinstallConfigFile, 1);
|
||
+ config_file->name = g_strdup (name);
|
||
+ config_file->file = g_object_ref (path);
|
||
+ g_ptr_array_add (config_files, g_steal_pointer (&config_file));
|
||
+ }
|
||
+
|
||
+ g_ptr_array_sort (config_files, preinstall_config_file_sort);
|
||
+
|
||
+ for (int i = 0; i < config_files->len; i++)
|
||
+ {
|
||
+ PreinstallConfigFile *config_file = g_ptr_array_index (config_files, i);
|
||
+ g_autofree char *path = NULL;
|
||
+ g_autoptr(GKeyFile) keyfile = NULL;
|
||
+ g_autoptr(GError) load_error = NULL;
|
||
+
|
||
+ path = g_file_get_path (config_file->file);
|
||
+
|
||
+ g_info ("Parsing config file %s", path);
|
||
+
|
||
+ keyfile = g_key_file_new ();
|
||
+
|
||
+ if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, &load_error))
|
||
+ g_info ("Parsing config file %s failed: %s", path, load_error->message);
|
||
+
|
||
+ flatpak_parse_preinstall_config_file (keyfile, configs);
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+GPtrArray *
|
||
+flatpak_get_preinstall_config (const char *default_arch,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GHashTable) configs = NULL;
|
||
+ g_autoptr(GPtrArray) preinstalls = NULL;
|
||
+ g_autofree char *config_dir = NULL;
|
||
+ g_autofree char *data_dir = NULL;
|
||
+ GHashTableIter iter;
|
||
+ PreinstallConfig *config;
|
||
+
|
||
+ configs = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||
+ g_free, (GDestroyNotify)preinstall_config_free);
|
||
+
|
||
+ /* scan directories in reverse priority order */
|
||
+ data_dir = g_build_filename (get_data_dir_location (), FLATPAK_PREINSTALL_DIR, NULL);
|
||
+ if (!scan_preinstall_config_files (data_dir, configs, cancellable, error))
|
||
+ return NULL;
|
||
+
|
||
+ config_dir = g_build_filename (get_config_dir_location (), FLATPAK_PREINSTALL_DIR, NULL);
|
||
+ if (!scan_preinstall_config_files (config_dir, configs, cancellable, error))
|
||
+ return NULL;
|
||
+
|
||
+ preinstalls = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_preinstall_config_free);
|
||
+
|
||
+ g_hash_table_iter_init (&iter, configs);
|
||
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&config))
|
||
+ {
|
||
+ g_autoptr(FlatpakPreinstallConfig) preinstall = NULL;
|
||
+ g_autoptr(FlatpakDecomposed) ref = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ if (!config->install)
|
||
+ {
|
||
+ g_info ("Skipping preinstall of %s because it is configured to not install",
|
||
+ config->name);
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ ref = flatpak_decomposed_new_from_parts (config->is_runtime ?
|
||
+ FLATPAK_KINDS_RUNTIME :
|
||
+ FLATPAK_KINDS_APP,
|
||
+ config->name,
|
||
+ default_arch,
|
||
+ config->branch,
|
||
+ &local_error);
|
||
+ if (ref == NULL)
|
||
+ {
|
||
+ g_info ("Skipping preinstall of %s because of problems in the configuration: %s",
|
||
+ config->name,
|
||
+ local_error->message);
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ preinstall = g_new0 (FlatpakPreinstallConfig, 1);
|
||
+ preinstall->ref = g_steal_pointer (&ref);
|
||
+ preinstall->collection_id = g_strdup (config->collection_id);
|
||
+
|
||
+ g_info ("Found preinstall ref %s",
|
||
+ flatpak_decomposed_get_ref (preinstall->ref));
|
||
+
|
||
+ g_ptr_array_add (preinstalls, g_steal_pointer (&preinstall));
|
||
+ }
|
||
+
|
||
+ return g_steal_pointer (&preinstalls);
|
||
+}
|
||
+
|
||
+gboolean
|
||
+flatpak_dir_uninitialized_mark_preinstalled (FlatpakDir *self,
|
||
+ const GPtrArray *preinstall_config,
|
||
+ GError **error)
|
||
+{
|
||
+ const char *existing_preinstalls;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ existing_preinstalls = flatpak_dir_get_config (self,
|
||
+ "preinstalled",
|
||
+ &local_error);
|
||
+
|
||
+ if (existing_preinstalls != NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ if (!g_error_matches (local_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
|
||
+ {
|
||
+ g_propagate_error (error, g_steal_pointer (&local_error));
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ g_clear_error (&local_error);
|
||
+
|
||
+ for (int i = 0; i < preinstall_config->len; i++)
|
||
+ {
|
||
+ const FlatpakPreinstallConfig *config = g_ptr_array_index (preinstall_config, i);
|
||
+ GError **append_error = local_error == NULL ? &local_error : NULL;
|
||
+
|
||
+ flatpak_dir_config_append_pattern (self,
|
||
+ "preinstalled",
|
||
+ flatpak_decomposed_get_ref (config->ref),
|
||
+ FALSE,
|
||
+ NULL,
|
||
+ append_error);
|
||
+ }
|
||
+
|
||
+ if (local_error)
|
||
+ {
|
||
+ g_propagate_error (error, g_steal_pointer (&local_error));
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
GPtrArray *
|
||
flatpak_get_system_base_dir_locations (GCancellable *cancellable,
|
||
GError **error)
|
||
@@ -9255,6 +9640,7 @@ flatpak_dir_deploy_install (FlatpakDir *self,
|
||
const char **previous_ids,
|
||
gboolean reinstall,
|
||
gboolean pin_on_deploy,
|
||
+ gboolean update_preinstalled_on_deploy,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
@@ -9360,6 +9746,14 @@ flatpak_dir_deploy_install (FlatpakDir *self,
|
||
TRUE, NULL, error))
|
||
goto out;
|
||
|
||
+ /* Save preinstalled refs to keep the data on what is user installed and what
|
||
+ * is automatically installed. */
|
||
+ if (update_preinstalled_on_deploy &&
|
||
+ !flatpak_dir_config_append_pattern (self, "preinstalled",
|
||
+ flatpak_decomposed_get_ref (ref),
|
||
+ FALSE, NULL, error))
|
||
+ goto out;
|
||
+
|
||
ret = TRUE;
|
||
|
||
commit = flatpak_dir_read_active (self, ref, cancellable);
|
||
@@ -9960,6 +10354,7 @@ flatpak_dir_install (FlatpakDir *self,
|
||
gboolean reinstall,
|
||
gboolean app_hint,
|
||
gboolean pin_on_deploy,
|
||
+ gboolean update_preinstalled_on_deploy,
|
||
FlatpakRemoteState *state,
|
||
FlatpakDecomposed *ref,
|
||
const char *opt_commit,
|
||
@@ -10174,6 +10569,9 @@ flatpak_dir_install (FlatpakDir *self,
|
||
if (pin_on_deploy)
|
||
helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PINNED;
|
||
|
||
+ if (update_preinstalled_on_deploy)
|
||
+ helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PREINSTALLED;
|
||
+
|
||
helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_INSTALL_HINT;
|
||
|
||
if (!flatpak_dir_system_helper_call_deploy (self,
|
||
@@ -10212,6 +10610,7 @@ flatpak_dir_install (FlatpakDir *self,
|
||
{
|
||
if (!flatpak_dir_deploy_install (self, ref, state->remote_name, opt_subpaths,
|
||
opt_previous_ids, reinstall, pin_on_deploy,
|
||
+ update_preinstalled_on_deploy,
|
||
cancellable, error))
|
||
return FALSE;
|
||
|
||
@@ -10502,7 +10901,7 @@ flatpak_dir_install_bundle (FlatpakDir *self,
|
||
}
|
||
else
|
||
{
|
||
- if (!flatpak_dir_deploy_install (self, ref, remote, NULL, NULL, FALSE, FALSE, cancellable, error))
|
||
+ if (!flatpak_dir_deploy_install (self, ref, remote, NULL, NULL, FALSE, FALSE, FALSE, cancellable, error))
|
||
return FALSE;
|
||
}
|
||
|
||
@@ -10933,6 +11332,7 @@ flatpak_dir_uninstall (FlatpakDir *self,
|
||
g_autoptr(GBytes) deploy_data = NULL;
|
||
gboolean keep_ref = flags & FLATPAK_HELPER_UNINSTALL_FLAGS_KEEP_REF;
|
||
gboolean force_remove = flags & FLATPAK_HELPER_UNINSTALL_FLAGS_FORCE_REMOVE;
|
||
+ gboolean update_preinstalled = flags & FLATPAK_HELPER_UNINSTALL_FLAGS_UPDATE_PREINSTALLED;
|
||
|
||
name = flatpak_decomposed_dup_id (ref);
|
||
|
||
@@ -11039,6 +11439,10 @@ flatpak_dir_uninstall (FlatpakDir *self,
|
||
if (!flatpak_dir_mark_changed (self, error))
|
||
return FALSE;
|
||
|
||
+ if (update_preinstalled &&
|
||
+ !flatpak_dir_config_remove_pattern (self, "preinstalled", flatpak_decomposed_get_ref (ref), error))
|
||
+ return FALSE;
|
||
+
|
||
if (!was_deployed)
|
||
{
|
||
const char *branch = flatpak_decomposed_get_branch (ref);
|
||
diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c
|
||
index 3a8677e8..be706174 100644
|
||
--- a/common/flatpak-installation.c
|
||
+++ b/common/flatpak-installation.c
|
||
@@ -1929,7 +1929,7 @@ flatpak_installation_install_full (FlatpakInstallation *self,
|
||
(flags & FLATPAK_INSTALL_FLAGS_NO_PULL) != 0,
|
||
(flags & FLATPAK_INSTALL_FLAGS_NO_DEPLOY) != 0,
|
||
(flags & FLATPAK_INSTALL_FLAGS_NO_STATIC_DELTAS) != 0,
|
||
- FALSE, FALSE, FALSE, state,
|
||
+ FALSE, FALSE, FALSE, FALSE, state,
|
||
ref, NULL, (const char **) subpaths, NULL, NULL, NULL, NULL, NULL,
|
||
progress, cancellable, error))
|
||
return NULL;
|
||
diff --git a/common/flatpak-transaction.c b/common/flatpak-transaction.c
|
||
index b498e4d4..22b7ad8c 100644
|
||
--- a/common/flatpak-transaction.c
|
||
+++ b/common/flatpak-transaction.c
|
||
@@ -116,6 +116,7 @@ struct _FlatpakTransactionOperation
|
||
gboolean skip;
|
||
gboolean update_only_deploy;
|
||
gboolean pin_on_deploy;
|
||
+ gboolean update_preinstalled_on_deploy;
|
||
|
||
gboolean resolved;
|
||
char *resolved_commit;
|
||
@@ -701,7 +702,8 @@ flatpak_transaction_operation_new (const char *remote,
|
||
const char *commit,
|
||
GFile *bundle,
|
||
FlatpakTransactionOperationType kind,
|
||
- gboolean pin_on_deploy)
|
||
+ gboolean pin_on_deploy,
|
||
+ gboolean update_preinstalled_on_deploy)
|
||
{
|
||
FlatpakTransactionOperation *self;
|
||
|
||
@@ -716,6 +718,7 @@ flatpak_transaction_operation_new (const char *remote,
|
||
self->bundle = g_object_ref (bundle);
|
||
self->kind = kind;
|
||
self->pin_on_deploy = pin_on_deploy;
|
||
+ self->update_preinstalled_on_deploy = update_preinstalled_on_deploy;
|
||
|
||
return self;
|
||
}
|
||
@@ -2176,7 +2179,8 @@ flatpak_transaction_add_op (FlatpakTransaction *self,
|
||
const char *commit,
|
||
GFile *bundle,
|
||
FlatpakTransactionOperationType kind,
|
||
- gboolean pin_on_deploy)
|
||
+ gboolean pin_on_deploy,
|
||
+ gboolean update_preinstalled_on_deploy)
|
||
{
|
||
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
|
||
FlatpakTransactionOperation *op;
|
||
@@ -2206,7 +2210,8 @@ flatpak_transaction_add_op (FlatpakTransaction *self,
|
||
}
|
||
|
||
op = flatpak_transaction_operation_new (remote, ref, subpaths, previous_ids,
|
||
- commit, bundle, kind, pin_on_deploy);
|
||
+ commit, bundle, kind, pin_on_deploy,
|
||
+ update_preinstalled_on_deploy);
|
||
g_hash_table_insert (priv->last_op_for_ref, flatpak_decomposed_ref (ref), op);
|
||
|
||
priv->ops = g_list_prepend (priv->ops, op);
|
||
@@ -2314,7 +2319,7 @@ add_related (FlatpakTransaction *self,
|
||
related_op = flatpak_transaction_add_op (self, rel->remote, rel->ref,
|
||
NULL, NULL, NULL, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_UNINSTALL,
|
||
- FALSE);
|
||
+ FALSE, FALSE);
|
||
related_op->non_fatal = TRUE;
|
||
related_op->fail_if_op_fails = op;
|
||
flatpak_transaction_operation_add_related_to_op (related_op, op);
|
||
@@ -2343,7 +2348,7 @@ add_related (FlatpakTransaction *self,
|
||
(const char **) rel->subpaths,
|
||
NULL, NULL, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE,
|
||
- FALSE);
|
||
+ FALSE, FALSE);
|
||
related_op->non_fatal = TRUE;
|
||
related_op->fail_if_op_fails = op;
|
||
flatpak_transaction_operation_add_related_to_op (related_op, op);
|
||
@@ -2574,7 +2579,7 @@ add_new_dep_op (FlatpakTransaction *self,
|
||
return FALSE;
|
||
|
||
*dep_op = flatpak_transaction_add_op (self, dep_remote, dep_ref, NULL, NULL, NULL, NULL,
|
||
- FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE, FALSE);
|
||
+ FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE, FALSE, FALSE);
|
||
}
|
||
else
|
||
{
|
||
@@ -2584,7 +2589,7 @@ add_new_dep_op (FlatpakTransaction *self,
|
||
g_info ("Updating dependency %s of %s", flatpak_decomposed_get_pref (dep_ref),
|
||
flatpak_decomposed_get_pref (op->ref));
|
||
*dep_op = flatpak_transaction_add_op (self, dep_remote, dep_ref, NULL, NULL, NULL, NULL,
|
||
- FLATPAK_TRANSACTION_OPERATION_UPDATE, FALSE);
|
||
+ FLATPAK_TRANSACTION_OPERATION_UPDATE, FALSE, FALSE);
|
||
(*dep_op)->non_fatal = TRUE;
|
||
}
|
||
}
|
||
@@ -2680,6 +2685,7 @@ flatpak_transaction_add_ref (FlatpakTransaction *self,
|
||
FlatpakImageSource *image_source,
|
||
const char *external_metadata,
|
||
gboolean pin_on_deploy,
|
||
+ gboolean update_preinstalled_on_deploy,
|
||
FlatpakTransactionOperation **out_op,
|
||
GError **error)
|
||
{
|
||
@@ -2803,7 +2809,8 @@ flatpak_transaction_add_ref (FlatpakTransaction *self,
|
||
}
|
||
|
||
op = flatpak_transaction_add_op (self, remote, ref, subpaths, previous_ids,
|
||
- commit, bundle, kind, pin_on_deploy);
|
||
+ commit, bundle, kind, pin_on_deploy,
|
||
+ update_preinstalled_on_deploy);
|
||
|
||
if (image_source)
|
||
op->image_source = g_object_ref (image_source);
|
||
@@ -2861,7 +2868,7 @@ flatpak_transaction_add_install (FlatpakTransaction *self,
|
||
|
||
if (!flatpak_transaction_add_ref (self, remote, decomposed, subpaths, NULL, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_INSTALL,
|
||
- NULL, NULL, NULL, pin_on_deploy, NULL, error))
|
||
+ NULL, NULL, NULL, pin_on_deploy, FALSE, NULL, error))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
@@ -2921,7 +2928,9 @@ flatpak_transaction_add_rebase (FlatpakTransaction *self,
|
||
if (dir_ref_is_installed (priv->dir, decomposed, &installed_origin, NULL))
|
||
remote = installed_origin;
|
||
|
||
- return flatpak_transaction_add_ref (self, remote, decomposed, subpaths, previous_ids, NULL, FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE, NULL, NULL, NULL, FALSE, NULL, error);
|
||
+ return flatpak_transaction_add_ref (self, remote, decomposed, subpaths, previous_ids, NULL,
|
||
+ FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE,
|
||
+ NULL, NULL, NULL, FALSE, FALSE, NULL, error);
|
||
}
|
||
|
||
/**
|
||
@@ -2997,25 +3006,22 @@ flatpak_transaction_add_rebase_and_uninstall (FlatpakTransaction *self,
|
||
if (!flatpak_transaction_add_ref (self, remote, new_decomposed, subpaths,
|
||
previous_ids, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE,
|
||
- NULL, NULL, NULL, FALSE, &rebase_op, error))
|
||
+ NULL, NULL, NULL, FALSE, FALSE, &rebase_op, error))
|
||
return FALSE;
|
||
|
||
if (!flatpak_transaction_add_ref (self, NULL, old_decomposed, NULL, NULL, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_UNINSTALL,
|
||
- NULL, NULL, NULL, FALSE, &uninstall_op, &local_error))
|
||
+ NULL, NULL, NULL, FALSE, FALSE, &uninstall_op, &local_error))
|
||
{
|
||
/* If the user is trying to install an eol-rebased app from scratch, the
|
||
* @old_ref can’t be uninstalled because it’s not installed already.
|
||
* Silently ignore that. */
|
||
- if (g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED))
|
||
- {
|
||
- g_clear_error (&local_error);
|
||
- }
|
||
- else
|
||
+ if (!g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED))
|
||
{
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
+ g_clear_error (&local_error);
|
||
}
|
||
|
||
/* Link the ops together so that the install/update is done first, and if
|
||
@@ -3119,6 +3125,134 @@ flatpak_transaction_add_install_flatpakref (FlatpakTransaction *self,
|
||
return TRUE;
|
||
}
|
||
|
||
+/**
|
||
+ * flatpak_transaction_add_sync_preinstalled:
|
||
+ * @self: a #FlatpakTransaction
|
||
+ * @error: return location for a #GError
|
||
+ *
|
||
+ * Adds preinstall operations to this transaction. This can involve both
|
||
+ * installing and removing refs, based on /etc/preinstall.d contents and what
|
||
+ * the system had preinstalled before.
|
||
+ *
|
||
+ * Returns: %TRUE on success; %FALSE with @error set on failure.
|
||
+ */
|
||
+gboolean
|
||
+flatpak_transaction_add_sync_preinstalled (FlatpakTransaction *self,
|
||
+ GError **error)
|
||
+{
|
||
+ FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
|
||
+ g_autoptr(GPtrArray) install_refs = g_ptr_array_new_with_free_func (g_free);
|
||
+ g_autoptr(GPtrArray) preinstalled_refs = NULL;
|
||
+ g_auto(GStrv) remotes = NULL;
|
||
+ g_autoptr(GPtrArray) configs = NULL;
|
||
+
|
||
+ remotes = flatpak_dir_list_remotes (priv->dir, NULL, error);
|
||
+ if (remotes == NULL)
|
||
+ return FALSE;
|
||
+
|
||
+ preinstalled_refs = flatpak_dir_get_config_patterns (priv->dir, "preinstalled");
|
||
+
|
||
+ configs = flatpak_get_preinstall_config (priv->default_arch, NULL, error);
|
||
+ if (configs == NULL)
|
||
+ return FALSE;
|
||
+
|
||
+ /* If the system has not had any apps pre-installed (i.e. the key in the
|
||
+ * config is missing) we mark all apps we would pre-install as pre-installed.
|
||
+ * This makes sure we will uninstall them when the config says that they no
|
||
+ * longer should be installed. */
|
||
+ if (!flatpak_dir_uninitialized_mark_preinstalled (priv->dir, configs, NULL))
|
||
+ g_message (_("Warning: Could not mark already installed apps as preinstalled"));
|
||
+
|
||
+ /* Find preinstalls that should get installed */
|
||
+ for (int i = 0; i < configs->len; i++)
|
||
+ {
|
||
+ const FlatpakPreinstallConfig *config = g_ptr_array_index (configs, i);
|
||
+
|
||
+ /* Store for later */
|
||
+ g_ptr_array_add (install_refs, flatpak_decomposed_dup_ref (config->ref));
|
||
+
|
||
+ /* Skip over if it's listed as previously preinstalled - it's now under
|
||
+ * user's control and we no longer install it again, even if the user
|
||
+ * manually removes it. */
|
||
+ if (!priv->reinstall &&
|
||
+ flatpak_g_ptr_array_contains_string (preinstalled_refs,
|
||
+ flatpak_decomposed_get_ref (config->ref)))
|
||
+ {
|
||
+ g_info ("Preinstall ref %s is marked as already preinstalled; skipping",
|
||
+ flatpak_decomposed_get_ref (config->ref));
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ for (int j = 0; remotes[j] != NULL; j++)
|
||
+ {
|
||
+ const char *remote = remotes[j];
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ g_autofree char *remote_collection_id = NULL;
|
||
+
|
||
+ if (flatpak_dir_get_remote_disabled (priv->dir, remote))
|
||
+ continue;
|
||
+
|
||
+ remote_collection_id = flatpak_dir_get_remote_collection_id (priv->dir,
|
||
+ remote);
|
||
+
|
||
+ /* Choose the first match if the collection ID was not specified */
|
||
+ if (config->collection_id != NULL &&
|
||
+ g_strcmp0 (remote_collection_id, config->collection_id) != 0)
|
||
+ continue;
|
||
+
|
||
+ g_info ("Adding preinstall of %s from remote %s",
|
||
+ flatpak_decomposed_get_ref (config->ref),
|
||
+ remote);
|
||
+
|
||
+ if (!flatpak_transaction_add_ref (self, remote, config->ref, NULL, NULL, NULL,
|
||
+ FLATPAK_TRANSACTION_OPERATION_INSTALL,
|
||
+ NULL, NULL, NULL, FALSE, TRUE, NULL,
|
||
+ &local_error))
|
||
+ {
|
||
+ g_info ("Failed to add preinstall ref %s: %s",
|
||
+ flatpak_decomposed_get_ref (config->ref),
|
||
+ local_error->message);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Find previously preinstalled refs that are no longer in the preinstall
|
||
+ * list and should now get uninstalled */
|
||
+ for (int i = 0; i < preinstalled_refs->len; i++)
|
||
+ {
|
||
+ const char *ref = g_ptr_array_index (preinstalled_refs, i);
|
||
+
|
||
+ /* No longer in the preinstall.d list, so uninstall */
|
||
+ if (!flatpak_g_ptr_array_contains_string (install_refs, ref))
|
||
+ {
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ g_autoptr(FlatpakDecomposed) decomposed = NULL;
|
||
+
|
||
+ decomposed = flatpak_decomposed_new_from_ref (ref, error);
|
||
+ if (decomposed == NULL)
|
||
+ return FALSE;
|
||
+
|
||
+ g_info ("Preinstalled ref %s is no longer listed as wanted in preinstall.d config; uninstalling",
|
||
+ flatpak_decomposed_get_ref (decomposed));
|
||
+
|
||
+ if (!flatpak_transaction_add_ref (self, NULL, decomposed, NULL, NULL, NULL,
|
||
+ FLATPAK_TRANSACTION_OPERATION_UNINSTALL,
|
||
+ NULL, NULL, NULL, FALSE, TRUE, NULL,
|
||
+ &local_error))
|
||
+ {
|
||
+ if (!g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED))
|
||
+ {
|
||
+ g_propagate_error (error, g_steal_pointer (&local_error));
|
||
+ return FALSE;
|
||
+ }
|
||
+ g_clear_error (&local_error);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
/**
|
||
* flatpak_transaction_add_update:
|
||
* @self: a #FlatpakTransaction
|
||
@@ -3154,7 +3288,7 @@ flatpak_transaction_add_update (FlatpakTransaction *self,
|
||
return FALSE;
|
||
|
||
/* Note: we implement the merge when subpaths == NULL in flatpak_transaction_add_ref() */
|
||
- return flatpak_transaction_add_ref (self, NULL, decomposed, subpaths, NULL, commit, FLATPAK_TRANSACTION_OPERATION_UPDATE, NULL, NULL, NULL, FALSE, NULL, error);
|
||
+ return flatpak_transaction_add_ref (self, NULL, decomposed, subpaths, NULL, commit, FLATPAK_TRANSACTION_OPERATION_UPDATE, NULL, NULL, NULL, FALSE, FALSE, NULL, error);
|
||
}
|
||
|
||
/**
|
||
@@ -3181,7 +3315,7 @@ flatpak_transaction_add_uninstall (FlatpakTransaction *self,
|
||
if (decomposed == NULL)
|
||
return FALSE;
|
||
|
||
- return flatpak_transaction_add_ref (self, NULL, decomposed, NULL, NULL, NULL, FLATPAK_TRANSACTION_OPERATION_UNINSTALL, NULL, NULL, NULL, FALSE, NULL, error);
|
||
+ return flatpak_transaction_add_ref (self, NULL, decomposed, NULL, NULL, NULL, FLATPAK_TRANSACTION_OPERATION_UNINSTALL, NULL, NULL, NULL, FALSE, FALSE, NULL, error);
|
||
}
|
||
|
||
static gboolean
|
||
@@ -3293,7 +3427,7 @@ flatpak_transaction_add_auto_install (FlatpakTransaction *self,
|
||
|
||
if (!flatpak_transaction_add_ref (self, remote, auto_install_ref, NULL, NULL, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE,
|
||
- NULL, NULL, NULL, FALSE, NULL,
|
||
+ NULL, NULL, NULL, FALSE, FALSE, NULL,
|
||
&local_error))
|
||
g_info ("Failed to add auto-install ref %s: %s", flatpak_decomposed_get_ref (auto_install_ref),
|
||
local_error->message);
|
||
@@ -4808,7 +4942,7 @@ flatpak_transaction_resolve_bundles (FlatpakTransaction *self,
|
||
|
||
if (!flatpak_transaction_add_ref (self, remote, ref, NULL, NULL, commit,
|
||
FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE,
|
||
- data->file, NULL, metadata, FALSE, NULL, error))
|
||
+ data->file, NULL, metadata, FALSE, FALSE, NULL, error))
|
||
return FALSE;
|
||
}
|
||
|
||
@@ -4875,7 +5009,8 @@ flatpak_transaction_resolve_images (FlatpakTransaction *self,
|
||
|
||
if (!flatpak_transaction_add_ref (self, remote, ref, NULL, NULL, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_INSTALL,
|
||
- NULL, image_source, metadata_label, FALSE, &op, error))
|
||
+ NULL, image_source, metadata_label, FALSE, FALSE,
|
||
+ &op, error))
|
||
return FALSE;
|
||
}
|
||
|
||
@@ -4947,6 +5082,7 @@ _run_op_kind (FlatpakTransaction *self,
|
||
priv->reinstall,
|
||
priv->max_op >= APP_UPDATE,
|
||
op->pin_on_deploy,
|
||
+ op->update_preinstalled_on_deploy,
|
||
remote_state, op->ref,
|
||
op->resolved_commit,
|
||
(const char **) op->subpaths,
|
||
@@ -4986,7 +5122,7 @@ _run_op_kind (FlatpakTransaction *self,
|
||
if (flatpak_decomposed_is_app (op->ref))
|
||
*out_needs_triggers = TRUE;
|
||
|
||
- if (op->pin_on_deploy)
|
||
+ if (op->pin_on_deploy|| op->update_preinstalled_on_deploy)
|
||
*out_needs_cache_drop = TRUE;
|
||
}
|
||
}
|
||
@@ -5091,6 +5227,9 @@ _run_op_kind (FlatpakTransaction *self,
|
||
if (priv->force_uninstall)
|
||
flags |= FLATPAK_HELPER_UNINSTALL_FLAGS_FORCE_REMOVE;
|
||
|
||
+ if (op->update_preinstalled_on_deploy)
|
||
+ flags |= FLATPAK_HELPER_UNINSTALL_FLAGS_UPDATE_PREINSTALLED;
|
||
+
|
||
emit_new_op (self, op, progress);
|
||
|
||
res = flatpak_dir_uninstall (priv->dir, op->ref, flags,
|
||
@@ -5271,7 +5410,7 @@ add_uninstall_unused_ops (FlatpakTransaction *self,
|
||
uninstall_op = flatpak_transaction_add_op (self, origin, unused_ref,
|
||
NULL, NULL, NULL, NULL,
|
||
FLATPAK_TRANSACTION_OPERATION_UNINSTALL,
|
||
- FALSE);
|
||
+ FALSE, FALSE);
|
||
run_operation_last (uninstall_op);
|
||
}
|
||
}
|
||
diff --git a/common/flatpak-transaction.h b/common/flatpak-transaction.h
|
||
index dfa3d039..ae305c34 100644
|
||
--- a/common/flatpak-transaction.h
|
||
+++ b/common/flatpak-transaction.h
|
||
@@ -339,6 +339,9 @@ gboolean flatpak_transaction_add_install_flatpakref (FlatpakTransacti
|
||
GBytes *flatpakref_data,
|
||
GError **error);
|
||
FLATPAK_EXTERN
|
||
+gboolean flatpak_transaction_add_sync_preinstalled (FlatpakTransaction *self,
|
||
+ GError **error);
|
||
+FLATPAK_EXTERN
|
||
gboolean flatpak_transaction_add_update (FlatpakTransaction *self,
|
||
const char *ref,
|
||
const char **subpaths,
|
||
diff --git a/doc/flatpak-docs.xml.in b/doc/flatpak-docs.xml.in
|
||
index 581bf685..4de8d1f9 100644
|
||
--- a/doc/flatpak-docs.xml.in
|
||
+++ b/doc/flatpak-docs.xml.in
|
||
@@ -52,6 +52,7 @@
|
||
<xi:include href="@srcdir@/flatpak-permission-show.xml"/>
|
||
<xi:include href="@srcdir@/flatpak-permission-reset.xml"/>
|
||
<xi:include href="@srcdir@/flatpak-permission-set.xml"/>
|
||
+ <xi:include href="@srcdir@/flatpak-preinstall.xml"/>
|
||
<xi:include href="@srcdir@/flatpak-ps.xml"/>
|
||
<xi:include href="@srcdir@/flatpak-remote-add.xml"/>
|
||
<xi:include href="@srcdir@/flatpak-remote-delete.xml"/>
|
||
diff --git a/doc/flatpak-preinstall.xml b/doc/flatpak-preinstall.xml
|
||
new file mode 100644
|
||
index 00000000..b774c618
|
||
--- /dev/null
|
||
+++ b/doc/flatpak-preinstall.xml
|
||
@@ -0,0 +1,276 @@
|
||
+<?xml version='1.0'?> <!--*-nxml-*-->
|
||
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||
+
|
||
+<refentry id="flatpak-preinstall">
|
||
+
|
||
+ <refentryinfo>
|
||
+ <title>flatpak preinstall</title>
|
||
+ <productname>flatpak</productname>
|
||
+
|
||
+ <authorgroup>
|
||
+ <author>
|
||
+ <contrib>Developer</contrib>
|
||
+ <firstname>Kalev</firstname>
|
||
+ <surname>Lember</surname>
|
||
+ <email>klember@redhat.com</email>
|
||
+ </author>
|
||
+ </authorgroup>
|
||
+ </refentryinfo>
|
||
+
|
||
+ <refmeta>
|
||
+ <refentrytitle>flatpak preinstall</refentrytitle>
|
||
+ <manvolnum>1</manvolnum>
|
||
+ </refmeta>
|
||
+
|
||
+ <refnamediv>
|
||
+ <refname>flatpak-preinstall</refname>
|
||
+ <refpurpose>Install flatpaks that are part of the operating system</refpurpose>
|
||
+ </refnamediv>
|
||
+
|
||
+ <refsynopsisdiv>
|
||
+ <cmdsynopsis>
|
||
+ <command>flatpak preinstall</command>
|
||
+ <arg choice="opt" rep="repeat">OPTION</arg>
|
||
+ </cmdsynopsis>
|
||
+ </refsynopsisdiv>
|
||
+
|
||
+ <refsect1>
|
||
+ <title>Description</title>
|
||
+
|
||
+ <para>
|
||
+ This command manages flatpaks that are part of the operating system. If no options are given, running <command>flatpak preinstall</command> will synchronize (install and remove) flatpaks to match the set that the OS vendor has chosen.
|
||
+ </para>
|
||
+
|
||
+ <para>
|
||
+ Preinstalled flatpaks are defined by dropping .preinstall files into the directories <filename>/usr/share/flatpak/preinstall.d/</filename> and <filename>/etc/flatpak/preinstall.d/</filename>. The OS runs <command>flatpak preinstall -y</command> (or its GUI equivalent) on system startup, which then does the actual installation.
|
||
+ </para>
|
||
+
|
||
+ <para>
|
||
+ This system allows the OS vendor to define the list of flatpaks that are installed together with the OS, and also makes it possible for the OS vendor to make changes to the list in the future, which is then applied once <command>flatpak preinstall</command> is run next time.
|
||
+
|
||
+ Users can opt out of preinstalled flatpaks by simply uninstalling them, at which point they won't get automatically reinstalled again.
|
||
+ </para>
|
||
+ </refsect1>
|
||
+
|
||
+ <refsect1>
|
||
+ <title>File format</title>
|
||
+
|
||
+ <para>
|
||
+ The .preinstall file is using the same .ini file format that is used for systemd unit files or application .desktop files.
|
||
+ </para>
|
||
+
|
||
+ <refsect2>
|
||
+ <title>[Flatpak Preinstall NAME]</title>
|
||
+
|
||
+ <para>
|
||
+ The NAME is the fully qualified name of the runtime or application. All the information for a single runtime or application is contained in one [Flatpak Preinstall NAME] group. Multiple groups can be defined in a single file.
|
||
+ </para>
|
||
+ <para>
|
||
+ The following keys can be present in this group:
|
||
+ </para>
|
||
+ <variablelist>
|
||
+ <varlistentry>
|
||
+ <term><option>Install</option> (boolean)</term>
|
||
+ <listitem><para>
|
||
+ Whether this group should be installed. If this key is not specified, the group will be installed.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+ <varlistentry>
|
||
+ <term><option>Branch</option> (string)</term>
|
||
+ <listitem><para>
|
||
+ The name of the branch from which to install the application or runtime. If this key is not specified, the "master" branch is used.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+ <varlistentry>
|
||
+ <term><option>IsRuntime</option> (boolean)</term>
|
||
+ <listitem><para>
|
||
+ Whether this group refers to a runtime. If this key is not specified, the group is assumed to refer to an application.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+ <varlistentry>
|
||
+ <term><option>CollectionID</option> (string)</term>
|
||
+ <listitem><para>
|
||
+ The collection ID of the remote to use, if it has one.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+ </variablelist>
|
||
+ </refsect2>
|
||
+ </refsect1>
|
||
+
|
||
+ <refsect1>
|
||
+ <title>Example</title>
|
||
+<programlisting>
|
||
+[Flatpak Preinstall org.gnome.Loupe]
|
||
+Branch=stable
|
||
+IsRuntime=false
|
||
+</programlisting>
|
||
+ </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>--reinstall</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Uninstall first if already installed.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>-u</option></term>
|
||
+ <term><option>--user</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Install the application or runtime in a per-user installation.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--system</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Install the application or runtime in the default system-wide installation.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--installation=NAME</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Install the application or runtime 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>--no-deploy</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Download the latest version, but don't deploy it.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--no-pull</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Don't download the latest version, deploy whatever is locally available.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--no-related</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Don't download related extensions, such as the locale data.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--no-deps</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Don't verify runtime dependencies when installing.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--sideload-repo=PATH</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Adds an extra local ostree repo as a source for installation. This is equivalent
|
||
+ to using the <filename>sideload-repos</filename> directories (see
|
||
+ <citerefentry><refentrytitle>flatpak</refentrytitle><manvolnum>1</manvolnum></citerefentry>),
|
||
+ but can be done on a per-command basis. Any path added here is used in addition
|
||
+ to ones in those directories.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--include-sdk</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ For each app being installed, also installs the SDK that was used to build it.
|
||
+ Implies <option>--or-update</option>; incompatible with <option>--no-deps</option>.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--include-debug</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ For each ref being installed, as well as all dependencies, also installs its
|
||
+ debug info. Implies <option>--or-update</option>; incompatible with
|
||
+ <option>--no-deps</option>.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>-y</option></term>
|
||
+ <term><option>--assumeyes</option></term>
|
||
+ <listitem><para>
|
||
+ Automatically answer yes to all questions (or pick the most prioritized answer). This is useful for automation.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--noninteractive</option></term>
|
||
+ <listitem><para>
|
||
+ Produce minimal output and avoid most questions. This is suitable for use in
|
||
+ non-interactive situations, e.g. in a build script.
|
||
+ </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>
|
||
+
|
||
+ <varlistentry>
|
||
+ <term><option>--ostree-verbose</option></term>
|
||
+
|
||
+ <listitem><para>
|
||
+ Print OSTree debug information during command processing.
|
||
+ </para></listitem>
|
||
+ </varlistentry>
|
||
+ </variablelist>
|
||
+ </refsect1>
|
||
+
|
||
+ <refsect1>
|
||
+ <title>Examples</title>
|
||
+
|
||
+ <para>
|
||
+ <command>$ flatpak preinstall</command>
|
||
+ </para>
|
||
+ </refsect1>
|
||
+
|
||
+ <refsect1>
|
||
+ <title>See also</title>
|
||
+
|
||
+ <para>
|
||
+ <citerefentry><refentrytitle>flatpak</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||
+ </para>
|
||
+ </refsect1>
|
||
+
|
||
+</refentry>
|
||
diff --git a/doc/meson.build b/doc/meson.build
|
||
index a4cedfbe..9ef8eee1 100644
|
||
--- a/doc/meson.build
|
||
+++ b/doc/meson.build
|
||
@@ -26,6 +26,7 @@ man1 = [
|
||
'flatpak-config',
|
||
'flatpak-update',
|
||
'flatpak-uninstall',
|
||
+ 'flatpak-preinstall',
|
||
'flatpak-mask',
|
||
'flatpak-pin',
|
||
'flatpak-list',
|
||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||
index b3cb79c0..52600abd 100644
|
||
--- a/po/POTFILES.in
|
||
+++ b/po/POTFILES.in
|
||
@@ -30,6 +30,7 @@ app/flatpak-builtins-permission-reset.c
|
||
app/flatpak-builtins-permission-set.c
|
||
app/flatpak-builtins-permission-show.c
|
||
app/flatpak-builtins-pin.c
|
||
+app/flatpak-builtins-preinstall.c
|
||
app/flatpak-builtins-ps.c
|
||
app/flatpak-builtins-remote-add.c
|
||
app/flatpak-builtins-remote-delete.c
|
||
diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c
|
||
index 1f950b20..623c2fa0 100644
|
||
--- a/system-helper/flatpak-system-helper.c
|
||
+++ b/system-helper/flatpak-system-helper.c
|
||
@@ -404,6 +404,7 @@ handle_deploy (FlatpakSystemHelper *object,
|
||
gboolean local_pull;
|
||
gboolean reinstall;
|
||
gboolean update_pinned;
|
||
+ gboolean update_preinstalled;
|
||
g_autofree char *url = NULL;
|
||
g_autoptr(OngoingPull) ongoing_pull = NULL;
|
||
g_autofree gchar *src_dir = NULL;
|
||
@@ -489,6 +490,7 @@ handle_deploy (FlatpakSystemHelper *object,
|
||
local_pull = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL) != 0;
|
||
reinstall = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_REINSTALL) != 0;
|
||
update_pinned = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PINNED) != 0;
|
||
+ update_preinstalled = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE_PREINSTALLED) != 0;
|
||
|
||
deploy_dir = flatpak_dir_get_if_deployed (system, ref, NULL, NULL);
|
||
|
||
@@ -667,7 +669,8 @@ handle_deploy (FlatpakSystemHelper *object,
|
||
if (!flatpak_dir_deploy_install (system, ref, arg_origin,
|
||
(const char **) arg_subpaths,
|
||
(const char **) arg_previous_ids,
|
||
- reinstall, update_pinned, NULL, &error))
|
||
+ reinstall, update_pinned, update_preinstalled,
|
||
+ NULL, &error))
|
||
{
|
||
flatpak_invocation_return_error (invocation, error, "Error deploying");
|
||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||
diff --git a/tests/libtest.sh b/tests/libtest.sh
|
||
index 7dad594f..69ae0fef 100644
|
||
--- a/tests/libtest.sh
|
||
+++ b/tests/libtest.sh
|
||
@@ -105,6 +105,7 @@ export FLATPAK_SYSTEM_DIR=${TEST_DATA_DIR}/system
|
||
export FLATPAK_SYSTEM_CACHE_DIR=${TEST_DATA_DIR}/system-cache
|
||
export FLATPAK_SYSTEM_HELPER_ON_SESSION=1
|
||
export FLATPAK_CONFIG_DIR=${TEST_DATA_DIR}/config
|
||
+export FLATPAK_DATA_DIR=${TEST_DATA_DIR}/datadir
|
||
export FLATPAK_RUN_DIR=${TEST_DATA_DIR}/run
|
||
export FLATPAK_FANCY_OUTPUT=0
|
||
export FLATPAK_FORCE_ALLOW_FUZZY_MATCHING=1
|
||
diff --git a/tests/test-basic.sh b/tests/test-basic.sh
|
||
index 76369b35..9f1cfb20 100755
|
||
--- a/tests/test-basic.sh
|
||
+++ b/tests/test-basic.sh
|
||
@@ -71,7 +71,7 @@ for cmd in install update uninstall list info config repair create-usb \
|
||
remote-modify remote-delete remote-ls remote-info build-init \
|
||
build build-finish build-export build-bundle build-import-bundle \
|
||
build-sign build-update-repo build-commit-from repo kill history \
|
||
- mask;
|
||
+ mask preinstall;
|
||
do
|
||
${FLATPAK} $cmd --help > help_out
|
||
head -2 help_out > help_out2
|
||
diff --git a/tests/test-matrix/meson.build b/tests/test-matrix/meson.build
|
||
index fd0b5034..3c20abed 100644
|
||
--- a/tests/test-matrix/meson.build
|
||
+++ b/tests/test-matrix/meson.build
|
||
@@ -45,3 +45,4 @@ wrapped_tests += {'name' : 'test-unused.sh', 'script' : 'test-unused.sh'}
|
||
wrapped_tests += {'name' : 'test-prune.sh', 'script' : 'test-prune.sh'}
|
||
wrapped_tests += {'name' : 'test-seccomp.sh', 'script' : 'test-seccomp.sh'}
|
||
wrapped_tests += {'name' : 'test-repair.sh', 'script' : 'test-repair.sh'}
|
||
+wrapped_tests += {'name' : 'test-preinstall.sh', 'script' : 'test-preinstall.sh'}
|
||
diff --git a/tests/test-preinstall.sh b/tests/test-preinstall.sh
|
||
new file mode 100755
|
||
index 00000000..23ae094c
|
||
--- /dev/null
|
||
+++ b/tests/test-preinstall.sh
|
||
@@ -0,0 +1,226 @@
|
||
+#!/bin/bash
|
||
+#
|
||
+# Copyright (C) 2025 Red Hat, Inc
|
||
+#
|
||
+# This library 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 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, write to the
|
||
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
+# Boston, MA 02111-1307, USA.
|
||
+
|
||
+set -euo pipefail
|
||
+
|
||
+. $(dirname $0)/libtest.sh
|
||
+
|
||
+mkdir -p $FLATPAK_DATA_DIR/preinstall.d
|
||
+mkdir -p $FLATPAK_CONFIG_DIR/preinstall.d
|
||
+
|
||
+cat << EOF > hello-install.preinstall
|
||
+[Flatpak Preinstall org.test.Hello]
|
||
+EOF
|
||
+
|
||
+cat << EOF > hello-not-install.preinstall
|
||
+[Flatpak Preinstall org.test.Hello]
|
||
+Install=false
|
||
+EOF
|
||
+
|
||
+cat << EOF > hello-install-multi.preinstall
|
||
+[Flatpak Preinstall org.test.Hello]
|
||
+[Flatpak Preinstall org.test.Hello2]
|
||
+EOF
|
||
+
|
||
+cat << EOF > hello-install-devel.preinstall
|
||
+[Flatpak Preinstall org.test.Hello]
|
||
+Branch=devel
|
||
+EOF
|
||
+
|
||
+cat << EOF > bad.preinstall
|
||
+[Wrong Group]
|
||
+a=b
|
||
+
|
||
+[Flatpak Preinstall ]
|
||
+Install=false
|
||
+
|
||
+[Flatpak Preinstall]
|
||
+Install=true
|
||
+EOF
|
||
+
|
||
+# Set up the runtimes
|
||
+# org.test.Platform//master and org.test.Platform//devel
|
||
+# and the apps
|
||
+# org.test.Hello//master, org.test.Hello//devel,
|
||
+# org.test.Hello2//master, org.test.Hello2//devel
|
||
+setup_repo
|
||
+make_updated_runtime test org.test.Collection.test devel HELLO_DEVEL org.test.Hello
|
||
+make_updated_app test org.test.Collection.test devel HELLO_DEVEL org.test.Hello
|
||
+make_updated_app test org.test.Collection.test master HELLO2_MASTER org.test.Hello2
|
||
+make_updated_app test org.test.Collection.test devel HELLO2_DEVEL org.test.Hello2
|
||
+
|
||
+# make the test repo available by adding the test remote
|
||
+cat << EOF > test.flatpakrepo
|
||
+[Flatpak Repo]
|
||
+Url=http://127.0.0.1:${port}/test
|
||
+GPGKey=${FL_GPG_BASE64}
|
||
+Title=The Title
|
||
+Comment=The Comment
|
||
+Description=The Description
|
||
+Homepage=https://the.homepage/
|
||
+Icon=https://the.icon/
|
||
+EOF
|
||
+
|
||
+${FLATPAK} ${U} remote-add test test.flatpakrepo >&2
|
||
+
|
||
+echo "1..9"
|
||
+
|
||
+# just checking that the test remote got added
|
||
+assert_remote_has_config test url "http://127.0.0.1:${port}/test"
|
||
+
|
||
+ok "setup"
|
||
+
|
||
+# if we have nothing configured and nothing is marked as preinstalled
|
||
+# calling preinstall should be a no-op
|
||
+${FLATPAK} ${U} preinstall -y > nothingtodo
|
||
+assert_file_has_content nothingtodo "Nothing to do"
|
||
+
|
||
+ok "no config"
|
||
+
|
||
+# make sure nothing is installed
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_file_empty list-log
|
||
+
|
||
+# The preinstall config wants org.test.Hello.
|
||
+cp hello-install.preinstall $FLATPAK_DATA_DIR/preinstall.d/
|
||
+
|
||
+${FLATPAK} ${U} preinstall -y >&2
|
||
+
|
||
+# Make sure it and the runtime were installed
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+ok "simple preinstall"
|
||
+
|
||
+# Make sure calling preinstall with the same config again is a no-op...
|
||
+${FLATPAK} ${U} preinstall -y > nothingtodo
|
||
+assert_file_has_content nothingtodo "Nothing to do"
|
||
+
|
||
+# ...and everything is still installed
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+ok "simple preinstall no op"
|
||
+
|
||
+${FLATPAK} ${U} uninstall -y org.test.Hello >&2
|
||
+
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+# Make sure calling preinstall with the same config again is a no-op
|
||
+# Even if the user uninstalled the app (it is marked as preinstalled)
|
||
+${FLATPAK} ${U} preinstall -y > nothingtodo
|
||
+assert_file_has_content nothingtodo "Nothing to do"
|
||
+
|
||
+# Make sure nothing has changed
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+ok "uninstall preinstall"
|
||
+
|
||
+${FLATPAK} ${U} install test -y org.test.Hello master >&2
|
||
+
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+# Add a config to /etc which overwrites the config in /usr ($FLATPAK_DATA_DIR)
|
||
+# It has the Install=false setting which means it shall not be installed.
|
||
+cp hello-not-install.preinstall $FLATPAK_CONFIG_DIR/preinstall.d/
|
||
+
|
||
+${FLATPAK} ${U} preinstall -y >&2
|
||
+
|
||
+# Make sure preinstall removed org.test.Hello as indicated by the config
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+ok "preinstall install false"
|
||
+
|
||
+# Remove the existing configs
|
||
+rm -rf $FLATPAK_CONFIG_DIR/preinstall.d/*
|
||
+rm -rf $FLATPAK_DATA_DIR/preinstall.d/*
|
||
+
|
||
+# Add a config file which wants org.test.Hello and org.test.Hello2 installed
|
||
+cp hello-install-multi.preinstall $FLATPAK_CONFIG_DIR/preinstall.d/
|
||
+
|
||
+${FLATPAK} ${U} preinstall -y >&2
|
||
+
|
||
+# Make sure both apps got installed
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+ok "install multi"
|
||
+
|
||
+# Overwrite the branch of org.test.Hello from master to devel
|
||
+cp hello-install-devel.preinstall $FLATPAK_CONFIG_DIR/preinstall.d/
|
||
+
|
||
+${FLATPAK} ${U} preinstall -y >&2
|
||
+
|
||
+# Make sure org.test.Hello//devel replaced org.test.Hello//master
|
||
+${FLATPAK} ${U} list --columns=ref > list-log
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello/.*/master$"
|
||
+assert_file_has_content list-log "^org\.test\.Hello/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Hello2/.*/master$"
|
||
+assert_not_file_has_content list-log "^org\.test\.Hello2/.*/devel$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/master$"
|
||
+assert_file_has_content list-log "^org\.test\.Platform/.*/devel$"
|
||
+
|
||
+ok "overwrite branch"
|
||
+
|
||
+# Make sure some config file parsing edge cases don't blow up
|
||
+cp bad.preinstall $FLATPAK_CONFIG_DIR/preinstall.d/
|
||
+
|
||
+${FLATPAK} ${U} preinstall -y > nothingtodo
|
||
+assert_file_has_content nothingtodo "Nothing to do"
|
||
+
|
||
+ok "bad config"
|
||
\ No newline at end of file
|
||
--
|
||
2.47.1
|
||
|