Add install-runtime builtin

tingping/wmclass
Alexander Larsson 2014-12-18 11:06:37 +01:00
parent 63859dc0ee
commit 06e57a081d
6 changed files with 292 additions and 0 deletions

View File

@ -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 \

View File

@ -0,0 +1,92 @@
#include "config.h"
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#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;
}

View File

@ -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

View File

@ -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)
{

View File

@ -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__ */

View File

@ -20,6 +20,7 @@ typedef struct {
static XdgAppCommand commands[] = {
{ "add-repo", xdg_app_builtin_add_repo },
{ "install-runtime", xdg_app_builtin_install_runtime },
{ NULL }
};