daemon: Implement package diff methods

This commit is contained in:
petervo 2015-06-09 19:27:39 -07:00 committed by Matthew Barnes
parent a140c26200
commit 075d6bdad0
9 changed files with 712 additions and 22 deletions

View File

@ -30,6 +30,8 @@ librpmostreed_la_SOURCES = \
src/daemon/auth.c \
src/daemon/deployment-utils.h \
src/daemon/deployment-utils.c \
src/daemon/rpmostree-package-variants.h \
src/daemon/rpmostree-package-variants.c \
src/daemon/os.h \
src/daemon/os.c \
$(NULL)

View File

@ -16,9 +16,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "deployment-utils.h"
#include "libgsystem.h"
#include <libglnx.h>
char *
deployment_generate_id (OstreeDeployment *deployment)
@ -42,8 +44,9 @@ deployment_gpg_results (OstreeRepo *repo,
GError *error = NULL;
GVariant *ret = NULL;
gs_free gchar *remote = NULL;
gs_unref_object OstreeGpgVerifyResult *result = NULL;
g_autofree gchar *remote = NULL;
glnx_unref_object OstreeGpgVerifyResult *result = NULL;
guint n_sigs, i;
gboolean gpg_verify;
GVariantBuilder builder;
@ -87,17 +90,47 @@ out:
return ret;
}
char *
deployment_get_refspec (OstreeDeployment *deployment)
{
GKeyFile *origin = NULL; // owned by deployment
char *origin_refspec = NULL;
origin = ostree_deployment_get_origin (deployment);
if (!origin)
goto out;
origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
out:
return origin_refspec;
}
GVariant *
deployment_generate_blank_variant (void)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
g_variant_builder_add (&builder, "s", "");
g_variant_builder_add (&builder, "s", "");
g_variant_builder_add (&builder, "i", -1);
g_variant_builder_add (&builder, "s", "");
g_variant_builder_add (&builder, "s", "");
g_variant_builder_add (&builder, "t", 0);
g_variant_builder_add (&builder, "s", "");
g_variant_builder_add_value (&builder, g_variant_new("av", NULL));
return g_variant_builder_end (&builder);
}
GVariant *
deployment_generate_variant (OstreeDeployment *deployment,
OstreeRepo *repo)
{
gs_unref_variant GVariant *commit = NULL;
g_autoptr (GKeyFile) origin = NULL;
g_autoptr (GVariant) commit = NULL;
gs_free gchar *origin_refspec = NULL;
gs_free gchar *version_commit = NULL;
gs_free gchar *id = NULL;
g_autofree gchar *origin_refspec = NULL;
g_autofree gchar *version_commit = NULL;
g_autofree gchar *id = NULL;
GVariant *sigs = NULL; // floating variant
GError *error = NULL;
@ -116,7 +149,7 @@ deployment_generate_variant (OstreeDeployment *deployment,
&commit,
&error))
{
gs_unref_variant GVariant *metadata = NULL;
g_autoptr (GVariant) metadata = NULL;
timestamp = ostree_commit_get_timestamp (commit);
metadata = g_variant_get_child_value (commit, 0);
if (metadata != NULL)
@ -128,13 +161,9 @@ deployment_generate_variant (OstreeDeployment *deployment,
}
g_clear_error (&error);
origin = ostree_deployment_get_origin (deployment);
if (origin)
{
origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
if (origin_refspec)
sigs = deployment_gpg_results (repo, origin_refspec, csum);
}
origin_refspec = deployment_get_refspec (deployment);
if (origin_refspec)
sigs = deployment_gpg_results (repo, origin_refspec, csum);
if (!sigs)
sigs = g_variant_new ("av", NULL);

View File

@ -24,5 +24,8 @@
char * deployment_generate_id (OstreeDeployment *deployment);
char * deployment_get_refspec (OstreeDeployment *deployment);
GVariant * deployment_generate_blank_variant (void);
GVariant * deployment_generate_variant (OstreeDeployment *deployment,
OstreeRepo *repo);

View File

@ -29,6 +29,8 @@ static const GDBusErrorEntry dbus_error_entries[] =
"org.projectatomic.rpmostreed.Error.NotAuthorized"},
{RPM_OSTREED_ERROR_UPDATE_IN_PROGRESS,
"org.projectatomic.rpmostreed.Error.UpdateInProgress"},
{RPM_OSTREED_ERROR_INVALID_REFSPEC,
"org.projectatomic.rpmostreed.Error.InvalidRefspec"},
};
GQuark

