forked from Mirrors/flatpak-builder
Add install-runtime builtin
parent
63859dc0ee
commit
06e57a081d
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
179
xdg-app-dir.c
179
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)
|
||||
{
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -20,6 +20,7 @@ typedef struct {
|
|||
|
||||
static XdgAppCommand commands[] = {
|
||||
{ "add-repo", xdg_app_builtin_add_repo },
|
||||
{ "install-runtime", xdg_app_builtin_install_runtime },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue