From 06e57a081d10c7978025db29fe0b68d0d8717967 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 18 Dec 2014 11:06:37 +0100 Subject: [PATCH] Add install-runtime builtin --- Makefile.am | 1 + xdg-app-builtins-install.c | 92 +++++++++++++++++++ xdg-app-builtins.h | 1 + xdg-app-dir.c | 179 +++++++++++++++++++++++++++++++++++++ xdg-app-dir.h | 18 ++++ xdg-app-main.c | 1 + 6 files changed, 292 insertions(+) create mode 100644 xdg-app-builtins-install.c diff --git a/Makefile.am b/Makefile.am index 47fec5cb..f303e462 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ xdg_app_SOURCES = \ xdg-app-main.c \ xdg-app-builtins.h \ xdg-app-builtins-add-repo.c \ + xdg-app-builtins-install.c \ xdg-app-dir.c \ xdg-app-dir.h \ xdg-app-utils.h \ diff --git a/xdg-app-builtins-install.c b/xdg-app-builtins-install.c new file mode 100644 index 00000000..83f6def4 --- /dev/null +++ b/xdg-app-builtins-install.c @@ -0,0 +1,92 @@ +#include "config.h" + +#include +#include +#include +#include + +#include "libgsystem.h" + +#include "xdg-app-builtins.h" +#include "xdg-app-utils.h" + +static char *opt_arch; + +static GOptionEntry options[] = { + { "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, "Arch to install for", NULL }, + { NULL } +}; + +static void +usage_error (GOptionContext *context, const char *message, GError **error) +{ + gs_free gchar *help = g_option_context_get_help (context, TRUE, NULL); + g_printerr ("%s", help); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message); +} + +gboolean +xdg_app_builtin_install_runtime (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + gboolean ret = FALSE; + GOptionContext *context; + gs_unref_object XdgAppDir *dir = NULL; + gs_unref_object GFile *deploy_base = NULL; + gs_unref_object GFile *origin = NULL; + const char *repository; + const char *runtime; + const char *branch = "master"; + gs_free char *ref = NULL; + gboolean created_deploy_base = FALSE; + + context = g_option_context_new ("REPOSITORY RUNTIME [BRANCH] - Install a runtime"); + + if (!xdg_app_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) + goto out; + + if (argc < 3) + { + usage_error (context, "REPOSITORY and RUNTIME must be specified", error); + goto out; + } + + repository = argv[1]; + runtime = argv[2]; + if (argc >= 4) + branch = argv[3]; + + ref = xdg_app_build_runtime_ref (runtime, branch, opt_arch); + + deploy_base = xdg_app_dir_get_deploy_dir (dir, ref); + if (g_file_query_exists (deploy_base, cancellable)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Runtime %s branch %s already installed", runtime, branch); + goto out; + } + + if (!xdg_app_dir_pull (dir, repository, ref, + cancellable, error)) + goto out; + + if (!g_file_make_directory_with_parents (deploy_base, cancellable, error)) + goto out; + created_deploy_base = TRUE; + + origin = g_file_get_child (deploy_base, "origin"); + if (!g_file_replace_contents (origin, repository, strlen (repository), NULL, FALSE, + G_FILE_CREATE_NONE, NULL, cancellable, error)) + goto out; + + if (!xdg_app_dir_deploy (dir, ref, NULL, cancellable, error)) + goto out; + + ret = TRUE; + + out: + if (created_deploy_base && !ret) + gs_shutil_rm_rf (deploy_base, cancellable, NULL); + + if (context) + g_option_context_free (context); + return ret; +} diff --git a/xdg-app-builtins.h b/xdg-app-builtins.h index 70ae704d..66e2a6fb 100644 --- a/xdg-app-builtins.h +++ b/xdg-app-builtins.h @@ -25,6 +25,7 @@ gboolean xdg_app_option_context_parse (GOptionContext *context, #define BUILTINPROTO(name) gboolean xdg_app_builtin_ ## name (int argc, char **argv, GCancellable *cancellable, GError **error) BUILTINPROTO(add_repo); +BUILTINPROTO(install_runtime); #undef BUILTINPROTO diff --git a/xdg-app-dir.c b/xdg-app-dir.c index 4264471b..c7ffba8c 100644 --- a/xdg-app-dir.c +++ b/xdg-app-dir.c @@ -19,6 +19,8 @@ typedef struct { G_DEFINE_TYPE (XdgAppDir, xdg_app_dir, G_TYPE_OBJECT) +G_DEFINE_QUARK (xdg-app-dir-error-quark, xdg_app_dir_error) + enum { PROP_0, @@ -26,6 +28,9 @@ enum { PROP_PATH }; +#define OSTREE_GIO_FAST_QUERYINFO ("standard::name,standard::type,standard::size,standard::is-symlink,standard::symlink-target," \ + "unix::device,unix::inode,unix::mode,unix::uid,unix::gid,unix::rdev") + GFile * xdg_app_get_system_base_dir_location (void) { @@ -199,6 +204,180 @@ xdg_app_dir_ensure_repo (XdgAppDir *self, return ret; } +static void +pull_progress (OstreeAsyncProgress *progress, + gpointer user_data) +{ + GSConsole *console = user_data; + GString *buf; + gs_free char *status = NULL; + guint outstanding_fetches; + guint outstanding_writes; + guint n_scanned_metadata; + + if (!console) + return; + + buf = g_string_new (""); + + status = ostree_async_progress_get_status (progress); + outstanding_fetches = ostree_async_progress_get_uint (progress, "outstanding-fetches"); + outstanding_writes = ostree_async_progress_get_uint (progress, "outstanding-writes"); + n_scanned_metadata = ostree_async_progress_get_uint (progress, "scanned-metadata"); + if (status) + { + g_string_append (buf, status); + } + else if (outstanding_fetches) + { + guint64 bytes_transferred = ostree_async_progress_get_uint64 (progress, "bytes-transferred"); + guint fetched = ostree_async_progress_get_uint (progress, "fetched"); + guint requested = ostree_async_progress_get_uint (progress, "requested"); + guint64 bytes_sec = (g_get_monotonic_time () - ostree_async_progress_get_uint64 (progress, "start-time")) / G_USEC_PER_SEC; + gs_free char *formatted_bytes_transferred = + g_format_size_full (bytes_transferred, 0); + gs_free char *formatted_bytes_sec = NULL; + + if (!bytes_sec) // Ignore first second + formatted_bytes_sec = g_strdup ("-"); + else + { + bytes_sec = bytes_transferred / bytes_sec; + formatted_bytes_sec = g_format_size (bytes_sec); + } + + g_string_append_printf (buf, "Receiving objects: %u%% (%u/%u) %s/s %s", + (guint)((((double)fetched) / requested) * 100), + fetched, requested, formatted_bytes_sec, formatted_bytes_transferred); + } + else if (outstanding_writes) + { + g_string_append_printf (buf, "Writing objects: %u", outstanding_writes); + } + else + { + g_string_append_printf (buf, "Scanning metadata: %u", n_scanned_metadata); + } + + gs_console_begin_status_line (console, buf->str, NULL, NULL); + + g_string_free (buf, TRUE); +} + +gboolean +xdg_app_dir_pull (XdgAppDir *self, + const char *repository, + const char *ref, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GSConsole *console = NULL; + gs_unref_object OstreeAsyncProgress *progress = NULL; + const char *refs[2]; + + if (!xdg_app_dir_ensure_repo (self, cancellable, error)) + goto out; + + console = gs_console_get (); + if (console) + { + gs_console_begin_status_line (console, "", NULL, NULL); + progress = ostree_async_progress_new_and_connect (pull_progress, console); + } + + refs[0] = ref; + refs[1] = NULL; + if (!ostree_repo_pull (self->repo, repository, + (char **)refs, OSTREE_REPO_PULL_FLAGS_NONE, + progress, + cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +gboolean +xdg_app_dir_deploy (XdgAppDir *self, + const char *ref, + const char *hash, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_free char *resolved_ref = NULL; + gs_free char *tmpname = NULL; + gs_unref_object GFile *root = NULL; + gs_unref_object GFile *latest_tmp_link = NULL; + gs_unref_object GFile *latest_link = NULL; + gs_unref_object GFileInfo *file_info = NULL; + gs_unref_object GFile *deploy_base = NULL; + gs_unref_object GFile *checkoutdir = NULL; + gs_unref_object GFile *dotref = NULL; + + if (!xdg_app_dir_ensure_repo (self, cancellable, error)) + goto out; + + if (hash == NULL) + { + if (!ostree_repo_resolve_rev (self->repo, ref, FALSE, &resolved_ref, error)) + goto out; + + hash = resolved_ref; + } + + deploy_base = xdg_app_dir_get_deploy_dir (self, ref); + + checkoutdir = g_file_get_child (deploy_base, hash); + if (g_file_query_exists (checkoutdir, cancellable)) + { + g_set_error (error, XDG_APP_DIR_ERROR, + XDG_APP_DIR_ERROR_ALREADY_DEPLOYED, + "%s version %s already deployed", ref, hash); + goto out; + } + + if (!ostree_repo_read_commit (self->repo, hash, &root, NULL, cancellable, error)) + goto out; + + file_info = g_file_query_info (root, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + if (file_info == NULL) + goto out; + + if (!ostree_repo_checkout_tree (self->repo, + self->user ? OSTREE_REPO_CHECKOUT_MODE_USER : OSTREE_REPO_CHECKOUT_MODE_NONE, + OSTREE_REPO_CHECKOUT_OVERWRITE_NONE, + checkoutdir, + OSTREE_REPO_FILE (root), file_info, + cancellable, error)) + goto out; + + dotref = g_file_get_child (checkoutdir, ".ref"); + if (!g_file_replace_contents (dotref, "", 0, NULL, FALSE, + G_FILE_CREATE_NONE, NULL, cancellable, error)) + goto out; + + + tmpname = gs_fileutil_gen_tmp_name (".latest-", NULL); + latest_tmp_link = g_file_get_child (deploy_base, tmpname); + if (!g_file_make_symbolic_link (latest_tmp_link, hash, cancellable, error)) + goto out; + + latest_link = g_file_get_child (deploy_base, "latest"); + if (!gs_file_rename (latest_tmp_link, + latest_link, + cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + XdgAppDir* xdg_app_dir_new (GFile *path, gboolean user) { diff --git a/xdg-app-dir.h b/xdg-app-dir.h index e5abd40a..caf54ccd 100644 --- a/xdg-app-dir.h +++ b/xdg-app-dir.h @@ -11,6 +11,14 @@ typedef struct XdgAppDir XdgAppDir; GType xdg_app_dir_get_type (void); +#define XDG_APP_DIR_ERROR xdg_app_dir_error_quark() + +typedef enum { + XDG_APP_DIR_ERROR_ALREADY_DEPLOYED, +} XdgAppErrorEnum; + +GQuark xdg_app_dir_error_quark (void); + GFile * xdg_app_get_system_base_dir_location (void); GFile * xdg_app_get_user_base_dir_location (void); @@ -30,5 +38,15 @@ gboolean xdg_app_dir_ensure_path (XdgAppDir *self, gboolean xdg_app_dir_ensure_repo (XdgAppDir *self, GCancellable *cancellable, GError **error); +gboolean xdg_app_dir_pull (XdgAppDir *self, + const char *repository, + const char *ref, + GCancellable *cancellable, + GError **error); +gboolean xdg_app_dir_deploy (XdgAppDir *self, + const char *ref, + const char *hash, + GCancellable *cancellable, + GError **error); #endif /* __XDG_APP_DIR_H__ */ diff --git a/xdg-app-main.c b/xdg-app-main.c index b3d7cd36..24a7f721 100644 --- a/xdg-app-main.c +++ b/xdg-app-main.c @@ -20,6 +20,7 @@ typedef struct { static XdgAppCommand commands[] = { { "add-repo", xdg_app_builtin_add_repo }, + { "install-runtime", xdg_app_builtin_install_runtime }, { NULL } };