View File

@ -31,6 +31,7 @@ typedef enum {
RPM_OSTREED_ERROR_INVALID_SYSROOT,
RPM_OSTREED_ERROR_NOT_AUTHORIZED,
RPM_OSTREED_ERROR_UPDATE_IN_PROGRESS,
RPM_OSTREED_ERROR_INVALID_REFSPEC,
RPM_OSTREED_ERROR_NUM_ENTRIES,
} RpmOstreedError;

View File

@ -38,12 +38,18 @@
<property name="DefaultDeployment" type="(ssisstsav)" access="read"/>
<property name="RollbackDeployment" type="(ssisstsav)" access="read"/>
<property name="HasCachedUpdateRpmDiff" type="b" access="read"/>
<!-- NONE, DIFF, PREPARE, REBOOT -->
<property name="AutomaticUpdatePolicy" type="s" access="read"/>
<property name="Name" type="s" access="read"/>
<method name="GetDeploymentsRpmDiff">
<arg type="s" name="deployid0"/>
<arg type="s" name="deployid1"/>
<arg type="a(sua{sv})" name="result" direction="out"/>
</method>
<method name="GetCachedUpdateRpmDiff">
<arg type="s" name="deployid"/>
<arg type="a(sua{sv})" name="result" direction="out"/>
</method>

View File

