forked from Mirrors/flatpak-builder
install: Support installing multiple apps at the same time
Instead of using "NAME [BRANCH]" as the command list we now support REF..., where each REF can be partial. This is easiest explained by examples. Here are some valid refs: org.test.App - only app id app/org.test.App/x86_64/stable - full ref org.test.App/x86_64/stable - full ref without prefix org.test.App - only app id org.test.App//stable - only branch org.test.App/x86_64 - only arch If any parts are left out they are wildcarded. Such parts are filled first by looking at other command line arguments like --arch and --app/--runtime. And finally by looking at what is available in the remote. If there are multiple matches the user is told the options in an error message.tingping/wmclass
parent
a5d1f6331b
commit
7018717ce2
|
@ -151,8 +151,12 @@ do_install (FlatpakDir *dir,
|
|||
GError **error)
|
||||
{
|
||||
g_autoptr(GPtrArray) related = NULL;
|
||||
const char *slash;
|
||||
int i;
|
||||
|
||||
slash = strchr (ref, '/');
|
||||
g_print (_("Installing: %s\n"), slash + 1);
|
||||
|
||||
if (!flatpak_dir_install (dir,
|
||||
opt_no_pull,
|
||||
opt_no_deploy,
|
||||
|
@ -223,6 +227,7 @@ install_from (FlatpakDir *dir,
|
|||
g_autofree char *remote = NULL;
|
||||
g_autofree char *ref = NULL;
|
||||
g_auto(GStrv) parts = NULL;
|
||||
const char *slash;
|
||||
FlatpakDir *clone;
|
||||
|
||||
if (argc < 2)
|
||||
|
@ -248,8 +253,8 @@ install_from (FlatpakDir *dir,
|
|||
if (!flatpak_dir_ensure_repo (clone, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
parts = g_strsplit (ref, "/", 0);
|
||||
g_print (_("Installing: %s\n"), parts[1]);
|
||||
slash = strchr (ref, '/');
|
||||
g_print (_("Installing: %s\n"), slash + 1);
|
||||
|
||||
if (!do_install (clone,
|
||||
opt_no_pull,
|
||||
|
@ -262,22 +267,37 @@ install_from (FlatpakDir *dir,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
looks_like_branch (const char *branch)
|
||||
{
|
||||
/* In particular, / is not a valid branch char, so
|
||||
this lets us distinguish full or partial refs as
|
||||
non-branches. */
|
||||
if (!flatpak_is_valid_branch (branch, NULL))
|
||||
return FALSE;
|
||||
|
||||
/* Dots are allowed in branches, but not really used much, while
|
||||
they are required for app ids, so thats a good check to
|
||||
distinguish the two */
|
||||
if (strchr (branch, '.') != NULL)
|
||||
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;
|
||||
const char *pref = NULL;
|
||||
char **prefs = NULL;
|
||||
int i, n_prefs;
|
||||
const char *default_branch = NULL;
|
||||
g_autofree char *ref = NULL;
|
||||
FlatpakKinds kinds;
|
||||
FlatpakKinds kind;
|
||||
g_autofree char *id = NULL;
|
||||
g_autofree char *arch = NULL;
|
||||
g_autofree char *branch = NULL;
|
||||
g_autoptr(GPtrArray) refs = NULL;
|
||||
|
||||
context = g_option_context_new (_("REPOSITORY NAME [BRANCH] - Install an application or runtime"));
|
||||
context = g_option_context_new (_("REPOSITORY REF... - Install applications or runtimes"));
|
||||
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
|
||||
|
||||
if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
|
||||
|
@ -290,34 +310,58 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
|
|||
return install_from (dir, context, argc, argv, cancellable, error);
|
||||
|
||||
if (argc < 3)
|
||||
return usage_error (context, _("REPOSITORY and NAME must be specified"), error);
|
||||
return usage_error (context, _("REPOSITORY and REF must be specified"), error);
|
||||
|
||||
if (argc > 4)
|
||||
return usage_error (context, _("Too many arguments"), error);
|
||||
|
||||
repository = argv[1];
|
||||
pref = argv[2];
|
||||
if (argc >= 4)
|
||||
default_branch = argv[3];
|
||||
prefs = &argv[2];
|
||||
n_prefs = argc - 2;
|
||||
|
||||
/* Backwards compat for old "REPOSITORY NAME [BRANCH]" argument version */
|
||||
if (argc == 4 && looks_like_branch (argv[3]))
|
||||
{
|
||||
default_branch = argv[3];
|
||||
n_prefs = 1;
|
||||
}
|
||||
|
||||
kinds = flatpak_kinds_from_bools (opt_app, opt_runtime);
|
||||
|
||||
if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, default_branch,
|
||||
&kinds, &id, &arch, &branch, error))
|
||||
return FALSE;
|
||||
refs = g_ptr_array_new_with_free_func (g_free);
|
||||
for (i = 0; i < n_prefs; i++)
|
||||
{
|
||||
const char *pref = prefs[i];
|
||||
FlatpakKinds matched_kinds;
|
||||
g_autofree char *id = NULL;
|
||||
g_autofree char *arch = NULL;
|
||||
g_autofree char *branch = NULL;
|
||||
FlatpakKinds kind;
|
||||
g_autofree char *ref = NULL;
|
||||
|
||||
ref = flatpak_dir_find_remote_ref (dir, repository, id, branch, arch,
|
||||
kinds, &kind, cancellable, error);
|
||||
if (ref == NULL)
|
||||
return FALSE;
|
||||
if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, default_branch,
|
||||
&matched_kinds, &id, &arch, &branch, error))
|
||||
return FALSE;
|
||||
|
||||
if (!do_install (dir,
|
||||
opt_no_pull,
|
||||
opt_no_deploy,
|
||||
ref, repository,
|
||||
(const char **)opt_subpaths,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
ref = flatpak_dir_find_remote_ref (dir, repository, id, branch, arch,
|
||||
matched_kinds, &kind, cancellable, error);
|
||||
if (ref == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_ptr_array_add (refs, g_steal_pointer (&ref));
|
||||
}
|
||||
|
||||
for (i = 0; i < refs->len; i++)
|
||||
{
|
||||
const char *ref = g_ptr_array_index (refs, i);
|
||||
if (!do_install (dir,
|
||||
opt_no_pull,
|
||||
opt_no_deploy,
|
||||
ref, repository,
|
||||
(const char **)opt_subpaths,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -327,8 +371,6 @@ flatpak_complete_install (FlatpakCompletion *completion)
|
|||
{
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(FlatpakDir) dir = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_auto(GStrv) refs = NULL;
|
||||
FlatpakKinds kinds;
|
||||
int i;
|
||||
|
||||
|
@ -338,8 +380,6 @@ flatpak_complete_install (FlatpakCompletion *completion)
|
|||
|
||||
kinds = flatpak_kinds_from_bools (opt_app, opt_runtime);
|
||||
|
||||
flatpak_completion_debug ("install argc %d", completion->argc);
|
||||
|
||||
switch (completion->argc)
|
||||
{
|
||||
case 0:
|
||||
|
@ -358,37 +398,8 @@ flatpak_complete_install (FlatpakCompletion *completion)
|
|||
|
||||
break;
|
||||
|
||||
case 2: /* Name */
|
||||
refs = flatpak_dir_find_remote_refs (dir, completion->argv[1], NULL, NULL,
|
||||
opt_arch, kinds,
|
||||
NULL, &error);
|
||||
if (refs == NULL)
|
||||
flatpak_completion_debug ("find remote refs error: %s", error->message);
|
||||
for (i = 0; refs != NULL && refs[i] != NULL; i++)
|
||||
{
|
||||
g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], NULL);
|
||||
if (parts)
|
||||
flatpak_complete_word (completion, "%s ", parts[1]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3: /* Branch */
|
||||
refs = flatpak_dir_find_remote_refs (dir, completion->argv[1], completion->argv[2], NULL,
|
||||
opt_arch, kinds,
|
||||
NULL, &error);
|
||||
if (refs == NULL)
|
||||
flatpak_completion_debug ("find remote refs error: %s", error->message);
|
||||
for (i = 0; refs != NULL && refs[i] != NULL; i++)
|
||||
{
|
||||
g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], NULL);
|
||||
if (parts)
|
||||
flatpak_complete_word (completion, "%s ", parts[3]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
flatpak_complete_partial_remote_ref (completion, kinds, opt_arch, dir, completion->argv[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -684,15 +684,16 @@ flatpak_kinds_from_bools (gboolean app, gboolean runtime)
|
|||
}
|
||||
|
||||
gboolean
|
||||
flatpak_split_partial_ref_arg (const char *partial_ref,
|
||||
FlatpakKinds default_kinds,
|
||||
const char *default_arch,
|
||||
const char *default_branch,
|
||||
FlatpakKinds *out_kinds,
|
||||
char **out_id,
|
||||
char **out_arch,
|
||||
char **out_branch,
|
||||
GError **error)
|
||||
_flatpak_split_partial_ref_arg (const char *partial_ref,
|
||||
gboolean validate,
|
||||
FlatpakKinds default_kinds,
|
||||
const char *default_arch,
|
||||
const char *default_branch,
|
||||
FlatpakKinds *out_kinds,
|
||||
char **out_id,
|
||||
char **out_arch,
|
||||
char **out_branch,
|
||||
GError **error)
|
||||
{
|
||||
const char *id_start = NULL;
|
||||
const char *id_end = NULL;
|
||||
|
@ -723,7 +724,7 @@ flatpak_split_partial_ref_arg (const char *partial_ref,
|
|||
id_end = next_element (&partial_ref);
|
||||
id = g_strndup (id_start, id_end - id_start);
|
||||
|
||||
if (!flatpak_is_valid_name (id, &local_error))
|
||||
if (validate && !flatpak_is_valid_name (id, &local_error))
|
||||
return flatpak_fail (error, "Invalid id %s: %s", id, local_error->message);
|
||||
|
||||
arch_start = partial_ref;
|
||||
|
@ -740,7 +741,7 @@ flatpak_split_partial_ref_arg (const char *partial_ref,
|
|||
else
|
||||
branch = g_strdup (default_branch);
|
||||
|
||||
if (branch != NULL && !flatpak_is_valid_branch (branch, &local_error))
|
||||
if (validate && branch != NULL && !flatpak_is_valid_branch (branch, &local_error))
|
||||
return flatpak_fail (error, "Invalid branch %s: %s", branch, local_error->message);
|
||||
|
||||
if (out_kinds)
|
||||
|
@ -755,6 +756,52 @@ flatpak_split_partial_ref_arg (const char *partial_ref,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_split_partial_ref_arg (const char *partial_ref,
|
||||
FlatpakKinds default_kinds,
|
||||
const char *default_arch,
|
||||
const char *default_branch,
|
||||
FlatpakKinds *out_kinds,
|
||||
char **out_id,
|
||||
char **out_arch,
|
||||
char **out_branch,
|
||||
GError **error)
|
||||
{
|
||||
return _flatpak_split_partial_ref_arg (partial_ref,
|
||||
TRUE,
|
||||
default_kinds,
|
||||
default_arch,
|
||||
default_branch,
|
||||
out_kinds,
|
||||
out_id,
|
||||
out_arch,
|
||||
out_branch,
|
||||
error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_split_partial_ref_arg_novalidate (const char *partial_ref,
|
||||
FlatpakKinds default_kinds,
|
||||
const char *default_arch,
|
||||
const char *default_branch,
|
||||
FlatpakKinds *out_kinds,
|
||||
char **out_id,
|
||||
char **out_arch,
|
||||
char **out_branch)
|
||||
{
|
||||
return _flatpak_split_partial_ref_arg (partial_ref,
|
||||
FALSE,
|
||||
default_kinds,
|
||||
default_arch,
|
||||
default_branch,
|
||||
out_kinds,
|
||||
out_id,
|
||||
out_arch,
|
||||
out_branch,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
flatpak_compose_ref (gboolean app,
|
||||
const char *name,
|
||||
|
@ -3851,6 +3898,91 @@ flatpak_complete_ref (FlatpakCompletion *completion,
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
find_current_element (const char *str)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (g_str_has_prefix (str, "app/"))
|
||||
str += strlen ("app/");
|
||||
else if (g_str_has_prefix (str, "runtime/"))
|
||||
str += strlen ("runtime/");
|
||||
|
||||
while (str != NULL && count <= 3)
|
||||
{
|
||||
str = strchr (str, '/');
|
||||
count++;
|
||||
if (str != NULL)
|
||||
str = str + 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_complete_partial_remote_ref (FlatpakCompletion *completion,
|
||||
FlatpakKinds kinds,
|
||||
const char *only_arch,
|
||||
FlatpakDir *dir,
|
||||
const char *remote)
|
||||
{
|
||||
FlatpakKinds matched_kinds;
|
||||
const char *pref;
|
||||
g_autofree char *id = NULL;
|
||||
g_autofree char *arch = NULL;
|
||||
g_autofree char *branch = NULL;
|
||||
g_auto(GStrv) refs = NULL;
|
||||
int element;
|
||||
const char *cur_parts[4] = { NULL };
|
||||
g_autoptr(GError) error = NULL;
|
||||
int i;
|
||||
|
||||
pref = completion->cur;
|
||||
element = find_current_element (pref);
|
||||
|
||||
flatpak_split_partial_ref_arg_novalidate (pref, kinds,
|
||||
NULL, NULL,
|
||||
&matched_kinds, &id, &arch, &branch);
|
||||
|
||||
cur_parts[1] = id;
|
||||
cur_parts[2] = arch ? arch : "";
|
||||
cur_parts[3] = branch ? branch : "";
|
||||
|
||||
refs = flatpak_dir_find_remote_refs (dir, completion->argv[1],
|
||||
(element > 1) ? id : NULL,
|
||||
(element > 3) ? branch : NULL,
|
||||
(element > 2 )? arch : only_arch,
|
||||
matched_kinds, NULL, &error);
|
||||
if (refs == NULL)
|
||||
flatpak_completion_debug ("find remote refs error: %s", error->message);
|
||||
for (i = 0; refs != NULL && refs[i] != NULL; i++)
|
||||
{
|
||||
int j;
|
||||
g_autoptr(GString) comp = NULL;
|
||||
g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], NULL);
|
||||
if (parts == NULL)
|
||||
continue;
|
||||
|
||||
if (!g_str_has_prefix (parts[element], cur_parts[element]))
|
||||
continue;
|
||||
|
||||
comp = g_string_new (pref);
|
||||
g_string_append (comp, parts[element] + strlen (cur_parts[element]));
|
||||
|
||||
/* Only complete on the last part if the user explicitly adds a / */
|
||||
if (element >= 2)
|
||||
{
|
||||
for (j = element + 1; j < 4; j++)
|
||||
{
|
||||
g_string_append (comp, "/");
|
||||
g_string_append (comp, parts[j]);
|
||||
}
|
||||
}
|
||||
|
||||
flatpak_complete_word (completion, "%s", comp->str);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
switch_already_in_line (FlatpakCompletion *completion,
|
||||
GOptionEntry *entry)
|
||||
|
|
|
@ -100,6 +100,14 @@ gboolean flatpak_split_partial_ref_arg (const char *partial_ref,
|
|||
char **out_arch,
|
||||
char **out_branch,
|
||||
GError **error);
|
||||
gboolean flatpak_split_partial_ref_arg_novalidate (const char *partial_ref,
|
||||
FlatpakKinds default_kinds,
|
||||
const char *default_arch,
|
||||
const char *default_branch,
|
||||
FlatpakKinds *out_kinds,
|
||||
char **out_id,
|
||||
char **out_arch,
|
||||
char **out_branch);
|
||||
|
||||
char * flatpak_compose_ref (gboolean app,
|
||||
const char *name,
|
||||
|
@ -478,6 +486,11 @@ void flatpak_complete_word (FlatpakCompletion *completion,
|
|||
...);
|
||||
void flatpak_complete_ref (FlatpakCompletion *completion,
|
||||
OstreeRepo *repo);
|
||||
void flatpak_complete_partial_remote_ref (FlatpakCompletion *completion,
|
||||
FlatpakKinds kinds,
|
||||
const char *only_arch,
|
||||
FlatpakDir *dir,
|
||||
const char *remote);
|
||||
void flatpak_complete_file (FlatpakCompletion *completion);
|
||||
void flatpak_complete_dir (FlatpakCompletion *completion);
|
||||
void flatpak_complete_options (FlatpakCompletion *completion,
|
||||
|
|
|
@ -33,8 +33,7 @@
|
|||
<command>flatpak install</command>
|
||||
<arg choice="opt" rep="repeat">OPTION</arg>
|
||||
<arg choice="plain">REMOTE</arg>
|
||||
<arg choice="plain">NAME</arg>
|
||||
<arg choice="opt">BRANCH</arg>
|
||||
<arg choice="plain" rep="repeat">REF</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>flatpak install</command>
|
||||
|
@ -52,14 +51,21 @@
|
|||
|
||||
<para>
|
||||
Installs an application or runtime. <arg choice="plain">REMOTE</arg> must name
|
||||
an existing remote and <arg choice="plain">NAME</arg> is the name of the
|
||||
application or runtime to install. Optionally, <arg choice="plain">BRANCH</arg> can
|
||||
be specified to install a branch other than the default branch. This required
|
||||
if there are multiple matches in the selected remote.
|
||||
an existing remote and <arg choice="plain">REF</arg> is a reference to the
|
||||
application or runtime to install.
|
||||
</para>
|
||||
<para>
|
||||
By default this looks for both apps and runtime with the given <arg choice="plain">NAME</arg> in
|
||||
the specified <arg choice="plain">REMOTE</arg>, but you can limit this by using the --app or --runtime option.
|
||||
Each <arg choice="plain">REF</arg> arguments is a full or partial indentifier in the
|
||||
flatpak ref format, which looks like "(app|runtime)/ID/ARCH/BRANCH". All elements
|
||||
except ID are optional and can be left out, including the slashes,
|
||||
so most of the time you need only specify ID. Any part left out will be matched
|
||||
against what is in the remote, and if there are multiple matches an error message
|
||||
will list the alternatives.
|
||||
</para>
|
||||
<para>
|
||||
By default this looks for both apps and runtime with the given <arg choice="plain">REF</arg> in
|
||||
the specified <arg choice="plain">REMOTE</arg>, but you can limit this by using the --app or
|
||||
--runtime option, or by supplying the initial element in the REF.
|
||||
</para>
|
||||
<para>
|
||||
Note that flatpak allows one to have multiple branches of an application and runtimes
|
||||
|
@ -134,7 +140,7 @@
|
|||
<term><option>--arch=ARCH</option></term>
|
||||
|
||||
<listitem><para>
|
||||
The architecture to install for.
|
||||
The default architecture to install for, if not given explicitly in the <arg choice="plain">REF</arg>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
@ -175,15 +181,14 @@
|
|||
<term><option>--app</option></term>
|
||||
|
||||
<listitem><para>
|
||||
Only look for an app with the given name.
|
||||
Assume that all <arg choice="plain">REF</arg>s are apps if not explicitly specified.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--runtime</option></term>
|
||||
|
||||
<listitem><para>
|
||||
Only look for an runtime with the given name.
|
||||
Assume that all <arg choice="plain">REF</arg>s are runtimes if not explicitly specified.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
|
Loading…
Reference in New Issue