diff --git a/app/Makefile.am.inc b/app/Makefile.am.inc index 034dab6c..02d23f01 100644 --- a/app/Makefile.am.inc +++ b/app/Makefile.am.inc @@ -13,6 +13,7 @@ flatpak_SOURCES = \ app/flatpak-builtins-delete-remote.c \ app/flatpak-builtins-list-remotes.c \ app/flatpak-builtins-ls-remote.c \ + app/flatpak-builtins-info-remote.c \ app/flatpak-builtins-install.c \ app/flatpak-builtins-override.c \ app/flatpak-builtins-make-current.c \ diff --git a/app/flatpak-builtins-info-remote.c b/app/flatpak-builtins-info-remote.c new file mode 100644 index 00000000..b4e30208 --- /dev/null +++ b/app/flatpak-builtins-info-remote.c @@ -0,0 +1,327 @@ +/* + * Copyright © 2017 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 . + * + * Authors: + * Alexander Larsson + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#include "libglnx/libglnx.h" + +#include "flatpak-builtins.h" +#include "flatpak-utils.h" +#include "flatpak-table-printer.h" + +static char *opt_arch; +static char *opt_commit; +static gboolean opt_runtime; +static gboolean opt_app; +static gboolean opt_show_ref; +static gboolean opt_show_commit; +static gboolean opt_show_parent; +static gboolean opt_show_metadata; +static gboolean opt_log; + +static GOptionEntry options[] = { + { "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to install for"), N_("ARCH") }, + { "commit", 0, 0, G_OPTION_ARG_STRING, &opt_commit, N_("Commit to show info for"), N_("COMMIT") }, + { "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 }, + { "log", 0, 0, G_OPTION_ARG_NONE, &opt_log, N_("Display log"), NULL }, + { "show-ref", 'r', 0, G_OPTION_ARG_NONE, &opt_show_ref, N_("Show ref"), NULL }, + { "show-commit", 'c', 0, G_OPTION_ARG_NONE, &opt_show_commit, N_("Show commit"), NULL }, + { "show-parent", 'p', 0, G_OPTION_ARG_NONE, &opt_show_parent, N_("Show parent"), NULL }, + { "show-metadata", 'm', 0, G_OPTION_ARG_NONE, &opt_show_metadata, N_("Show metadata"), NULL }, + { NULL } +}; + +static void +maybe_print_space (gboolean *first) +{ + if (*first) + *first = FALSE; + else + g_print (" "); +} + +static gchar * +format_timestamp (guint64 timestamp) +{ + GDateTime *dt; + gchar *str; + + dt = g_date_time_new_from_unix_utc (timestamp); + if (dt == NULL) + return g_strdup ("?"); + + str = g_date_time_format (dt, "%Y-%m-%d %H:%M:%S +0000"); + g_date_time_unref (dt); + + return str; +} + + +gboolean +flatpak_builtin_info_remote (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FlatpakDir) dir = NULL; + g_autoptr(GVariant) commit_v = NULL; + g_autoptr(GVariant) commit_metadata = NULL; + const char *remote; + const char *pref; + g_autofree char *default_branch = NULL; + FlatpakKinds kinds; + FlatpakKinds matched_kinds; + g_autofree char *id = NULL; + g_autofree char *arch = NULL; + g_autofree char *branch = NULL; + g_auto(GStrv) parts = NULL; + FlatpakKinds kind; + g_autofree char *ref = NULL; + g_autofree char *commit = NULL; + g_autofree char *parent = NULL; + const char *on = ""; + const char *off = ""; + gboolean friendly = TRUE; + const char *xa_metadata = NULL; + g_autoptr(GKeyFile) metakey = NULL; + guint64 installed_size = 0; + guint64 download_size = 0; + g_autofree char *formatted_installed_size = NULL; + g_autofree char *formatted_download_size = NULL; + const gchar *subject; + const gchar *body; + guint64 timestamp; + g_autofree char *formatted_timestamp = NULL; + + context = g_option_context_new (_(" REMOTE REF - Show information about installed application and/or runtime in a remote")); + 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_app && !opt_runtime) + opt_app = opt_runtime = TRUE; + + if (argc < 3) + return usage_error (context, _("REMOTE and REF must be specified"), error); + + remote = argv[1]; + pref = argv[2]; + + default_branch = flatpak_dir_get_remote_default_branch (dir, remote); + kinds = flatpak_kinds_from_bools (opt_app, opt_runtime); + + if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, NULL, + &matched_kinds, &id, &arch, &branch, error)) + return FALSE; + + ref = flatpak_dir_find_remote_ref (dir, remote, id, branch, default_branch, arch, + matched_kinds, &kind, cancellable, error); + if (ref == NULL) + return FALSE; + + commit_v = flatpak_dir_fetch_remote_commit (dir, remote, ref, opt_commit, &commit, cancellable, error); + if (commit_v == NULL) + return FALSE; + + if (flatpak_fancy_output ()) + { + on = FLATPAK_ANSI_BOLD_ON; + off = FLATPAK_ANSI_BOLD_OFF; /* bold off */ + } + + if (opt_show_ref || opt_show_commit || opt_show_parent || opt_show_metadata) + friendly = FALSE; + + if (friendly) + { + g_variant_get (commit_v, "(a{sv}aya(say)&s&stayay)", NULL, NULL, NULL, + &subject, &body, NULL, NULL, NULL); + + parent = ostree_commit_get_parent (commit_v); + timestamp = ostree_commit_get_timestamp (commit_v); + + commit_metadata = g_variant_get_child_value (commit_v, 0); + g_variant_lookup (commit_metadata, "xa.metadata", "&s", &xa_metadata); + if (xa_metadata == NULL) + return flatpak_fail (error, "Commit has no metadata"); + + if (g_variant_lookup (commit_metadata, "xa.installed-size", "t", &installed_size)) + installed_size = GUINT64_FROM_BE (installed_size); + + if (g_variant_lookup (commit_metadata, "xa.download-size", "t", &download_size)) + download_size = GUINT64_FROM_BE (download_size); + + metakey = g_key_file_new (); + if (!g_key_file_load_from_data (metakey, xa_metadata, -1, 0, error)) + return FALSE; + + parts = g_strsplit (ref, "/", 0); + formatted_installed_size = g_format_size (installed_size); + formatted_download_size = g_format_size (download_size); + formatted_timestamp = format_timestamp (timestamp); + + g_print ("%s%s%s %s\n", on, _("Ref:"), off, ref); + g_print ("%s%s%s %s\n", on, _("ID:"), off, parts[1]); + g_print ("%s%s%s %s\n", on, _("Arch:"), off, parts[2]); + g_print ("%s%s%s %s\n", on, _("Branch:"), off, parts[3]); + g_print ("%s%s%s %s\n", on, _("Date:"), off, formatted_timestamp); + g_print ("%s%s%s %s\n", on, _("Subject:"), off, subject); + g_print ("%s%s%s %s\n", on, _("Commit:"), off, commit); + g_print ("%s%s%s %s\n", on, _("Parent:"), off, parent ? parent : "-"); + g_print ("%s%s%s %s\n", on, _("Download size:"), off, formatted_download_size); + g_print ("%s%s%s %s\n", on, _("Installed size:"), off, formatted_installed_size); + if (strcmp (parts[0], "app") == 0) + { + g_autofree char *runtime = NULL; + runtime = g_key_file_get_string (metakey, "Application", "runtime", error); + g_print ("%s%s%s %s\n", on, _("Runtime:"), off, runtime ? runtime : "-"); + } + + if (opt_log) + { + g_autofree char *p = g_strdup (parent); + + g_print ("%s%s%s", on, _("History:"), off); + + while (p) + { + g_autofree char *p_parent = NULL; + const gchar *p_subject; + guint64 p_timestamp; + g_autofree char *p_formatted_timestamp = NULL; + g_autoptr(GVariant) p_commit_v = NULL; + + p_commit_v = flatpak_dir_fetch_remote_commit (dir, remote, ref, p, NULL, cancellable, NULL); + if (p_commit_v == NULL) + break; + + p_parent = ostree_commit_get_parent (p_commit_v); + p_timestamp = ostree_commit_get_timestamp (p_commit_v); + p_formatted_timestamp = format_timestamp (p_timestamp); + + g_variant_get (p_commit_v, "(a{sv}aya(say)&s&stayay)", NULL, NULL, NULL, + &p_subject, NULL, NULL, NULL, NULL); + + g_print ("\n"); + g_print ("%s%s%s %s\n", on, _(" Subject:"), off, p_subject); + g_print ("%s%s%s %s\n", on, _(" Date:"), off, p_formatted_timestamp); + g_print ("%s%s%s %s\n", on, _(" Commit:"), off, p); + + g_free (p); + p = g_steal_pointer (&p_parent); + } + } + } + else + { + g_autoptr(GVariant) c_v = g_variant_ref (commit_v); + g_autofree char *c = g_strdup (commit); + + do + { + g_autofree char *p = ostree_commit_get_parent (c_v); + gboolean first = TRUE; + + if (opt_show_ref) + { + maybe_print_space (&first); + g_print ("%s", ref); + } + + if (opt_show_commit) + { + maybe_print_space (&first); + g_print ("%s", c); + } + + if (opt_show_parent) + { + maybe_print_space (&first); + g_print ("%s", p ? p : "-"); + } + + if (!first) + g_print ("\n"); + + if (opt_show_metadata) + g_print ("%s", xa_metadata); + + g_free (c); + c = g_steal_pointer (&p); + + g_variant_unref (c_v); + c_v = NULL; + + if (c && opt_log) + c_v = flatpak_dir_fetch_remote_commit (dir, remote, ref, c, NULL, cancellable, NULL); + } + while (c_v != NULL); + } + + return TRUE; +} + +gboolean +flatpak_complete_info_remote (FlatpakCompletion *completion) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FlatpakDir) dir = NULL; + FlatpakKinds kinds; + int i; + + context = g_option_context_new (""); + + if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, 0, &dir, NULL, NULL)) + return FALSE; + + kinds = flatpak_kinds_from_bools (opt_app, opt_runtime); + + switch (completion->argc) + { + case 0: + case 1: /* REMOTE */ + flatpak_complete_options (completion, global_entries); + flatpak_complete_options (completion, options); + flatpak_complete_options (completion, user_entries); + + { + g_auto(GStrv) remotes = flatpak_dir_list_remotes (dir, NULL, NULL); + if (remotes == NULL) + return FALSE; + for (i = 0; remotes[i] != NULL; i++) + flatpak_complete_word (completion, "%s ", remotes[i]); + } + + break; + + default: /* REF */ + flatpak_complete_partial_ref (completion, kinds, opt_arch, dir, completion->argv[1]); + break; + } + + return TRUE; +} diff --git a/app/flatpak-builtins.h b/app/flatpak-builtins.h index 700f2eda..d3c9e5ca 100644 --- a/app/flatpak-builtins.h +++ b/app/flatpak-builtins.h @@ -59,6 +59,7 @@ BUILTINPROTO (add_remote) BUILTINPROTO (modify_remote) BUILTINPROTO (delete_remote) BUILTINPROTO (ls_remote) +BUILTINPROTO (info_remote) BUILTINPROTO (list_remotes) BUILTINPROTO (install) BUILTINPROTO (update) diff --git a/app/flatpak-main.c b/app/flatpak-main.c index 6dfd795c..856790bd 100644 --- a/app/flatpak-main.c +++ b/app/flatpak-main.c @@ -92,6 +92,7 @@ static FlatpakCommand commands[] = { { "remote-delete", N_("Delete a configured remote"), flatpak_builtin_delete_remote, flatpak_complete_delete_remote }, { "remote-list", NULL, flatpak_builtin_list_remotes, flatpak_complete_list_remotes, TRUE }, { "remote-ls", N_("List contents of a configured remote"), flatpak_builtin_ls_remote, flatpak_complete_ls_remote }, + { "remote-info", N_("Show information about a remote app or runtime"), flatpak_builtin_info_remote, flatpak_complete_info_remote }, /* translators: please keep the leading newline and space */ { N_("\n Build applications") }, diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index d6efb7fd..9716a583 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -3429,7 +3429,7 @@ flatpak_dir_pull_untrusted_local (FlatpakDir *self, g_autofree const char **commit_refs = NULL; if (!g_variant_lookup (new_commit_metadata, - "ostree.ref-binding", + OSTREE_COMMIT_META_KEY_REF_BINDING, "^a&s", &commit_refs)) { @@ -9779,7 +9779,6 @@ flatpak_dir_fetch_remote_object (FlatpakDir *self, g_autofree char *object_url = NULL; g_autofree char *part1 = NULL; g_autofree char *part2 = NULL; - g_autoptr(GBytes) bytes = NULL; if (!ostree_repo_remote_get_url (self->repo, remote_name, &base_url, error)) @@ -9844,9 +9843,9 @@ flatpak_dir_fetch_remote_commit (FlatpakDir *self, const char *xa_ref = NULL; g_autofree const char **commit_refs = NULL; - if ((g_variant_lookup (commit_metadata, "xa.ref", "s", &xa_ref) && + if ((g_variant_lookup (commit_metadata, "xa.ref", "&s", &xa_ref) && g_strcmp0 (xa_ref, ref) != 0) || - (g_variant_lookup (commit_metadata, "ostree.ref-binding", "^a&s", &commit_refs) && + (g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_REF_BINDING, "^a&s", &commit_refs) && !g_strv_contains ((const char *const *) commit_refs, ref))) { flatpak_fail (error, "commit has no requested ref ‘%s’ in ref binding metadata", ref); diff --git a/doc/Makefile.am b/doc/Makefile.am index c81460b3..9e6d1f45 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -24,6 +24,7 @@ man1 = \ flatpak-remote-delete.1 \ flatpak-remote-modify.1 \ flatpak-remote-ls.1 \ + flatpak-remote-info.1 \ flatpak-install.1 \ flatpak-config.1 \ flatpak-update.1 \ diff --git a/doc/flatpak-remote-info.xml b/doc/flatpak-remote-info.xml new file mode 100644 index 00000000..07eed531 --- /dev/null +++ b/doc/flatpak-remote-info.xml @@ -0,0 +1,231 @@ + + + + + + + flatpak remote-info + flatpak + + + + Developer + Alexander + Larsson + alexl@redhat.com + + + + + + flatpak remote-info + 1 + + + + flatpak-remote-info + Show information about an application or runtime in a remote + + + + + flatpak remote-info + OPTION + REMOTE + REF + + + + + Description + + + Shows information about the runtime or application REF from the + remote repository with the name REMOTE. + You can find all configured remote repositories with flatpak remotes. + + + By default, the output is formatted in a friendly format. + If you specify one of the options --show-ref, --show-commit, + --show-parent, or --show-metadata, the output is instead formatted + in a machine-readable format. + + + Unless overridden with the --user or --installation options, this command + uses the default system-wide configuration. + + + + + + Options + + The following options are understood: + + + + + + + + Show help options and exit. + + + + + + + + Use the per-user configuration. + + + + + + + + Use the default system-wide configuration. + + + + + + + + Use a system-wide installation specified by NAME + among those defined in /etc/flatpak/installations.d/. + Using --installation=default is equivalent to using + --system. + + + + + + + + Assume that REF is a runtimes if not explicitly specified. + + + + + + + + Assume that REF is an apps if not explicitly specified. + + + + + + + + The default architecture to look for, if not given explicitly in the REF. + + + + + + + + Show information about the specific commit, rather than the latest version. + + + + + + + + Display a log of previous versions. + + + + + + + + + Show the matched ref. + + + + + + + + + Show the commit id. + + + + + + + + + Show the parent commit id. + + + + + + + + + Show the metadata. + + + + + + + + + Print debug information during command processing. + + + + + + + + Print OSTree debug information during command processing. + + + + + + + + Examples + + + $ flatpak --user remote-info flathub org.gnome.gedit + + +Ref: app/org.gnome.gedit/x86_64/stable +ID: org.gnome.gedit +Arch: x86_64 +Branch: stable +Date: 2017-07-31 16:05:22 +0000 +Subject: Build org.gnome.gedit at 3ec291fc1ce4d78220527fa79576f4cc1481ebe5 +Commit: 3de7e9dde3bb8382aad9dfbbff20eccd9bf2100bc1887a3619ec0372e8066bf7 +Parent: - +Download size: 3,4 MB +Installed size: 11,1 MB +Runtime: org.gnome.Platform/x86_64/3.24 + + + + + + See also + + + flatpak1, + flatpak-remotes1 + flatpak-remote-ls1 + + + + diff --git a/doc/flatpak.xml b/doc/flatpak.xml index 677fd1ef..72c0e13b 100644 --- a/doc/flatpak.xml +++ b/doc/flatpak.xml @@ -318,6 +318,13 @@ List contents of a configured remote repository. + + flatpak-remote-info1 + + + Show information about a ref in a configured remote repository. + + Commands for building applications: