From 9285d055abfe81f2a7e2b6609263655ff338a982 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 15 Dec 2015 12:26:59 +0100 Subject: [PATCH] lib: Add xdg_app_installation_install --- app/xdg-app-builtins-install.c | 4 +- app/xdg-app-builtins-update.c | 4 +- common/xdg-app-dir.c | 21 +++- common/xdg-app-dir.h | 2 + lib/test-lib.c | 30 ++++- lib/xdg-app-installation.c | 219 +++++++++++++++++++++++++++++++++ lib/xdg-app-installation.h | 15 +++ 7 files changed, 285 insertions(+), 10 deletions(-) diff --git a/app/xdg-app-builtins-install.c b/app/xdg-app-builtins-install.c index 713cd5d24..97373a838 100644 --- a/app/xdg-app-builtins-install.c +++ b/app/xdg-app-builtins-install.c @@ -144,7 +144,7 @@ xdg_app_builtin_install_runtime (int argc, char **argv, GCancellable *cancellabl goto out; } - if (!xdg_app_dir_pull (dir, repository, ref, + if (!xdg_app_dir_pull (dir, repository, ref, NULL, cancellable, error)) goto out; @@ -219,7 +219,7 @@ xdg_app_builtin_install_app (int argc, char **argv, GCancellable *cancellable, G goto out; } - if (!xdg_app_dir_pull (dir, repository, ref, + if (!xdg_app_dir_pull (dir, repository, ref, NULL, cancellable, error)) goto out; diff --git a/app/xdg-app-builtins-update.c b/app/xdg-app-builtins-update.c index ba2fa0c13..fb0c90d71 100644 --- a/app/xdg-app-builtins-update.c +++ b/app/xdg-app-builtins-update.c @@ -78,7 +78,7 @@ xdg_app_builtin_update_runtime (int argc, char **argv, GCancellable *cancellable if (repository == NULL) return FALSE; - if (!xdg_app_dir_pull (dir, repository, ref, + if (!xdg_app_dir_pull (dir, repository, ref, NULL, cancellable, error)) return FALSE; @@ -148,7 +148,7 @@ xdg_app_builtin_update_app (int argc, char **argv, GCancellable *cancellable, GE if (repository == NULL) return FALSE; - if (!xdg_app_dir_pull (dir, repository, ref, + if (!xdg_app_dir_pull (dir, repository, ref, NULL, cancellable, error)) return FALSE; diff --git a/common/xdg-app-dir.c b/common/xdg-app-dir.c index ab0eaa8ee..8f9f48308 100644 --- a/common/xdg-app-dir.c +++ b/common/xdg-app-dir.c @@ -556,22 +556,27 @@ gboolean xdg_app_dir_pull (XdgAppDir *self, const char *repository, const char *ref, + OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GSConsole *console = NULL; - g_autoptr(OstreeAsyncProgress) progress = NULL; + g_autoptr(OstreeAsyncProgress) console_progress = NULL; const char *refs[2]; if (!xdg_app_dir_ensure_repo (self, cancellable, error)) goto out; - console = gs_console_get (); - if (console) + if (progress == NULL) { - gs_console_begin_status_line (console, "", NULL, NULL); - progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console); + console = gs_console_get (); + if (console) + { + gs_console_begin_status_line (console, "", NULL, NULL); + console_progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console); + progress = console_progress; + } } refs[0] = ref; @@ -1986,6 +1991,12 @@ xdg_app_dir_new (GFile *path, gboolean user) return g_object_new (XDG_APP_TYPE_DIR, "path", path, "user", user, NULL); } +XdgAppDir* +xdg_app_dir_clone (XdgAppDir *self) +{ + return xdg_app_dir_new (self->basedir, self->user); +} + XdgAppDir * xdg_app_dir_get_system (void) { diff --git a/common/xdg-app-dir.h b/common/xdg-app-dir.h index 7e9fee27c..1feb69f52 100644 --- a/common/xdg-app-dir.h +++ b/common/xdg-app-dir.h @@ -70,6 +70,7 @@ GKeyFile * xdg_app_deploy_get_metadata (XdgAppDeploy *deploy); XdgAppDir* xdg_app_dir_new (GFile *basedir, gboolean user); +XdgAppDir* xdg_app_dir_clone (XdgAppDir *self); XdgAppDir *xdg_app_dir_get (gboolean user); XdgAppDir *xdg_app_dir_get_system (void); XdgAppDir *xdg_app_dir_get_user (void); @@ -111,6 +112,7 @@ gboolean xdg_app_dir_ensure_repo (XdgAppDir *self, gboolean xdg_app_dir_pull (XdgAppDir *self, const char *repository, const char *ref, + OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error); gboolean xdg_app_dir_list_refs_for_name (XdgAppDir *self, diff --git a/lib/test-lib.c b/lib/test-lib.c index f594ed581..91a7ba490 100644 --- a/lib/test-lib.c +++ b/lib/test-lib.c @@ -4,6 +4,16 @@ #include + +static void +progress_cb (const char *status, + guint progress, + gboolean estimating, + gpointer user_data) +{ + g_print ("status: %s, progress: %d estimating: %d, user_data: %p\n", status, progress, estimating, user_data); +} + int main (int argc, char *argv[]) { @@ -15,10 +25,29 @@ main (int argc, char *argv[]) XdgAppInstalledRef **runtimes; XdgAppRemoteRef *remote_ref; XdgAppRemote **remotes; + GError *error = NULL; int i, j; installation = xdg_app_installation_new_user (); + if (argc == 3) + { + app1 = xdg_app_installation_install (installation, + argv[1], + XDG_APP_REF_KIND_APP, + argv[2], + NULL, NULL, + progress_cb, (gpointer)0xdeadbeef, + NULL, &error); + if (app1 == NULL) + g_print ("Error: %s\n", error->message); + else + g_print ("Installed %s: %s\n", argv[2], + xdg_app_ref_get_commit (XDG_APP_REF (app1))); + + return 0; + } + g_print ("**** Listing all installed apps\n"); apps = xdg_app_installation_list_installed_refs (installation, XDG_APP_REF_KIND_APP, @@ -97,7 +126,6 @@ main (int argc, char *argv[]) for (i = 0; remotes[i] != NULL; i++) { - GError *error = NULL; g_print ("\nRemote: %s %s %s %d %d\n", xdg_app_remote_get_name (remotes[i]), xdg_app_remote_get_url (remotes[i]), diff --git a/lib/xdg-app-installation.c b/lib/xdg-app-installation.c index 5bc91e183..716e7b387 100644 --- a/lib/xdg-app-installation.c +++ b/lib/xdg-app-installation.c @@ -20,6 +20,7 @@ #include "config.h" +#include "libgsystem.h" #include "xdg-app-utils.h" #include "xdg-app-installation.h" #include "xdg-app-installed-ref-private.h" @@ -345,3 +346,221 @@ xdg_app_installation_load_app_overrides (XdgAppInstallation *self, return metadata_contents; } + +static void +progress_cb (OstreeAsyncProgress *progress, gpointer user_data) +{ + XdgAppProgressCallback progress_cb = g_object_get_data (G_OBJECT (progress), "callback"); + guint last_progress = GPOINTER_TO_UINT(g_object_get_data (G_OBJECT (progress), "last_progress")); + GString *buf; + g_autofree char *status = NULL; + guint outstanding_fetches; + guint outstanding_metadata_fetches; + guint outstanding_writes; + guint n_scanned_metadata; + guint fetched_delta_parts; + guint total_delta_parts; + guint64 bytes_transferred; + guint64 total_delta_part_size; + guint fetched; + guint metadata_fetched; + guint requested; + guint64 ellapsed_time; + guint new_progress = 0; + gboolean estimating = FALSE; + + buf = g_string_new (""); + + status = ostree_async_progress_get_status (progress); + outstanding_fetches = ostree_async_progress_get_uint (progress, "outstanding-fetches"); + outstanding_metadata_fetches = ostree_async_progress_get_uint (progress, "outstanding-metadata-fetches"); + outstanding_writes = ostree_async_progress_get_uint (progress, "outstanding-writes"); + n_scanned_metadata = ostree_async_progress_get_uint (progress, "scanned-metadata"); + fetched_delta_parts = ostree_async_progress_get_uint (progress, "fetched-delta-parts"); + total_delta_parts = ostree_async_progress_get_uint (progress, "total-delta-parts"); + total_delta_part_size = ostree_async_progress_get_uint64 (progress, "total-delta-part-size"); + bytes_transferred = ostree_async_progress_get_uint64 (progress, "bytes-transferred"); + fetched = ostree_async_progress_get_uint (progress, "fetched"); + metadata_fetched = ostree_async_progress_get_uint (progress, "metadata-fetched"); + requested = ostree_async_progress_get_uint (progress, "requested"); + ellapsed_time = (g_get_monotonic_time () - ostree_async_progress_get_uint64 (progress, "start-time")) / G_USEC_PER_SEC; + + if (status) + { + g_string_append (buf, status); + } + else if (outstanding_fetches) + { + guint64 bytes_sec = bytes_transferred / ellapsed_time; + g_autofree char *formatted_bytes_transferred = + g_format_size_full (bytes_transferred, 0); + g_autofree char *formatted_bytes_sec = NULL; + + if (!bytes_sec) // Ignore first second + formatted_bytes_sec = g_strdup ("-"); + else + { + formatted_bytes_sec = g_format_size (bytes_sec); + } + + if (total_delta_parts > 0) + { + g_autofree char *formatted_total = + g_format_size (total_delta_part_size); + g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/s %s/%s", + fetched_delta_parts, total_delta_parts, + formatted_bytes_sec, formatted_bytes_transferred, + formatted_total); + + new_progress = (100 * bytes_transferred) / total_delta_part_size; + } + else if (outstanding_metadata_fetches) + { + /* At this point we don't really know how much data there is, so we have to make a guess. + * Since its really hard to figure out early how much data there is we report 1% until + * all objects are scanned. */ + + new_progress = 1; + estimating = TRUE; + + g_string_append_printf (buf, "Receiving metadata objects: %u/(estimating) %s/s %s", + metadata_fetched, formatted_bytes_sec, formatted_bytes_transferred); + } + else + { + new_progress = (100 * fetched) / requested; + 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); + } + + if (new_progress < last_progress) + new_progress = last_progress; + g_object_set_data (G_OBJECT (progress), "last_progress", GUINT_TO_POINTER(new_progress)); + + progress_cb (buf->str, new_progress, estimating, user_data); + + g_string_free (buf, TRUE); +} + +/** + * xdg_app_installation_install: + * @self: a #XdgAppInstallation + * @progress: (scope call): the callback + * @cancellable: (nullable): a #GCancellable + * @error: return location for a #GError + * + * Lists the remotes. + * + * Returns: (transfer full): The ref for the newly installed app or %null on failure + */ +XdgAppInstalledRef * +xdg_app_installation_install (XdgAppInstallation *self, + const char *remote_name, + XdgAppRefKind kind, + const char *name, + const char *arch, + const char *version, + XdgAppProgressCallback progress, + gpointer progress_data, + GCancellable *cancellable, + GError **error) +{ + XdgAppInstallationPrivate *priv = xdg_app_installation_get_instance_private (self); + g_autofree char *ref = NULL; + gboolean created_deploy_base = FALSE; + g_autoptr(GFile) deploy_base = NULL; + g_autoptr(XdgAppDir) dir_clone = NULL; + g_autoptr(GMainContext) main_context = NULL; + g_autoptr(OstreeAsyncProgress) ostree_progress = NULL; + XdgAppInstalledRef *result = NULL; + + if (version == NULL) + version = "master"; + + if (arch == NULL) + arch = xdg_app_get_arch (); + + if (!xdg_app_is_valid_name (name)) + { + xdg_app_fail (error, "'%s' is not a valid name", name); + return NULL; + } + + if (!xdg_app_is_valid_branch (version)) + { + xdg_app_fail (error, "'%s' is not a valid branch name", version); + return NULL; + } + + if (kind == XDG_APP_REF_KIND_APP) + ref = xdg_app_build_app_ref (name, version, arch); + else + ref = xdg_app_build_runtime_ref (name, version, arch); + + deploy_base = xdg_app_dir_get_deploy_dir (priv->dir, ref); + if (g_file_query_exists (deploy_base, cancellable)) + { + xdg_app_fail (error, "%s branch %s already installed", name, version); + goto out; + } + + /* Pull, etc are not threadsafe, so we work on a copy */ + dir_clone = xdg_app_dir_clone (priv->dir); + + /* Work around ostree-pull spinning the default main context for the sync calls */ + main_context = g_main_context_new (); + g_main_context_push_thread_default (main_context); + + if (progress) + { + ostree_progress = ostree_async_progress_new_and_connect (progress_cb, progress_data); + g_object_set_data (G_OBJECT (ostree_progress), "callback", progress); + g_object_set_data (G_OBJECT (ostree_progress), "last_progress", GUINT_TO_POINTER(0)); + } + + if (!xdg_app_dir_pull (dir_clone, remote_name, ref, + ostree_progress, cancellable, error)) + goto out; + + if (!g_file_make_directory_with_parents (deploy_base, cancellable, error)) + goto out; + created_deploy_base = TRUE; + + if (!xdg_app_dir_set_origin (priv->dir, ref, remote_name, cancellable, error)) + goto out; + + if (!xdg_app_dir_deploy (priv->dir, ref, NULL, cancellable, error)) + goto out; + + if (kind == XDG_APP_REF_KIND_APP) + { + if (!xdg_app_dir_make_current_ref (priv->dir, ref, cancellable, error)) + goto out; + + if (!xdg_app_dir_update_exports (priv->dir, name, cancellable, error)) + goto out; + } + + xdg_app_dir_cleanup_removed (priv->dir, cancellable, NULL); + + result = get_ref (self, ref, cancellable); + + out: + if (main_context) + g_main_context_pop_thread_default (main_context); + + if (created_deploy_base && result == NULL) + gs_shutil_rm_rf (deploy_base, cancellable, NULL); + + return result; +} diff --git a/lib/xdg-app-installation.h b/lib/xdg-app-installation.h index e678f27de..268d2af62 100644 --- a/lib/xdg-app-installation.h +++ b/lib/xdg-app-installation.h @@ -55,6 +55,11 @@ XDG_APP_EXTERN XdgAppInstallation *xdg_app_installation_new_user (void); XDG_APP_EXTERN XdgAppInstallation *xdg_app_installation_new_for_path (GFile *path, gboolean user); +typedef void (*XdgAppProgressCallback)(const char *status, + guint progress, + gboolean estimating, + gpointer user_data); + XDG_APP_EXTERN gboolean xdg_app_installation_get_is_user (XdgAppInstallation *self); XDG_APP_EXTERN XdgAppInstalledRef **xdg_app_installation_list_installed_refs (XdgAppInstallation *self, XdgAppRefKind kind, @@ -78,5 +83,15 @@ XDG_APP_EXTERN char * xdg_app_installation_load_app_overrides const char *app_id, GCancellable *cancellable, GError **error); +XDG_APP_EXTERN XdgAppInstalledRef * xdg_app_installation_install (XdgAppInstallation *self, + const char *remote_name, + XdgAppRefKind kind, + const char *name, + const char *arch, + const char *version, + XdgAppProgressCallback progress, + gpointer progress_data, + GCancellable *cancellable, + GError **error); #endif /* __XDG_APP_INSTALLATION_H__ */