From a418641892e930f50267a01554af2d6051610b5c Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 16 Oct 2015 15:48:20 +0200 Subject: [PATCH] add build-bundle command --- app/Makefile.am.inc | 1 + app/xdg-app-builtins-build-bundle.c | 198 ++++++++++++++++++++++++++++ app/xdg-app-builtins.h | 1 + app/xdg-app-main.c | 1 + 4 files changed, 201 insertions(+) create mode 100644 app/xdg-app-builtins-build-bundle.c diff --git a/app/Makefile.am.inc b/app/Makefile.am.inc index 4345864d..e07b3beb 100644 --- a/app/Makefile.am.inc +++ b/app/Makefile.am.inc @@ -22,6 +22,7 @@ xdg_app_SOURCES = \ app/xdg-app-builtins-build.c \ app/xdg-app-builtins-build-finish.c \ app/xdg-app-builtins-build-export.c \ + app/xdg-app-builtins-build-bundle.c \ app/xdg-app-builtins-repo-update.c \ app/xdg-app-builtins-document.c \ $(xdp_dbus_built_sources) \ diff --git a/app/xdg-app-builtins-build-bundle.c b/app/xdg-app-builtins-build-bundle.c new file mode 100644 index 00000000..07d4d1a4 --- /dev/null +++ b/app/xdg-app-builtins-build-bundle.c @@ -0,0 +1,198 @@ +/* + * Copyright © 2015 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 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 "libgsystem.h" +#include "libglnx/libglnx.h" + +#include "xdg-app-builtins.h" +#include "xdg-app-utils.h" +#include "xdg-app-chain-input-stream.h" + +static char *opt_arch; +static char *opt_repo_url; +static gboolean opt_runtime = FALSE; +static char **opt_gpg_file; + +static GOptionEntry options[] = { + { "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, "Export runtime instead of app"}, + { "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, "Arch to bundle for", "ARCH" }, + { "repo-url", 0, 0, G_OPTION_ARG_STRING, &opt_repo_url, "Url for repo", "URL" }, + { "gpg-keys", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, "Add GPG key from FILE (- for stdin)", "FILE" }, + + { NULL } +}; + +static GBytes * +read_gpg_data (GCancellable *cancellable, + GError **error) +{ + g_autoptr(GInputStream) source_stream = NULL; + g_autoptr(GOutputStream) mem_stream = NULL; + guint n_keyrings = 0; + g_autoptr(GPtrArray) streams = NULL; + + if (opt_gpg_file != NULL) + n_keyrings = g_strv_length (opt_gpg_file); + + guint ii; + + streams = g_ptr_array_new_with_free_func (g_object_unref); + + for (ii = 0; ii < n_keyrings; ii++) + { + GInputStream *input_stream = NULL; + + if (strcmp (opt_gpg_file[ii], "-") == 0) + { + input_stream = g_unix_input_stream_new (STDIN_FILENO, FALSE); + } + else + { + g_autoptr(GFile) file = g_file_new_for_path (opt_gpg_file[ii]); + input_stream = G_INPUT_STREAM(g_file_read (file, cancellable, error)); + + if (input_stream == NULL) + return NULL; + } + + /* Takes ownership. */ + g_ptr_array_add (streams, input_stream); + } + + /* Chain together all the --keyring options as one long stream. */ + source_stream = (GInputStream *) xdg_app_chain_input_stream_new (streams); + + mem_stream = g_memory_output_stream_new_resizable (); + if (g_output_stream_splice (mem_stream, source_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, cancellable, error) < 0) + return NULL; + + return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (mem_stream)); +} + +gboolean +xdg_app_builtin_build_bundle (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GFile) repofile = NULL; + g_autoptr(OstreeRepo) repo = NULL; + g_autoptr(GBytes) gpg_data = NULL; + const char *location; + const char *filename; + const char *name; + const char *branch; + g_autofree char *full_branch = NULL; + g_autofree char *commit_checksum = NULL; + GVariantBuilder metadata_builder; + GVariantBuilder param_builder; + + context = g_option_context_new ("LOCATION FILENAME NAME [BRANCH] - Create a single file bundle from a local repository"); + + if (!xdg_app_option_context_parse (context, options, &argc, &argv, XDG_APP_BUILTIN_FLAG_NO_DIR, NULL, cancellable, error)) + return FALSE; + + if (argc < 4) + return usage_error (context, "LOCATION, FILENAME and NAME must be specified", error); + + location = argv[1]; + filename = argv[2]; + name = argv[3]; + + if (argc >= 5) + branch = argv[4]; + else + branch = "master"; + + repofile = g_file_new_for_commandline_arg (location); + repo = ostree_repo_new (repofile); + + if (!xdg_app_supports_bundles (repo)) + return xdg_app_fail (error, "Your version of ostree is too old to support single-file bundles"); + + if (!g_file_query_exists (repofile, cancellable)) + return xdg_app_fail (error, "'%s' is not a valid repository", location); + + file = g_file_new_for_commandline_arg (filename); + + if (!xdg_app_is_valid_branch (name)) + return xdg_app_fail (error, "'%s' is not a valid name", name); + + if (!xdg_app_is_valid_branch (branch)) + return xdg_app_fail (error, "'%s' is not a valid branch name", branch); + + if (opt_runtime) + full_branch = xdg_app_build_runtime_ref (name, branch, opt_arch); + else + full_branch = xdg_app_build_app_ref (name, branch, opt_arch); + + if (!ostree_repo_open (repo, cancellable, error)) + return FALSE; + + if (!ostree_repo_resolve_rev (repo, full_branch, FALSE, &commit_checksum, error)) + return FALSE; + + g_variant_builder_init (&metadata_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&metadata_builder, "{sv}", "ref", g_variant_new_string (full_branch)); + if (opt_repo_url) + g_variant_builder_add (&metadata_builder, "{sv}", "origin", g_variant_new_string (opt_repo_url)); + + if (opt_gpg_file != NULL) + { + gpg_data = read_gpg_data (cancellable, error); + if (gpg_data == NULL) + return FALSE; + } + + if (gpg_data) + g_variant_builder_add (&metadata_builder, "{sv}", "gpg-keys", + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + g_bytes_get_data (gpg_data, NULL), + g_bytes_get_size (gpg_data), + 1)); + + g_variant_builder_init (¶m_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (¶m_builder, "{sv}", "min-fallback-size", g_variant_new_uint32 (0)); + g_variant_builder_add (¶m_builder, "{sv}", "compression", g_variant_new_byte ('x')); + g_variant_builder_add (¶m_builder, "{sv}", "bsdiff-enabled", g_variant_new_boolean (FALSE)); + g_variant_builder_add (¶m_builder, "{sv}", "inline-parts", g_variant_new_boolean (TRUE)); + g_variant_builder_add (¶m_builder, "{sv}", "include-detached", g_variant_new_boolean (TRUE)); + g_variant_builder_add (¶m_builder, "{sv}", "filename", g_variant_new_bytestring (gs_file_get_path_cached (file))); + + if (!ostree_repo_static_delta_generate (repo, + OSTREE_STATIC_DELTA_GENERATE_OPT_LOWLATENCY, + /* from */ NULL, + commit_checksum, + g_variant_builder_end (&metadata_builder), + g_variant_builder_end (¶m_builder), + cancellable, + error)) + return FALSE; + + return TRUE; +} diff --git a/app/xdg-app-builtins.h b/app/xdg-app-builtins.h index 2a5ff63a..e5e24353 100644 --- a/app/xdg-app-builtins.h +++ b/app/xdg-app-builtins.h @@ -68,6 +68,7 @@ BUILTINPROTO(build_init); BUILTINPROTO(build); BUILTINPROTO(build_finish); BUILTINPROTO(build_export); +BUILTINPROTO(build_bundle); BUILTINPROTO(repo_update); BUILTINPROTO(export_file); BUILTINPROTO(override); diff --git a/app/xdg-app-main.c b/app/xdg-app-main.c index 987654f1..144494cd 100644 --- a/app/xdg-app-main.c +++ b/app/xdg-app-main.c @@ -63,6 +63,7 @@ static XdgAppCommand commands[] = { { "build", xdg_app_builtin_build }, { "build-finish", xdg_app_builtin_build_finish }, { "build-export", xdg_app_builtin_build_export }, + { "build-bundle", xdg_app_builtin_build_bundle }, { "dump-runtime", xdg_app_builtin_dump_runtime }, { "repo-update", xdg_app_builtin_repo_update }, { NULL }