mirror of
https://github.com/flatpak/flatpak.git
synced 2026-01-31 02:51:22 -05:00
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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user