From f20e5f7823c2c44a28c0f650e4e0f6df8e1919f4 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 20 Dec 2016 16:24:13 +0100 Subject: [PATCH] bundles: Support dependencies and runtime-repo If the bundle contains an origin link we can now install related things from it, such as locale data. You can also build the bundle with --runtime-repo=URL, where the url points to a flatpakrepo file for a repo with runtimes. This works similar to the RuntimeRepo= feature in flatpakref files. --- app/flatpak-builtins-build-bundle.c | 5 + app/flatpak-builtins-build-import-bundle.c | 6 +- app/flatpak-builtins-install.c | 148 ++++++++++------ app/flatpak-transaction.c | 135 +++++++++++---- app/flatpak-transaction.h | 4 + common/flatpak-dir.c | 187 ++++++++++++++------- common/flatpak-dir.h | 10 +- common/flatpak-utils.c | 18 +- common/flatpak-utils.h | 2 + data/org.freedesktop.Flatpak.xml | 4 +- doc/flatpak-build-bundle.xml | 10 ++ lib/flatpak-bundle-ref.c | 28 ++- lib/flatpak-bundle-ref.h | 1 + lib/flatpak-installation.c | 10 +- system-helper/flatpak-system-helper.c | 10 +- tests/test-bundle.sh | 21 ++- 16 files changed, 418 insertions(+), 181 deletions(-) diff --git a/app/flatpak-builtins-build-bundle.c b/app/flatpak-builtins-build-bundle.c index ed2d172f..e393199b 100644 --- a/app/flatpak-builtins-build-bundle.c +++ b/app/flatpak-builtins-build-bundle.c @@ -44,6 +44,7 @@ static char *opt_arch; static char *opt_repo_url; +static char *opt_runtime_repo; static gboolean opt_runtime = FALSE; static char **opt_gpg_file; static gboolean opt_oci = FALSE; @@ -52,6 +53,7 @@ static GOptionEntry options[] = { { "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, N_("Export runtime instead of app"), NULL }, { "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to bundle for"), N_("ARCH") }, { "repo-url", 0, 0, G_OPTION_ARG_STRING, &opt_repo_url, N_("Url for repo"), N_("URL") }, + { "runtime-repo", 0, 0, G_OPTION_ARG_STRING, &opt_runtime_repo, N_("Url for runtime flatpakrepo file"), N_("URL") }, { "gpg-keys", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, N_("Add GPG key from FILE (- for stdin)"), N_("FILE") }, { "oci", 0, 0, G_OPTION_ARG_NONE, &opt_oci, N_("Export oci image instead of flatpak bundle"), NULL }, @@ -222,6 +224,9 @@ build_bundle (OstreeRepo *repo, GFile *file, if (opt_repo_url) g_variant_builder_add (&metadata_builder, "{sv}", "origin", g_variant_new_string (opt_repo_url)); + if (opt_runtime_repo) + g_variant_builder_add (&metadata_builder, "{sv}", "runtime-repo", g_variant_new_string (opt_runtime_repo)); + if (opt_gpg_file != NULL) { gpg_data = read_gpg_data (cancellable, error); diff --git a/app/flatpak-builtins-build-import-bundle.c b/app/flatpak-builtins-build-import-bundle.c index 5faebb57..0e50e3f0 100644 --- a/app/flatpak-builtins-build-import-bundle.c +++ b/app/flatpak-builtins-build-import-bundle.c @@ -108,10 +108,8 @@ import_bundle (OstreeRepo *repo, GFile *file, metadata = flatpak_bundle_load (file, &to_checksum, &bundle_ref, - NULL, - NULL, - NULL, - error); + NULL, NULL, NULL, + NULL, NULL, error); if (metadata == NULL) return NULL; diff --git a/app/flatpak-builtins-install.c b/app/flatpak-builtins-install.c index 13a77ba4..ec529fc8 100644 --- a/app/flatpak-builtins-install.c +++ b/app/flatpak-builtins-install.c @@ -112,49 +112,8 @@ read_gpg_data (GCancellable *cancellable, } static gboolean -install_bundle (FlatpakDir *dir, - GOptionContext *context, - int argc, char **argv, - GCancellable *cancellable, - GError **error) +handle_runtime_repo_deps (FlatpakDir *dir, const char *dep_url, GError **error) { - g_autoptr(GFile) file = NULL; - const char *filename; - g_autoptr(GBytes) gpg_data = NULL; - - if (argc < 2) - return usage_error (context, _("Bundle filename must be specified"), error); - - if (argc > 2) - return usage_error (context, _("Too many arguments"), error); - - filename = argv[1]; - - file = g_file_new_for_commandline_arg (filename); - - if (!g_file_is_native (file)) - return flatpak_fail (error, _("Remote bundles are not supported")); - - if (opt_gpg_file != NULL) - { - /* Override gpg_data from file */ - gpg_data = read_gpg_data (cancellable, error); - if (gpg_data == NULL) - return FALSE; - } - - if (!flatpak_dir_install_bundle (dir, file, gpg_data, NULL, - cancellable, error)) - return FALSE; - - return TRUE; -} - -static gboolean -handle_runtime_repo_deps (FlatpakDir *dir, GBytes *data, GError **error) -{ - g_autoptr(GKeyFile) keyfile = g_key_file_new (); - g_autofree char *dep_url = NULL; g_autoptr(GBytes) dep_data = NULL; g_autofree char *runtime_url = NULL; g_autofree char *old_remote = NULL; @@ -168,13 +127,7 @@ handle_runtime_repo_deps (FlatpakDir *dir, GBytes *data, GError **error) char *t; int i; - if (!g_key_file_load_from_data (keyfile, g_bytes_get_data (data, NULL), g_bytes_get_size (data), - 0, error)) - return FALSE; - - dep_url = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP, - FLATPAK_REF_RUNTIME_REPO_KEY, NULL); - if (dep_url == NULL) + if (opt_no_deps) return TRUE; dep_data = download_uri (dep_url, error); @@ -256,6 +209,101 @@ handle_runtime_repo_deps (FlatpakDir *dir, GBytes *data, GError **error) return TRUE; } +static gboolean +handle_runtime_repo_deps_from_bundle (FlatpakDir *dir, GFile *file, GError **error) +{ + g_autofree char *dep_url = NULL; + g_autoptr(GVariant) metadata = NULL; + + if (opt_no_deps) + return TRUE; + + metadata = flatpak_bundle_load (file, NULL, + NULL, + NULL, + &dep_url, + NULL, + NULL, + NULL, + NULL); + if (metadata == NULL || dep_url == NULL) + return TRUE; + + return handle_runtime_repo_deps (dir, dep_url, error); +} + +static gboolean +install_bundle (FlatpakDir *dir, + GOptionContext *context, + int argc, char **argv, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GFile) file = NULL; + const char *filename; + g_autoptr(GBytes) gpg_data = NULL; + g_autoptr(FlatpakTransaction) transaction = NULL; + + if (argc < 2) + return usage_error (context, _("Bundle filename must be specified"), error); + + if (argc > 2) + return usage_error (context, _("Too many arguments"), error); + + filename = argv[1]; + + file = g_file_new_for_commandline_arg (filename); + + if (!g_file_is_native (file)) + return flatpak_fail (error, _("Remote bundles are not supported")); + + if (opt_gpg_file != NULL) + { + /* Override gpg_data from file */ + gpg_data = read_gpg_data (cancellable, error); + if (gpg_data == NULL) + return FALSE; + } + + if (!handle_runtime_repo_deps_from_bundle (dir, file, error)) + return FALSE; + + if (!flatpak_dir_ensure_repo (dir, cancellable, error)) + return FALSE; + + transaction = flatpak_transaction_new (dir, opt_yes, opt_no_pull, opt_no_deploy, + !opt_no_deps, !opt_no_related); + + if (!flatpak_transaction_add_install_bundle (transaction, file, gpg_data, error)) + return FALSE; + + if (!flatpak_transaction_run (transaction, TRUE, cancellable, error)) + return FALSE; + + return TRUE; +} + +static gboolean +handle_runtime_repo_deps_from_keyfile (FlatpakDir *dir, GBytes *data, GError **error) +{ + g_autoptr(GKeyFile) keyfile = g_key_file_new (); + g_autofree char *dep_url = NULL; + + if (opt_no_deps) + return TRUE; + + if (!g_key_file_load_from_data (keyfile, g_bytes_get_data (data, NULL), g_bytes_get_size (data), + 0, error)) + return FALSE; + + dep_url = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP, + FLATPAK_REF_RUNTIME_REPO_KEY, NULL); + if (dep_url == NULL) + return TRUE; + + return handle_runtime_repo_deps (dir, dep_url, error); +} + static gboolean install_from (FlatpakDir *dir, GOptionContext *context, @@ -303,7 +351,7 @@ install_from (FlatpakDir *dir, file_data = g_bytes_new_take (g_steal_pointer (&data), data_len); } - if (!handle_runtime_repo_deps (dir, file_data, error)) + if (!handle_runtime_repo_deps_from_keyfile (dir, file_data, error)) return FALSE; if (!flatpak_dir_create_remote_for_ref_file (dir, file_data, &remote, &ref, error)) diff --git a/app/flatpak-transaction.c b/app/flatpak-transaction.c index aefdb6ad..4b294938 100644 --- a/app/flatpak-transaction.c +++ b/app/flatpak-transaction.c @@ -30,13 +30,20 @@ typedef struct FlatpakTransactionOp FlatpakTransactionOp; +typedef enum { + FLATPAK_TRANSACTION_OP_KIND_INSTALL, + FLATPAK_TRANSACTION_OP_KIND_UPDATE, + FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE, + FLATPAK_TRANSACTION_OP_KIND_BUNDLE +} FlatpakTransactionOpKind; + struct FlatpakTransactionOp { char *remote; char *ref; char **subpaths; char *commit; - gboolean update; - gboolean install; + GFile *bundle; + FlatpakTransactionOpKind kind; gboolean non_fatal; }; @@ -117,8 +124,8 @@ flatpak_transaction_operation_new (const char *remote, const char *ref, const char **subpaths, const char *commit, - gboolean install, - gboolean update) + GFile *bundle, + FlatpakTransactionOpKind kind) { FlatpakTransactionOp *self = g_new0 (FlatpakTransactionOp, 1); @@ -126,8 +133,9 @@ flatpak_transaction_operation_new (const char *remote, self->ref = g_strdup (ref); self->subpaths = g_strdupv ((char **)subpaths); self->commit = g_strdup (commit); - self->update = update; - self->install = install; + if (bundle) + self->bundle = g_object_ref (bundle); + self->kind = kind; return self; } @@ -139,6 +147,7 @@ flatpak_transaction_operation_free (FlatpakTransactionOp *self) g_free (self->ref); g_free (self->commit); g_strfreev (self->subpaths); + g_clear_object (&self->bundle); g_free (self); } @@ -212,31 +221,38 @@ subpaths_to_string (const char **subpaths) return g_string_free (s, FALSE); } +static const char * +kind_to_str (FlatpakTransactionOpKind kind) +{ + switch (kind) + { + case FLATPAK_TRANSACTION_OP_KIND_INSTALL: + return "install"; + case FLATPAK_TRANSACTION_OP_KIND_UPDATE: + return "update"; + case FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE: + return "install/update"; + case FLATPAK_TRANSACTION_OP_KIND_BUNDLE: + return "install bundle"; + } + return "unknown"; +} + FlatpakTransactionOp * flatpak_transaction_add_op (FlatpakTransaction *self, const char *remote, const char *ref, const char **subpaths, const char *commit, - gboolean install, - gboolean update) + GFile *bundle, + FlatpakTransactionOpKind kind) { FlatpakTransactionOp *op; g_autofree char *subpaths_str = NULL; - const char *opname; - if (install) - { - if (update) - opname = "install/update"; - else - opname = "install"; - } - else - opname = "update"; subpaths_str = subpaths_to_string (subpaths); g_debug ("Transaction: %s %s:%s%s%s%s", - opname, remote, ref, + kind_to_str (kind), remote, ref, commit != NULL ? "@" : "", commit != NULL ? commit : "", subpaths_str); @@ -255,7 +271,7 @@ flatpak_transaction_add_op (FlatpakTransaction *self, return op; } - op = flatpak_transaction_operation_new (remote, ref, subpaths, commit, install, update); + op = flatpak_transaction_operation_new (remote, ref, subpaths, commit, bundle, kind); g_hash_table_insert (self->refs, g_strdup (ref), op); self->ops = g_list_prepend (self->ops, op); @@ -329,7 +345,8 @@ add_related (FlatpakTransaction *self, op = flatpak_transaction_add_op (self, remote, rel->ref, (const char **)rel->subpaths, - NULL, TRUE, TRUE); + NULL, NULL, + FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE); op->non_fatal = TRUE; } } @@ -394,14 +411,16 @@ add_deps (FlatpakTransaction *self, "The Application %s requires the runtime %s which is not installed", pref, runtime_ref); - flatpak_transaction_add_op (self, runtime_remote, full_runtime_ref, NULL, NULL, TRUE, TRUE); + flatpak_transaction_add_op (self, runtime_remote, full_runtime_ref, NULL, NULL, NULL, + FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE); } else { /* Update if in same dir */ if (dir_ref_is_installed (self->dir, full_runtime_ref, &runtime_remote)) { - flatpak_transaction_add_op (self, runtime_remote, full_runtime_ref, NULL, NULL, FALSE, TRUE); + flatpak_transaction_add_op (self, runtime_remote, full_runtime_ref, NULL, NULL, NULL, + FLATPAK_TRANSACTION_OP_KIND_UPDATE); } } } @@ -419,17 +438,19 @@ flatpak_transaction_add_ref (FlatpakTransaction *self, const char *ref, const char **subpaths, const char *commit, - gboolean is_update, + FlatpakTransactionOpKind kind, + GFile *bundle, + const char *metadata, GError **error) { g_autofree char *origin = NULL; const char *pref; - g_autofree char *metadata = NULL; + g_autofree char *remote_metadata = NULL; g_autoptr(GKeyFile) metakey = NULL; pref = strchr (ref, '/') + 1; - if (is_update) + if (kind == FLATPAK_TRANSACTION_OP_KIND_UPDATE) { if (!dir_ref_is_installed (self->dir, ref, &origin)) { @@ -456,7 +477,11 @@ flatpak_transaction_add_ref (FlatpakTransaction *self, } } - if (flatpak_dir_fetch_ref_cache (self->dir, remote, ref, NULL, NULL, &metadata, NULL, NULL)) + if (metadata == NULL && remote != NULL && + flatpak_dir_fetch_ref_cache (self->dir, remote, ref, NULL, NULL, &remote_metadata, NULL, NULL)) + metadata = remote_metadata; + + if (metadata) { metakey = g_key_file_new (); if (!g_key_file_load_from_data (metakey, metadata, -1, 0, NULL)) @@ -487,7 +512,7 @@ flatpak_transaction_add_ref (FlatpakTransaction *self, return FALSE; } - flatpak_transaction_add_op (self, remote, ref, subpaths, commit, !is_update, is_update); + flatpak_transaction_add_op (self, remote, ref, subpaths, commit, bundle, kind); if (!add_related (self, remote, ref, error)) return FALSE; @@ -508,7 +533,30 @@ flatpak_transaction_add_install (FlatpakTransaction *self, if (subpaths == NULL) subpaths = all_paths; - return flatpak_transaction_add_ref (self, remote, ref, subpaths, NULL, FALSE, error); + return flatpak_transaction_add_ref (self, remote, ref, subpaths, NULL, FLATPAK_TRANSACTION_OP_KIND_INSTALL, NULL, NULL, error); +} + +gboolean +flatpak_transaction_add_install_bundle (FlatpakTransaction *self, + GFile *file, + GBytes *gpg_data, + GError **error) +{ + g_autofree char *remote = NULL; + g_autofree char *ref = NULL; + g_autofree char *metadata = NULL; + gboolean created_remote; + + remote = flatpak_dir_ensure_bundle_remote (self->dir, file, gpg_data, + &ref, &metadata, &created_remote, + NULL, error); + if (remote == NULL) + return FALSE; + + if (!flatpak_dir_recreate_repo (self->dir, NULL, error)) + return FALSE; + + return flatpak_transaction_add_ref (self, remote, ref, NULL, NULL, FLATPAK_TRANSACTION_OP_KIND_BUNDLE, file, metadata, error); } gboolean @@ -557,7 +605,7 @@ flatpak_transaction_add_install_oci (FlatpakTransaction *self, id = g_strdup_printf ("oci-%s", parts[1]); remote = flatpak_dir_create_origin_remote (self->dir, NULL, - id, title, + id, title, ref, uri, tag, NULL, NULL, error); if (remote == NULL) @@ -568,7 +616,7 @@ flatpak_transaction_add_install_oci (FlatpakTransaction *self, g_debug ("Added OCI origin remote %s", remote); - return flatpak_transaction_add_ref (self, remote, ref, all_paths, checksum, FALSE, error); + return flatpak_transaction_add_ref (self, remote, ref, all_paths, checksum, FLATPAK_TRANSACTION_OP_KIND_INSTALL, NULL, NULL, error); } gboolean @@ -578,7 +626,7 @@ flatpak_transaction_add_update (FlatpakTransaction *self, const char *commit, GError **error) { - return flatpak_transaction_add_ref (self, NULL, ref, subpaths, commit, TRUE, error); + return flatpak_transaction_add_ref (self, NULL, ref, subpaths, commit, FLATPAK_TRANSACTION_OP_KIND_UPDATE, NULL, NULL, error); } gboolean @@ -599,18 +647,20 @@ flatpak_transaction_run (FlatpakTransaction *self, gboolean res; const char *pref; const char *opname; + FlatpakTransactionOpKind kind; - if (op->install && op->update) + kind = op->kind; + if (kind == FLATPAK_TRANSACTION_OP_KIND_INSTALL_OR_UPDATE) { if (dir_ref_is_installed (self->dir, op->ref, NULL)) - op->install = FALSE; + kind = FLATPAK_TRANSACTION_OP_KIND_UPDATE; else - op->update = FALSE; + kind = FLATPAK_TRANSACTION_OP_KIND_INSTALL; } pref = strchr (op->ref, '/') + 1; - if (op->install) + if (kind == FLATPAK_TRANSACTION_OP_KIND_INSTALL) { opname = _("install"); g_print (_("Installing: %s from %s\n"), pref, op->remote); @@ -622,7 +672,7 @@ flatpak_transaction_run (FlatpakTransaction *self, NULL, cancellable, &local_error); } - else /* update */ + else if (kind == FLATPAK_TRANSACTION_OP_KIND_UPDATE) { opname = _("update"); g_print (_("Updating: %s from %s\n"), pref, op->remote); @@ -651,6 +701,17 @@ flatpak_transaction_run (FlatpakTransaction *self, g_clear_error (&local_error); } } + else if (kind == FLATPAK_TRANSACTION_OP_KIND_BUNDLE) + { + g_autofree char *bundle_basename = g_file_get_basename (op->bundle); + opname = _("install bundle"); + g_print (_("Installing: %s from bundle %s\n"), pref, bundle_basename); + res = flatpak_dir_install_bundle (self->dir, op->bundle, + op->remote, NULL, + cancellable, &local_error); + } + else + g_assert_not_reached (); if (!res) { diff --git a/app/flatpak-transaction.h b/app/flatpak-transaction.h index 2eb81106..ae8a6f88 100644 --- a/app/flatpak-transaction.h +++ b/app/flatpak-transaction.h @@ -48,6 +48,10 @@ gboolean flatpak_transaction_add_install_oci (FlatpakTransaction *se const char *uri, const char *tag, GError **error); +gboolean flatpak_transaction_add_install_bundle (FlatpakTransaction *self, + GFile *file, + GBytes *gpg_data, + GError **error); gboolean flatpak_transaction_add_update (FlatpakTransaction *self, const char *ref, const char **subpaths, diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index cdad7886..4dd263cc 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -4310,45 +4310,125 @@ flatpak_dir_install (FlatpakDir *self, return TRUE; } -gboolean -flatpak_dir_install_bundle (FlatpakDir *self, - GFile *file, - GBytes *extra_gpg_data, - char **out_ref, - GCancellable *cancellable, - GError **error) +char * +flatpak_dir_ensure_bundle_remote (FlatpakDir *self, + GFile *file, + GBytes *extra_gpg_data, + char **out_ref, + char **out_metadata, + gboolean *out_created_remote, + GCancellable *cancellable, + GError **error) { g_autofree char *ref = NULL; - gboolean added_remote = FALSE; + gboolean created_remote = FALSE; g_autoptr(GVariant) deploy_data = NULL; g_autoptr(GVariant) metadata = NULL; g_autofree char *origin = NULL; + g_autofree char *fp_metadata = NULL; g_auto(GStrv) parts = NULL; g_autofree char *basename = NULL; g_autoptr(GBytes) included_gpg_data = NULL; GBytes *gpg_data = NULL; g_autofree char *to_checksum = NULL; g_autofree char *remote = NULL; - gboolean ret = FALSE; + + if (!flatpak_dir_ensure_repo (self, cancellable, error)) + return NULL; + + metadata = flatpak_bundle_load (file, &to_checksum, + &ref, + &origin, + NULL, &fp_metadata, NULL, + &included_gpg_data, + error); + if (metadata == NULL) + return NULL; + + gpg_data = extra_gpg_data ? extra_gpg_data : included_gpg_data; + + parts = flatpak_decompose_ref (ref, error); + if (parts == NULL) + return NULL; + + deploy_data = flatpak_dir_get_deploy_data (self, ref, cancellable, NULL); + if (deploy_data != NULL) + { + remote = g_strdup (flatpak_deploy_data_get_origin (deploy_data)); + + /* We need to import any gpg keys because otherwise the pull will fail */ + if (gpg_data != NULL) + { + g_autoptr(GKeyFile) new_config = NULL; + + new_config = ostree_repo_copy_config (flatpak_dir_get_repo (self)); + + if (!flatpak_dir_modify_remote (self, remote, new_config, + gpg_data, cancellable, error)) + return NULL; + } + } + else + { + /* Add a remote for later updates */ + basename = g_file_get_basename (file); + remote = flatpak_dir_create_origin_remote (self, + origin, + parts[1], + basename, + ref, + NULL, NULL, + gpg_data, + cancellable, + error); + if (remote == NULL) + return NULL; + + /* From here we need to goto out on error, to clean up */ + created_remote = TRUE; + } + + if (out_created_remote) + *out_created_remote = created_remote; + + if (out_ref) + *out_ref = g_steal_pointer (&ref); + + if (out_metadata) + *out_metadata = g_steal_pointer (&fp_metadata); + + + return g_steal_pointer (&remote); +} + +gboolean +flatpak_dir_install_bundle (FlatpakDir *self, + GFile *file, + const char *remote, + char **out_ref, + GCancellable *cancellable, + GError **error) +{ + g_autofree char *ref = NULL; + g_autoptr(GVariant) deploy_data = NULL; + g_autoptr(GVariant) metadata = NULL; + g_autofree char *origin = NULL; + g_auto(GStrv) parts = NULL; + g_autofree char *to_checksum = NULL; + gboolean gpg_verify; if (flatpak_dir_use_system_helper (self)) { FlatpakSystemHelper *system_helper; - g_autoptr(GVariant) gpg_data_v = NULL; const char *installation = flatpak_dir_get_id (self); system_helper = flatpak_dir_get_system_helper (self); g_assert (system_helper != NULL); - if (gpg_data != NULL) - gpg_data_v = variant_new_ay_bytes (gpg_data); - else - gpg_data_v = g_variant_ref_sink (g_variant_new_from_data (G_VARIANT_TYPE ("ay"), "", 0, TRUE, NULL, NULL)); - g_debug ("Calling system helper: InstallBundle"); if (!flatpak_system_helper_call_install_bundle_sync (system_helper, flatpak_file_get_path_cached (file), - 0, gpg_data_v, + 0, remote, installation ? installation : "", &ref, cancellable, @@ -4367,14 +4447,12 @@ flatpak_dir_install_bundle (FlatpakDir *self, metadata = flatpak_bundle_load (file, &to_checksum, &ref, &origin, - NULL, - &included_gpg_data, + NULL, NULL, + NULL, NULL, error); if (metadata == NULL) return FALSE; - gpg_data = extra_gpg_data ? extra_gpg_data : included_gpg_data; - parts = flatpak_decompose_ref (ref, error); if (parts == NULL) return FALSE; @@ -4388,51 +4466,27 @@ flatpak_dir_install_bundle (FlatpakDir *self, _("This version of %s is already installed"), parts[1]); return FALSE; } - remote = g_strdup (flatpak_deploy_data_get_origin (deploy_data)); - /* We need to import any gpg keys because otherwise the pull will fail */ - if (gpg_data != NULL) + if (strcmp (remote, flatpak_deploy_data_get_origin (deploy_data)) != 0) { - g_autoptr(GInputStream) input_stream = g_memory_input_stream_new_from_bytes (gpg_data); - guint imported = 0; - - if (!ostree_repo_remote_gpg_import (self->repo, remote, input_stream, - NULL, &imported, cancellable, error)) - return FALSE; - - /* XXX If we ever add internationalization, use ngettext() here. */ - g_debug ("Imported %u GPG key%s to remote \"%s\"", - imported, (imported == 1) ? "" : "s", remote); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Can't change remote during bundle install")); + return FALSE; } } - else - { - /* Add a remote for later updates */ - basename = g_file_get_basename (file); - remote = flatpak_dir_create_origin_remote (self, - origin, - parts[1], - basename, - ref, - NULL, NULL, - gpg_data, - cancellable, - error); - if (remote == NULL) - return FALSE; - /* From here we need to goto out on error, to clean up */ - added_remote = TRUE; - } + if (!ostree_repo_remote_get_gpg_verify (self->repo, remote, + &gpg_verify, error)) + return FALSE; if (!flatpak_pull_from_bundle (self->repo, file, remote, ref, - gpg_data != NULL, + gpg_verify, cancellable, error)) - goto out; + return FALSE; if (deploy_data != NULL) { @@ -4466,24 +4520,18 @@ flatpak_dir_install_bundle (FlatpakDir *self, if (deploy_data) { if (!flatpak_dir_deploy_update (self, ref, NULL, NULL, cancellable, error)) - goto out; + return FALSE; } else { if (!flatpak_dir_deploy_install (self, ref, remote, NULL, cancellable, error)) - goto out; + return FALSE; } if (out_ref) *out_ref = g_steal_pointer (&ref); - ret = TRUE; - -out: - if (added_remote && !ret) - ostree_repo_remote_delete (self->repo, remote, NULL, NULL); - - return ret; + return TRUE; } static gboolean @@ -6340,6 +6388,7 @@ create_origin_remote_config (OstreeRepo *repo, const char *main_ref, const char *oci_uri, const char *oci_tag, + gboolean gpg_verify, GKeyFile *new_config) { g_autofree char *remote = NULL; @@ -6370,8 +6419,16 @@ create_origin_remote_config (OstreeRepo *repo, g_key_file_set_string (new_config, group, "xa.title", title); g_key_file_set_string (new_config, group, "xa.noenumerate", "true"); g_key_file_set_string (new_config, group, "xa.prio", "0"); - g_key_file_set_string (new_config, group, "gpg-verify", "true"); - g_key_file_set_string (new_config, group, "gpg-verify-summary", "true"); + if (gpg_verify) + { + g_key_file_set_string (new_config, group, "gpg-verify", "true"); + g_key_file_set_string (new_config, group, "gpg-verify-summary", "true"); + } + else + { + g_key_file_set_string (new_config, group, "gpg-verify", "false"); + g_key_file_set_string (new_config, group, "gpg-verify-summary", "false"); + } if (main_ref) g_key_file_set_string (new_config, group, "xa.main-ref", main_ref); if (oci_uri) @@ -6397,7 +6454,7 @@ flatpak_dir_create_origin_remote (FlatpakDir *self, g_autoptr(GKeyFile) new_config = g_key_file_new (); g_autofree char *remote = NULL; - remote = create_origin_remote_config (self->repo, url, id, title, main_ref, oci_uri, oci_tag, new_config); + remote = create_origin_remote_config (self->repo, url, id, title, main_ref, oci_uri, oci_tag, gpg_data != NULL, new_config); if (!flatpak_dir_modify_remote (self, remote, new_config, gpg_data, cancellable, error)) diff --git a/common/flatpak-dir.h b/common/flatpak-dir.h index 3812b491..195d6d77 100644 --- a/common/flatpak-dir.h +++ b/common/flatpak-dir.h @@ -370,9 +370,17 @@ gboolean flatpak_dir_install (FlatpakDir *self, OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error); +char *flatpak_dir_ensure_bundle_remote (FlatpakDir *self, + GFile *file, + GBytes *extra_gpg_data, + char **out_ref, + char **out_metadata, + gboolean *out_created_remote, + GCancellable *cancellable, + GError **error); gboolean flatpak_dir_install_bundle (FlatpakDir *self, GFile *file, - GBytes *extra_gpg_data, + const char *remote, char **out_ref, GCancellable *cancellable, GError **error); diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c index bc55e718..5bceb13a 100644 --- a/common/flatpak-utils.c +++ b/common/flatpak-utils.c @@ -3878,6 +3878,8 @@ flatpak_bundle_load (GFile *file, char **commit, char **ref, char **origin, + char **runtime_repo, + char **app_metadata, guint64 *installed_size, GBytes **gpg_keys, GError **error) @@ -3947,6 +3949,18 @@ flatpak_bundle_load (GFile *file, *origin = NULL; } + if (runtime_repo != NULL) + { + if (!g_variant_lookup (metadata, "runtime-repo", "s", runtime_repo)) + *runtime_repo = NULL; + } + + if (app_metadata != NULL) + { + if (!g_variant_lookup (metadata, "metadata", "s", app_metadata)) + *runtime_repo = NULL; + } + if (gpg_keys != NULL) { g_autoptr(GVariant) gpg_value = g_variant_lookup_value (metadata, "gpg-keys", @@ -3991,12 +4005,10 @@ flatpak_pull_from_bundle (OstreeRepo *repo, g_autoptr(GVariant) metadata = NULL; gboolean metadata_valid; - metadata = flatpak_bundle_load (file, &to_checksum, NULL, NULL, NULL, NULL, error); + metadata = flatpak_bundle_load (file, &to_checksum, NULL, NULL, NULL, &metadata_contents, NULL, NULL, error); if (metadata == NULL) return FALSE; - g_variant_lookup (metadata, "metadata", "s", &metadata_contents); - if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) return FALSE; diff --git a/common/flatpak-utils.h b/common/flatpak-utils.h index 8420263e..757fe067 100644 --- a/common/flatpak-utils.h +++ b/common/flatpak-utils.h @@ -295,6 +295,8 @@ GVariant * flatpak_bundle_load (GFile *file, char **commit, char **ref, char **origin, + char **runtime_repo, + char **app_metadata, guint64 *installed_size, GBytes **gpg_keys, GError **error); diff --git a/data/org.freedesktop.Flatpak.xml b/data/org.freedesktop.Flatpak.xml index c50d1a5f..a17e597e 100644 --- a/data/org.freedesktop.Flatpak.xml +++ b/data/org.freedesktop.Flatpak.xml @@ -84,9 +84,7 @@ - - - + diff --git a/doc/flatpak-build-bundle.xml b/doc/flatpak-build-bundle.xml index 449ea9fa..80d07e0b 100644 --- a/doc/flatpak-build-bundle.xml +++ b/doc/flatpak-build-bundle.xml @@ -98,6 +98,16 @@ + + + + + The URL for a .flatpakrepo file that contains + the information about the repository that supplies + the runtimes required by the app. + + + diff --git a/lib/flatpak-bundle-ref.c b/lib/flatpak-bundle-ref.c index c92e3371..2e72c369 100644 --- a/lib/flatpak-bundle-ref.c +++ b/lib/flatpak-bundle-ref.c @@ -32,6 +32,7 @@ struct _FlatpakBundleRefPrivate { GFile *file; char *origin; + char *runtime_repo; GBytes *metadata; GBytes *appstream; GBytes *icon_64; @@ -60,6 +61,7 @@ flatpak_bundle_ref_finalize (GObject *object) g_bytes_unref (priv->icon_64); g_bytes_unref (priv->icon_128); g_free (priv->origin); + g_free (priv->runtime_repo); G_OBJECT_CLASS (flatpak_bundle_ref_parent_class)->finalize (object); } @@ -221,6 +223,25 @@ flatpak_bundle_ref_get_origin (FlatpakBundleRef *self) return g_strdup (priv->origin); } + +/** + * flatpak_bundle_ref_get_runtime_repo: + * @self: a #FlatpakBundleRef + * + * Get the runtime flatpakrepo url stored in the bundle (if any) + * + * Returns: (transfer full) : an url string, or %NULL + * + * Since: 0.8.0 + */ +char * +flatpak_bundle_ref_get_runtime_repo_url (FlatpakBundleRef *self) +{ + FlatpakBundleRefPrivate *priv = flatpak_bundle_ref_get_instance_private (self); + + return g_strdup (priv->runtime_repo); +} + /** * flatpak_bundle_ref_get_installed_size: * @self: a FlatpakBundleRef @@ -259,13 +280,14 @@ flatpak_bundle_ref_new (GFile *file, g_autofree char *commit = NULL; g_autofree char *full_ref = NULL; g_autofree char *origin = NULL; + g_autofree char *runtime_repo = NULL; g_autofree char *metadata_contents = NULL; g_autoptr(GVariant) appstream = NULL; g_autoptr(GVariant) icon_64 = NULL; g_autoptr(GVariant) icon_128 = NULL; guint64 installed_size; - metadata = flatpak_bundle_load (file, &commit, &full_ref, &origin, &installed_size, + metadata = flatpak_bundle_load (file, &commit, &full_ref, &origin, &runtime_repo, &metadata_contents, &installed_size, NULL, error); if (metadata == NULL) return NULL; @@ -274,9 +296,6 @@ flatpak_bundle_ref_new (GFile *file, if (parts == NULL) return NULL; - if (!g_variant_lookup (metadata, "metadata", "s", &metadata_contents)) - metadata_contents = NULL; - if (strcmp (parts[0], "app") != 0) kind = FLATPAK_REF_KIND_RUNTIME; @@ -310,6 +329,7 @@ flatpak_bundle_ref_new (GFile *file, priv->installed_size = installed_size; priv->origin = g_steal_pointer (&origin); + priv->runtime_repo = g_steal_pointer (&runtime_repo); return ref; } diff --git a/lib/flatpak-bundle-ref.h b/lib/flatpak-bundle-ref.h index 623cd724..20484dbd 100644 --- a/lib/flatpak-bundle-ref.h +++ b/lib/flatpak-bundle-ref.h @@ -55,6 +55,7 @@ FLATPAK_EXTERN GBytes *flatpak_bundle_ref_get_icon (FlatpakBundleRef *s int size); FLATPAK_EXTERN char *flatpak_bundle_ref_get_origin (FlatpakBundleRef *self); FLATPAK_EXTERN guint64 flatpak_bundle_ref_get_installed_size (FlatpakBundleRef *self); +FLATPAK_EXTERN char *flatpak_bundle_ref_get_runtime_repo_url (FlatpakBundleRef *self); #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC diff --git a/lib/flatpak-installation.c b/lib/flatpak-installation.c index 388efcf5..0245ff0c 100644 --- a/lib/flatpak-installation.c +++ b/lib/flatpak-installation.c @@ -1225,14 +1225,22 @@ flatpak_installation_install_bundle (FlatpakInstallation *self, g_autoptr(FlatpakDir) dir = flatpak_installation_get_dir (self); g_autoptr(FlatpakDir) dir_clone = NULL; g_autofree char *ref = NULL; + g_autofree char *remote = NULL; FlatpakInstalledRef *result = NULL; + remote = flatpak_dir_ensure_bundle_remote (dir, file, NULL, &ref, NULL, NULL, cancellable, error); + if (remote == NULL) + return NULL; + + /* Make sure we pick up the new config */ + flatpak_installation_drop_caches (self, NULL, NULL); + /* Pull, prune, etc are not threadsafe, so we work on a copy */ dir_clone = flatpak_dir_clone (dir); if (!flatpak_dir_ensure_repo (dir_clone, cancellable, error)) return NULL; - if (!flatpak_dir_install_bundle (dir_clone, file, NULL, &ref, + if (!flatpak_dir_install_bundle (dir_clone, file, remote, NULL, cancellable, error)) return NULL; diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c index d47efad5..47fe0072 100644 --- a/system-helper/flatpak-system-helper.c +++ b/system-helper/flatpak-system-helper.c @@ -428,16 +428,15 @@ handle_install_bundle (FlatpakSystemHelper *object, GDBusMethodInvocation *invocation, const gchar *arg_bundle_path, guint32 arg_flags, - GVariant *arg_gpg_key, + const gchar *arg_remote, const gchar *arg_installation) { g_autoptr(FlatpakDir) system = NULL; g_autoptr(GFile) path = g_file_new_for_path (arg_bundle_path); g_autoptr(GError) error = NULL; - g_autoptr(GBytes) gpg_data = NULL; g_autofree char *ref = NULL; - g_debug ("InstallBundle %s %u %p %s", arg_bundle_path, arg_flags, arg_gpg_key, arg_installation); + g_debug ("InstallBundle %s %u %s %s", arg_bundle_path, arg_flags, arg_remote, arg_installation); system = dir_get_system (arg_installation, &error); if (system == NULL) @@ -460,10 +459,7 @@ handle_install_bundle (FlatpakSystemHelper *object, return TRUE; } - if (g_variant_get_size (arg_gpg_key) > 0) - gpg_data = g_variant_get_data_as_bytes (arg_gpg_key); - - if (!flatpak_dir_install_bundle (system, path, gpg_data, &ref, NULL, &error)) + if (!flatpak_dir_install_bundle (system, path, arg_remote, &ref, NULL, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); return TRUE; diff --git a/tests/test-bundle.sh b/tests/test-bundle.sh index 3ca6364e..5cb18477 100755 --- a/tests/test-bundle.sh +++ b/tests/test-bundle.sh @@ -26,17 +26,22 @@ skip_without_user_xattrs echo "1..6" +mkdir bundles + setup_repo -${FLATPAK} build-bundle repo --repo-url=file://`pwd`/repo --gpg-keys=${FL_GPG_HOMEDIR}/pubring.gpg hello.flatpak org.test.Hello -assert_has_file hello.flatpak +${FLATPAK} build-bundle repo --repo-url=file://`pwd`/repo --gpg-keys=${FL_GPG_HOMEDIR}/pubring.gpg bundles/hello.flatpak org.test.Hello +assert_has_file bundles/hello.flatpak -${FLATPAK} build-bundle repo --runtime --repo-url=file://`pwd`/repo --gpg-keys=${FL_GPG_HOMEDIR}/pubring.gpg platform.flatpak org.test.Platform -assert_has_file platform.flatpak +${FLATPAK} build-bundle repo --runtime --repo-url=file://`pwd`/repo --gpg-keys=${FL_GPG_HOMEDIR}/pubring.gpg bundles/platform.flatpak org.test.Platform +assert_has_file bundles/platform.flatpak echo "ok create bundles" -${FLATPAK} install ${U} --bundle hello.flatpak +${FLATPAK} install ${U} -y --bundle bundles/hello.flatpak + +# This should have installed the runtime dependency too +assert_has_file $FL_DIR/repo/refs/remotes/test-repo/runtime/org.test.Platform/$ARCH/master assert_has_file $FL_DIR/repo/refs/remotes/org.test.Hello-origin/app/org.test.Hello/$ARCH/master APP_COMMIT=`cat $FL_DIR/repo/refs/remotes/org.test.Hello-origin/app/org.test.Hello/$ARCH/master` @@ -80,7 +85,11 @@ assert_has_file $FL_DIR/repo/org.test.Hello-origin.trustedkeys.gpg echo "ok install app bundle" -${FLATPAK} install ${U} --bundle platform.flatpak +${FLATPAK} uninstall ${U} org.test.Platform + +assert_not_has_file $FL_DIR/repo/refs/remotes/org.test.Platform-origin/runtime/org.test.Platform/$ARCH/master + +${FLATPAK} install ${U} --bundle bundles/platform.flatpak assert_has_file $FL_DIR/repo/refs/remotes/org.test.Platform-origin/runtime/org.test.Platform/$ARCH/master RUNTIME_COMMIT=`cat $FL_DIR/repo/refs/remotes/org.test.Platform-origin/runtime/org.test.Platform/$ARCH/master`