Add support for .flatpakref files

These are similar to .flatpakrepo, but for a single app.

For example, if you have a file gedit.flatpakref with this:

[Flatpak Ref]
Title=GEdit
Name=org.gnome.gedit
Branch=stable
Url=http://sdk.gnome.org/repo-apps/
IsRuntime=False
GPGKey=mQENBFUUCGcBCAC/K9WeV4xCaKr3NKRqPXeY5mpaXAJyasLqCtrDx92WUgbu0voWrhohNAKpqizod2dvzc/XTxm3rHyIxmNfdhz1gaGhynU75Qw4aJVcly2eghTIl++gfDtOvrOZo/VuAq30f32dMIgHQdRwEpgCwz7WyjpqZYltPAEcCNL4MTChAfiHJeeiQ5ibystNBW8W6Ymf7sO4m4g5+/aOxI54oCOzD9TwBAe+yXcJJWtc2rAhMCjtyPJzxd0ZVXqIzCe1xRvJ6Rq7YCiMbiM2DQFWXKnmYQbj4TGNMnwNdAajCdrcBWEMSbzq7EzuThIJRd8Ky4BkEe1St6tuqwFaMZz+F9eXABEBAAG0KEdub21lIFNESyAzLjE2IDxnbm9tZS1vcy1saXN0QGdub21lLm9yZz6JATgEEwECACIFAlUUCGcCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEArkz6VV0VKBa5cH/0vXa31YgEjNk78gGFXqnQxdD1WYA87OYxDi189l4lA802EFTF4wCBuZyDOqdd5BhS3Ab0cR778DmZXRUP2gwe+1zTJypU2JMnDpkwJ4NK1VP6/tE4SAPrznBtmb76BKaWBqUfZ9Wq1zg3ugvqkZB/Exq+usypIOwQVp1KL58TrjBRda0HvRctzkNhr0qYAtkfLFe0GvksBp4vBm8uGwAx7fw/HbhIjQ9pekTwvB+5GwDPO/tSip/1bQfCS+XJB8Ffa04HYPLGedalnWBrwhYY+G/kn5Zh9L/AC8xeLwTJTHM212rBjPa9CWs9C6a57MSaeGIEHLC1hEyiJJ15w8jmY=

You can then install gedit with
  flatpak install --from gedit.flatpakref
This commit is contained in:
Alexander Larsson
2016-09-09 16:50:32 +02:00
parent 65a1ba186f
commit 71500ae5ec
3 changed files with 293 additions and 82 deletions

View File