@ -67,6 +67,34 @@ G_DEFINE_TYPE_WITH_CODE (OSStub, osstub, RPMOSTREE_TYPE_OS_SKELETON,
G_IMPLEMENT_INTERFACE (RPMOSTREE_TYPE_OS,
osstub_iface_init));
/* ---------------------------------------------------------------------------------------------------- */
/**
* task_result_invoke:
*
* Completes a GTask where the user_data is
* an invocation and the task data or error is
* passed to the invocation when called back.
*/
static void
task_result_invoke (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
glnx_unref_object GTask *task = G_TASK (res);
GError *error = NULL;
GDBusMethodInvocation *invocation = user_data;
GVariant *result = g_task_propagate_pointer (task, &error);
if (error)
g_dbus_method_invocation_take_error (invocation, error);
else
g_dbus_method_invocation_return_value (invocation, result);
}
static void
task_data_free (TaskData *data)
{
@ -87,7 +115,7 @@ sysroot_changed (Sysroot *sysroot,
gpointer user_data)
{
OSStub *self = OSSTUB (user_data);
g_return_val_if_fail (OSTREE_IS_SYSROOT (ot_sysroot), NULL);
g_return_if_fail (OSTREE_IS_SYSROOT (ot_sysroot));
osstub_load_internals (self, ot_sysroot);
}
@ -199,6 +227,318 @@ out:
return index_to_prepend;
}
/**
* refspec_parse_partial:
* @new_provided_refspec: The provided refspec
* @base_refspec: The refspec string to base on.
* @out_refspec: Pointer to the new refspec
* @error: Pointer to an error pointer.
*
* Takes a refspec string and adds any missing bits based on the
* base_refspec argument. Errors if a full valid refspec can't
* be derived.
*
* Returns: True on sucess.
*/
static gboolean
refspec_parse_partial (const gchar *new_provided_refspec,
gchar *base_refspec,
gchar **out_refspec,
GError **error)
{
g_autofree gchar *ref = NULL;
g_autofree gchar *remote = NULL;
g_autofree gchar *origin_ref = NULL;
g_autofree gchar *origin_remote = NULL;
GError *parse_error = NULL;
gboolean ret = FALSE;
/* Allow just switching remotes */
if (g_str_has_suffix (new_provided_refspec, ":"))
{
remote = g_strdup (new_provided_refspec);
remote[strlen (remote) - 1] = '\0';
}
else
{
if (!ostree_parse_refspec (new_provided_refspec, &remote,
&ref, &parse_error))
{
g_set_error_literal (error, RPM_OSTREED_ERROR,
RPM_OSTREED_ERROR_INVALID_REFSPEC,
parse_error->message);
g_clear_error (&parse_error);
goto out;
}
}
if (base_refspec != NULL)
{
if (!ostree_parse_refspec (base_refspec, &origin_remote,
&origin_ref, &parse_error))
goto out;
}
if (ref == NULL)
{
if (origin_ref)
{
ref = g_strdup (origin_ref);
}
else
{
g_set_error (error, RPM_OSTREED_ERROR,
RPM_OSTREED_ERROR_INVALID_REFSPEC,
"Could not determine default ref to pull.");
goto out;
}
}
else if (remote == NULL)
{
if (origin_remote)
{
remote = g_strdup (origin_remote);
}
else
{
g_set_error (error, RPM_OSTREED_ERROR,
RPM_OSTREED_ERROR_INVALID_REFSPEC,
"Could not determine default remote to pull.");
goto out;
}
}
if (g_strcmp0 (origin_remote, remote) == 0 &&
g_strcmp0 (origin_ref, ref) == 0)
{
g_set_error (error, RPM_OSTREED_ERROR,
RPM_OSTREED_ERROR_INVALID_REFSPEC,
"Old and new refs are equal: %s:%s",
remote, ref);
goto out;
}
*out_refspec = g_strconcat (remote, ":", ref, NULL);
ret = TRUE;
out:
return ret;
}
static OstreeDeployment *
get_deployment_for_id (OstreeSysroot *sysroot,
const gchar *deploy_id)
{
g_autoptr (GPtrArray) deployments = NULL;
guint i;
OstreeDeployment *deployment = NULL;
deployments = ostree_sysroot_get_deployments (sysroot);
if (deployments == NULL)
goto out;
for (i=0; i<deployments->len; i++)
{
g_autofree gchar *id = deployment_generate_id (deployments->pdata[i]);
if (g_strcmp0 (deploy_id, id) == 0) {
deployment = g_object_ref (deployments->pdata[i]);
}
}
out:
return deployment;
}
static void
set_diff_task_result (GTask *task,
GVariant *value,
GError *error)
{
if (error == NULL)
{
g_task_return_pointer (task,
g_variant_new ("(@a(sua{sv}))", value),
NULL);
}
else
{
g_task_return_error (task, error);
}
}
static void
get_rebase_diff_variant_in_thread (GTask *task,
gpointer object,
gpointer data_ptr,
GCancellable *cancellable)
{
RPMOSTreeOS *self = RPMOSTREE_OS (object);
const gchar *name;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
glnx_unref_object OstreeRepo *ot_repo = NULL;
glnx_unref_object OstreeDeployment *base_deployment = NULL;
g_autofree gchar *comp_ref = NULL;
g_autofree gchar *base_refspec = NULL;
GVariant *value = NULL; // freed when invoked
GError *error = NULL; // freed when invoked
gchar *refspec = data_ptr; // freed by task
if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path ( sysroot_get ()),
cancellable,
&ot_sysroot,
&ot_repo,
&error))
goto out;
name = rpmostree_os_get_name (self);
base_deployment = ostree_sysroot_get_merge_deployment (ot_sysroot, name);
if (base_deployment == NULL)
{
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No deployments found for os %s", name);
goto out;
}
base_refspec = deployment_get_refspec (base_deployment);
if (!refspec_parse_partial (refspec,
base_refspec,
&comp_ref,
&error))
goto out;
value = rpm_ostree_db_diff_variant (ot_repo,
ostree_deployment_get_csum (base_deployment),
comp_ref,
cancellable,
&error);
out:
set_diff_task_result (task, value, error);
}
static void
get_upgrade_diff_variant_in_thread (GTask *task,
gpointer object,
gpointer data_ptr,
GCancellable *cancellable)
{
RPMOSTreeOS *self = RPMOSTREE_OS (object);
const gchar *name;
g_autofree gchar *comp_ref = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
glnx_unref_object OstreeRepo *ot_repo = NULL;
glnx_unref_object OstreeDeployment *base_deployment = NULL;
GVariant *value = NULL; // freed when invoked
GError *error = NULL; // freed when invoked
if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path ( sysroot_get ()),
cancellable,
&ot_sysroot,
&ot_repo,
&error))
goto out;
name = rpmostree_os_get_name (self);
base_deployment = ostree_sysroot_get_merge_deployment (ot_sysroot, name);
if (base_deployment == NULL)
{
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No deployments found for os %s", name);
goto out;
}
comp_ref = deployment_get_refspec (base_deployment);
if (!comp_ref)
{
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No upgrade remote found for os %s", name);
goto out;
}
value = rpm_ostree_db_diff_variant (ot_repo,
ostree_deployment_get_csum (base_deployment),
comp_ref,
cancellable,
&error);
out:
set_diff_task_result (task, value, error);
}
static void
get_deployments_diff_variant_in_thread (GTask *task,
gpointer object,
gpointer data_ptr,
GCancellable *cancellable)
{
const gchar *ref0;
const gchar *ref1;
glnx_unref_object OstreeDeployment *deployment0 = NULL;
glnx_unref_object OstreeDeployment *deployment1 = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
glnx_unref_object OstreeRepo *ot_repo = NULL;
GVariant *value = NULL; // freed when invoked
GError *error = NULL; // freed when invoked
GPtrArray *compare_refs = data_ptr; // freed by task
g_return_if_fail (compare_refs->len == 2);
if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path ( sysroot_get ()),
cancellable,
&ot_sysroot,
&ot_repo,
&error))
goto out;
deployment0 = get_deployment_for_id (ot_sysroot, compare_refs->pdata[0]);
if (!deployment0)
{
gchar *id = compare_refs->pdata[0];
g_set_error (&error,
RPM_OSTREED_ERROR,
RPM_OSTREED_ERROR_FAILED,
"Invalid deployment id %s",
id);
goto out;
}
ref0 = ostree_deployment_get_csum (deployment0);
deployment1 = get_deployment_for_id (ot_sysroot, compare_refs->pdata[1]);
if (!deployment1)
{
gchar *id = compare_refs->pdata[1];
g_set_error (&error,
RPM_OSTREED_ERROR,
RPM_OSTREED_ERROR_FAILED,
"Invalid deployment id %s",
id);
goto out;
}
ref1 = ostree_deployment_get_csum (deployment1);
value = rpm_ostree_db_diff_variant (ot_repo,
ref0,
ref1,
cancellable,
&error);
out:
set_diff_task_result (task, value, error);
}
static void
osstub_upgrade_thread (GTask *task,
gpointer source_object,
@ -282,12 +622,47 @@ osstub_transaction_complete (GObject *source_object,
g_clear_error (&local_error);
}
static gboolean
handle_get_deployments_rpm_diff (RPMOSTreeOS *interface,
GDBusMethodInvocation *invocation,
const char *arg_deployid0,
const char *arg_deployid1)
{
OSStub *self = OSSTUB (interface);
GPtrArray *compare_refs = NULL; // freed by task
GTask *task = NULL;
glnx_unref_object GCancellable *cancellable = NULL;
compare_refs = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (compare_refs, g_strdup (arg_deployid0));
g_ptr_array_add (compare_refs, g_strdup (arg_deployid1));
task = g_task_new (self, cancellable,
task_result_invoke,
invocation);
g_task_set_task_data (task,
compare_refs,
(GDestroyNotify) g_ptr_array_unref);
g_task_run_in_thread (task, get_deployments_diff_variant_in_thread);
return TRUE;
}
static gboolean
osstub_handle_get_cached_update_rpm_diff (RPMOSTreeOS *interface,
GDBusMethodInvocation *invocation,
const char *arg_deployid)
GDBusMethodInvocation *invocation)
{
/* FIXME */
OSStub *self = OSSTUB (interface);
GTask *task = NULL;
glnx_unref_object GCancellable *cancellable = NULL;
task = g_task_new (self, cancellable,
task_result_invoke,
invocation);
g_task_run_in_thread (task, get_upgrade_diff_variant_in_thread);
return TRUE;
}
@ -476,7 +851,18 @@ osstub_handle_get_cached_rebase_rpm_diff (RPMOSTreeOS *interface,
const char *arg_refspec,
const char * const *arg_packages)
{
/* FIXME */
OSStub *self = OSSTUB (interface);
GTask *task = NULL;
glnx_unref_object GCancellable *cancellable = NULL;
/* TODO: Totally ignoring packages for now */
task = g_task_new (self, cancellable,
task_result_invoke,
invocation);
g_task_set_task_data (task,
g_strdup (arg_refspec),
g_free);
g_task_run_in_thread (task, get_rebase_diff_variant_in_thread);
return TRUE;
}