@@ -45,6 +45,7 @@ static gboolean opt_no_related;
static gboolean opt_runtime;
static gboolean opt_app;
static gboolean opt_bundle;
static gboolean opt_from;
static GOptionEntry options[] = {
{ "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to install for"), N_("ARCH") },
@@ -54,6 +55,7 @@ static GOptionEntry options[] = {
{ "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, N_("Look for runtime with the specified name"), NULL },
{ "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, N_("Look for app with the specified name"), NULL },
{ "bundle", 0, 0, G_OPTION_ARG_NONE, &opt_bundle, N_("Install from local bundle file"), NULL },
{ "from", 0, 0, G_OPTION_ARG_NONE, &opt_from, N_("Load options from file"), N_("FILE") },
{ "gpg-file", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, N_("Check bundle signatures with GPG key from FILE (- for stdin)"), N_("FILE") },
{ "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, N_("Only install this subpath"), N_("PATH") },
{ NULL }
@@ -101,7 +103,7 @@ read_gpg_data (GCancellable *cancellable,
return flatpak_read_stream (source_stream, FALSE, error);
}
gboolean
static gboolean
install_bundle (FlatpakDir *dir,
GOptionContext *context,
int argc, char **argv,
@@ -135,51 +137,24 @@ install_bundle (FlatpakDir *dir,
return TRUE;
}
gboolean
flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GError **error)
static gboolean
do_install (FlatpakDir *dir,
gboolean no_pull,
gboolean no_deploy,
const char *ref,
const char *remote_name,
const char **opt_subpaths,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GOptionContext) context = NULL;
g_autoptr(FlatpakDir) dir = NULL;
const char *repository;
char *name;
char *branch = NULL;
g_autofree char *ref = NULL;
gboolean is_app;
g_autoptr(GPtrArray) related = NULL;
int i;
context = g_option_context_new (_("REPOSITORY NAME [BRANCH] - Install an application or runtime"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
return FALSE;
if (opt_bundle)
return install_bundle (dir, context, argc, argv, cancellable, error);
if (argc < 3)
return usage_error (context, _("REPOSITORY and NAME must be specified"), error);
repository = argv[1];
name = argv[2];
if (argc >= 4)
branch = argv[3];
if (!flatpak_split_partial_ref_arg (name, &opt_arch, &branch, error))
return FALSE;
if (!opt_app && !opt_runtime)
opt_app = opt_runtime = TRUE;
ref = flatpak_dir_find_remote_ref (dir, repository, name, branch, opt_arch,
opt_app, opt_runtime, &is_app, cancellable, error);
if (ref == NULL)
return FALSE;
if (!flatpak_dir_install (dir,
opt_no_pull,
opt_no_deploy,
ref, repository, (const char **)opt_subpaths,
ref, remote_name,
(const char **)opt_subpaths,
NULL,
cancellable, error))
return FALSE;
@@ -189,9 +164,9 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
g_autoptr(GError) local_error = NULL;
if (opt_no_pull)
related = flatpak_dir_find_local_related (dir, ref, repository, NULL, &local_error);
related = flatpak_dir_find_local_related (dir, ref, remote_name, NULL, &local_error);
else
related = flatpak_dir_find_remote_related (dir, ref, repository, NULL, &local_error);
related = flatpak_dir_find_remote_related (dir, ref, remote_name, NULL, &local_error);
if (related == NULL)
{
g_printerr (_("Warning: Problem looking for related refs: %s\n"), local_error->message);
@@ -214,7 +189,7 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
if (!flatpak_dir_install_or_update (dir,
opt_no_pull,
opt_no_deploy,
rel->ref, repository,
rel->ref, remote_name,
(const char **)rel->subpaths,
NULL,
cancellable, &local_error))
@@ -230,6 +205,110 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
return TRUE;
}
static gboolean
install_from (FlatpakDir *dir,
GOptionContext *context,
int argc, char **argv,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GFile) file = NULL;
g_autoptr(GBytes) file_data = NULL;
g_autofree char *data = NULL;
gsize data_len;
const char *filename;
g_autofree char *remote = NULL;
g_autofree char *ref = NULL;
g_auto(GStrv) parts = NULL;
FlatpakDir *clone;
if (argc < 2)
return usage_error (context, _("Filename must be specified"), error);
filename = argv[1];
file = g_file_new_for_commandline_arg (filename);
if (!g_file_load_contents (file, cancellable, &data, &data_len, NULL, error))
return FALSE;
file_data = g_bytes_new_take (g_steal_pointer (&data), data_len);
if (!flatpak_dir_create_remote_for_ref_file (dir, file_data, &remote, &ref, error))
return FALSE;
/* Need to pick up the new config, in case it was applied in the system helper. */
clone = flatpak_dir_clone (dir);
if (!flatpak_dir_ensure_repo (clone, cancellable, error))
return FALSE;
parts = g_strsplit (ref, "/", 0);
g_print (_("Installing: %s\n"), parts[1]);
if (!do_install (clone,
opt_no_pull,
opt_no_deploy,
ref, remote,
(const char **)opt_subpaths,
cancellable, error))
return FALSE;
return TRUE;
}
gboolean
flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GError **error)
{
g_autoptr(GOptionContext) context = NULL;
g_autoptr(FlatpakDir) dir = NULL;
const char *repository;
char *name;
char *branch = NULL;
g_autofree char *ref = NULL;
gboolean is_app;
context = g_option_context_new (_("REPOSITORY NAME [BRANCH] - Install an application or runtime"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
return FALSE;
if (opt_bundle)
return install_bundle (dir, context, argc, argv, cancellable, error);
if (opt_from)
return install_from (dir, context, argc, argv, cancellable, error);
if (argc < 3)
return usage_error (context, _("REPOSITORY and NAME must be specified"), error);
repository = argv[1];
name = argv[2];
if (argc >= 4)
branch = argv[3];
if (!flatpak_split_partial_ref_arg (name, &opt_arch, &branch, error))
return FALSE;
if (!opt_app && !opt_runtime)
opt_app = opt_runtime = TRUE;
ref = flatpak_dir_find_remote_ref (dir, repository, name, branch, opt_arch,
opt_app, opt_runtime, &is_app, cancellable, error);
if (ref == NULL)
return FALSE;
if (!do_install (dir,
opt_no_pull,
opt_no_deploy,
ref, repository,
(const char **)opt_subpaths,
cancellable, error))
return FALSE;
return TRUE;
}
gboolean
flatpak_complete_install (FlatpakCompletion *completion)
{

View File

@@ -4721,25 +4721,20 @@ cmp_remote (gconstpointer a,
return prio_b - prio_a;
}
char *
flatpak_dir_create_origin_remote (FlatpakDir *self,
const char *url,
const char *id,
const char *title,
GBytes *gpg_data,
GCancellable *cancellable,
GError **error)
static char *
create_origin_remote_config (OstreeRepo *repo,
const char *url,
const char *id,
const char *title,
GKeyFile *new_config)
{
g_autofree char *remote = NULL;
g_auto(GStrv) remotes = NULL;
int version = 0;
g_autoptr(GVariantBuilder) optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
g_autofree char *group = NULL;
if (!flatpak_dir_ensure_repo (self, cancellable, error))
return FALSE;
remotes = ostree_repo_remote_list (self->repo, NULL);
remotes = ostree_repo_remote_list (repo, NULL);
do
{
@@ -4756,38 +4751,161 @@ flatpak_dir_create_origin_remote (FlatpakDir *self,
}
while (remote == NULL);
g_variant_builder_add (optbuilder, "{s@v}",
"xa.title",
g_variant_new_variant (g_variant_new_string (title)));
group = g_strdup_printf ("remote \"%s\"", remote);
g_variant_builder_add (optbuilder, "{s@v}",
"xa.noenumerate",
g_variant_new_variant (g_variant_new_boolean (TRUE)));
g_variant_builder_add (optbuilder, "{s@v}",
"xa.prio",
g_variant_new_variant (g_variant_new_string ("0")));
if (!ostree_repo_remote_add (self->repo,
remote, url ? url : "", g_variant_builder_end (optbuilder), cancellable, error))
return NULL;
if (gpg_data)
{
g_autoptr(GInputStream) gpg_data_as_stream = g_memory_input_stream_new_from_bytes (gpg_data);
if (!ostree_repo_remote_gpg_import (self->repo, remote, gpg_data_as_stream,
NULL, NULL, cancellable, error))
{
ostree_repo_remote_delete (self->repo, remote,
NULL, NULL);
return NULL;
}
}
g_key_file_set_string (new_config, group, "url", url ? url : "");
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");
return g_steal_pointer (&remote);
}
char *
flatpak_dir_create_origin_remote (FlatpakDir *self,
const char *url,
const char *id,
const char *title,
GBytes *gpg_data,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GKeyFile) new_config = g_key_file_new ();
g_autofree char *remote = NULL;
remote = create_origin_remote_config (self->repo, url, id, title, new_config);
if (!flatpak_dir_modify_remote (self, remote, new_config,
gpg_data, cancellable, error))
return NULL;
return g_steal_pointer (&remote);
}
static gboolean
parse_ref_file (GBytes *data,
char **name_out,
char **branch_out,
char **url_out,
char **title_out,
GBytes **gpg_data_out,
gboolean *is_runtime_out,
GError **error)
{
g_autoptr(GKeyFile) keyfile = g_key_file_new ();
g_autofree char *url = NULL;
g_autofree char *title = NULL;
g_autofree char *name = NULL;
g_autofree char *branch = NULL;
g_autoptr(GBytes) gpg_data = NULL;
gboolean is_runtime = FALSE;
char *str;
*name_out = NULL;
*branch_out = NULL;
*url_out = NULL;
*title_out = NULL;
*gpg_data_out = NULL;
*is_runtime_out = FALSE;
if (!g_key_file_load_from_data (keyfile, g_bytes_get_data (data, NULL), g_bytes_get_size (data),
0, error))
return FALSE;
if (!g_key_file_has_group (keyfile, FLATPAK_REF_GROUP))
return flatpak_fail (error, "Invalid file format, no %s group", FLATPAK_REF_GROUP);
url = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP,
FLATPAK_REF_URL_KEY, NULL);
if (url == NULL)
return flatpak_fail (error, "Invalid file format, no Url specified");
name = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP,
FLATPAK_REF_NAME_KEY, NULL);
if (name == NULL)
return flatpak_fail (error, "Invalid file format, no Name specified");
branch = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP,
FLATPAK_REF_BRANCH_KEY, NULL);
if (branch == NULL)
branch = g_strdup ("master");
title = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP,
FLATPAK_REF_TITLE_KEY, NULL);
is_runtime = g_key_file_get_boolean (keyfile, FLATPAK_REF_GROUP,
FLATPAK_REF_IS_RUNTIME_KEY, NULL);
str = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP,
FLATPAK_REF_GPGKEY_KEY, NULL);
if (str != NULL)
{
guchar *decoded;
gsize decoded_len;
str = g_strstrip (str);
decoded = g_base64_decode (str, &decoded_len);
if (decoded_len < 10) /* Check some minimal size so we don't get crap */
return flatpak_fail (error, "Invalid file format, gpg key invalid");
gpg_data = g_bytes_new_take (decoded, decoded_len);
}
*name_out = g_steal_pointer (&name);
*branch_out = g_steal_pointer (&branch);
*url_out = g_steal_pointer (&url);
*title_out = g_steal_pointer (&title);
*gpg_data_out = g_steal_pointer (&gpg_data);
*is_runtime_out = is_runtime;
return TRUE;
}
gboolean
flatpak_dir_create_remote_for_ref_file (FlatpakDir *self,
GBytes *data,
char **remote_name_out,
char **ref_out,
GError **error)
{
g_autoptr(GBytes) gpg_data = NULL;
g_autofree char *name = NULL;
g_autofree char *branch = NULL;
g_autofree char *url = NULL;
g_autofree char *title = NULL;
g_autofree char *ref = NULL;
g_autofree char *remote = NULL;
gboolean is_runtime = FALSE;
g_autoptr(GFile) deploy_dir = NULL;
if (!parse_ref_file (data, &name, &branch, &url, &title, &gpg_data, &is_runtime, error))
return FALSE;
ref = flatpak_compose_ref (!is_runtime, name, branch, NULL, error);
if (ref == NULL)
return FALSE;
deploy_dir = flatpak_dir_get_if_deployed (self, ref, NULL, NULL);
if (deploy_dir != NULL)
{
g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED,
is_runtime ? _("Runtime %s, branch %s is already installed")
: _("App %s, branch %s is already installed"),
name, branch);
return FALSE;
}
remote = flatpak_dir_create_origin_remote (self, url, name, title,
gpg_data, NULL, error);
if (remote == NULL)
return FALSE;
*remote_name_out = g_steal_pointer (&remote);
*ref_out = (char *)g_steal_pointer (&ref);
return TRUE;
}
char **
flatpak_dir_list_remotes (FlatpakDir *self,

View File

@@ -37,6 +37,14 @@
GType flatpak_dir_get_type (void);
GType flatpak_deploy_get_type (void);
#define FLATPAK_REF_GROUP "Flatpak Ref"
#define FLATPAK_REF_URL_KEY "Url"
#define FLATPAK_REF_TITLE_KEY "Title"
#define FLATPAK_REF_GPGKEY_KEY "GPGKey"
#define FLATPAK_REF_IS_RUNTIME_KEY "IsRuntime"
#define FLATPAK_REF_NAME_KEY "Name"
#define FLATPAK_REF_BRANCH_KEY "Branch"
typedef struct
{
char *ref;
@@ -380,6 +388,12 @@ char *flatpak_dir_create_origin_remote (FlatpakDir *self,
GBytes *gpg_data,
GCancellable *cancellable,
GError **error);
gboolean flatpak_dir_create_remote_for_ref_file (FlatpakDir *self,
GBytes *data,
char **remote_name_out,
char **ref_out,
GError **error);
char **flatpak_dir_list_remotes (FlatpakDir *self,
GCancellable *cancellable,
GError **error);