View File

@ -0,0 +1,213 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 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 licence 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, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <rpmostree.h>
#include "rpmostree-package-variants.h"
#include <libglnx.h>
static GVariant *
build_diff_variant (const gchar *name,
guint type,
RpmOstreePackage *old_package,
RpmOstreePackage *new_package)
{
GVariantBuilder options_builder;
GVariantBuilder builder;
g_variant_builder_init (&options_builder, G_VARIANT_TYPE ("a{sv}"));
if (old_package)
{
g_variant_builder_add (&options_builder, "{sv}", "PreviousPackage",
rpm_ostree_package_to_variant (old_package));
}
if (new_package)
{
g_variant_builder_add (&options_builder, "{sv}", "NewPackage",
rpm_ostree_package_to_variant (new_package));
}
g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
g_variant_builder_add (&builder, "s", name);
g_variant_builder_add (&builder, "u", type);
g_variant_builder_add_value (&builder, g_variant_builder_end (&options_builder));
return g_variant_builder_end (&builder);
}
/**
* rpm_ostree_package_to_variant
* @package: RpmOstreePackage
*
* Returns: A GVariant of (sss) where values are
* (package name, evr, arch)
*/
GVariant *
rpm_ostree_package_to_variant (RpmOstreePackage *package)
{
return g_variant_new ("(sss)",
rpm_ostree_package_get_name (package),
rpm_ostree_package_get_evr (package),
rpm_ostree_package_get_arch (package));
}
int
rpm_ostree_db_diff_variant_compare_by_name (const void *v1,
const void *v2)
{
GVariant **v1pp = (GVariant**)v1;
GVariant *variant1 = *v1pp;
GVariant **v2pp = (GVariant**)v2;
GVariant *variant2 = *v2pp;
gchar *name1 = NULL;
gchar *name2 = NULL;
g_variant_get_child (variant1, 0, "&s", &name1);
g_variant_get_child (variant2, 0, "&s", &name2);
return g_strcmp0 (name1, name2);
}
int
rpm_ostree_db_diff_variant_compare_by_type (const void *v1,
const void *v2)
{
GVariant **v1pp = (GVariant**)v1;
GVariant *variant1 = *v1pp;
GVariant **v2pp = (GVariant**)v2;
GVariant *variant2 = *v2pp;
guint type1;
guint type2;
g_variant_get_child (variant1, 1, "u", &type1);
g_variant_get_child (variant2, 1, "u", &type2);
if (type1 == type2)
return rpm_ostree_db_diff_variant_compare_by_name (v1, v2);
return type1 - type2;
}
/**
* rpm_ostree_db_build_diff_variant
* @repo: A OstreeRepo
* @old_ref: old ref to use
* @new_ref: New ref to use
* GCancellable: *cancellable
* GError: **error
*
* Returns: A GVariant that represents the differences
* between the rpm databases on the given refs.
*/
GVariant *
rpm_ostree_db_diff_variant (OstreeRepo *repo,
const char *from_rev,
const char *to_rev,
GCancellable *cancellable,
GError **error)
{
GVariant *variant = NULL;
GVariantBuilder builder;
g_autoptr (GPtrArray) removed = NULL;
g_autoptr (GPtrArray) added = NULL;
g_autoptr (GPtrArray) modified_old = NULL;
g_autoptr (GPtrArray) modified_new = NULL;
g_autoptr (GPtrArray) found = NULL;
guint i;
found = g_ptr_array_new ();
if (!rpm_ostree_db_diff (repo, from_rev, to_rev,
&removed, &added, &modified_old,
&modified_new, cancellable, error))
goto out;
if (modified_old->len > 0)
{
for (i = 0; i < modified_old->len; i++)
{
guint type = RPM_OSTREE_PACKAGE_UPGRADED;
RpmOstreePackage *oldpkg = modified_old->pdata[i];
RpmOstreePackage *newpkg;
const char *name = rpm_ostree_package_get_name (oldpkg);
g_assert_cmpuint (i, <, modified_new->len);
newpkg = modified_new->pdata[i];
if (rpm_ostree_package_cmp (oldpkg, newpkg) > 0)
type = RPM_OSTREE_PACKAGE_DOWNGRADED;
g_ptr_array_add (found,
build_diff_variant (name, type, oldpkg, newpkg));
}
}
if (removed->len > 0)
{
for (i = 0; i < removed->len; i++)
{
RpmOstreePackage *pkg = removed->pdata[i];
const char *name = rpm_ostree_package_get_name (pkg);
g_ptr_array_add (found,
build_diff_variant (name,
RPM_OSTREE_PACKAGE_REMOVED,
pkg,
NULL));
}
}
if (added->len > 0)
{
for (i = 0; i < added->len; i++)
{
RpmOstreePackage *pkg = added->pdata[i];
const char *name = rpm_ostree_package_get_name (pkg);
g_ptr_array_add (found,
build_diff_variant (name,
RPM_OSTREE_PACKAGE_ADDED,
NULL,
pkg));
}
}
g_ptr_array_sort (found, rpm_ostree_db_diff_variant_compare_by_type);
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
for (i = 0; i < found->len; i++)
{
GVariant *v = found->pdata[i];
g_variant_builder_add_value (&builder, v);
}
if (found->len > 1)
variant = g_variant_builder_end (&builder);
else
variant = g_variant_new ("a(sua{sv})", NULL);
out:
return variant;
}

View File

@ -0,0 +1,48 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 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 licence 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, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#pragma once
#include <ostree.h>
#include <rpmostree.h>
typedef enum {
RPM_OSTREE_PACKAGE_ADDED,
RPM_OSTREE_PACKAGE_REMOVED,
RPM_OSTREE_PACKAGE_UPGRADED,
RPM_OSTREE_PACKAGE_DOWNGRADED
} RpmOstreePackageDiffTypes;
GVariant * rpm_ostree_db_diff_variant (OstreeRepo *repo,
const char *from_ref,
const char *to_ref,
GCancellable *cancellable,
GError **error);
int rpm_ostree_db_diff_variant_compare_by_name (const void *v1,
const void *v2);
int rpm_ostree_db_diff_variant_compare_by_type (const void *v1,
const void *v2);
_RPMOSTREE_EXTERN
GVariant * rpm_ostree_package_to_variant (RpmOstreePackage *package);