daemon: Add primitives for version lookup
rpmostreed_repo_pull_ancestry() downloads an ancestry of commit objects starting from a given refspec. An optional visitor function is called for each commit object. The visitor function can stop the recursion, such as when looking for a particular commit. rpmostreed_repo_lookup_version() builds on this by supplying a visitor function that examines commit metadata for a particular version value. When the version value is found, the commit's checksum is returned to the caller.
This commit is contained in:
parent
a5dd7d1a1a
commit
b3ecdd10b6
@ -224,3 +224,199 @@ rpmostreed_reboot (GCancellable *cancellable, GError **error)
|
|||||||
cancellable, error,
|
cancellable, error,
|
||||||
"systemctl", "reboot", NULL);
|
"systemctl", "reboot", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rpmostreed_repo_pull_ancestry:
|
||||||
|
* @repo: Repo
|
||||||
|
* @refspec: Repository branch
|
||||||
|
* @visitor: (allow-none): Visitor function to call on each commit
|
||||||
|
* @visitor_data: (allow-none): User data for @visitor
|
||||||
|
* @progress: (allow-none): Progress
|
||||||
|
* @cancellable: Cancellable
|
||||||
|
* @error: Error
|
||||||
|
*
|
||||||
|
* Downloads an ancestry of commit objects starting from @refspec.
|
||||||
|
*
|
||||||
|
* If a @visitor function pointer is given, commit objects are downloaded
|
||||||
|
* in batches and the @visitor function is called for each commit object.
|
||||||
|
* The @visitor function can stop the recursion, such as when looking for
|
||||||
|
* a particular commit.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE on success, %FALSE on failure
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
rpmostreed_repo_pull_ancestry (OstreeRepo *repo,
|
||||||
|
const char *refspec,
|
||||||
|
RpmostreedCommitVisitor visitor,
|
||||||
|
gpointer visitor_data,
|
||||||
|
OstreeAsyncProgress *progress,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
OstreeRepoPullFlags flags;
|
||||||
|
GVariantDict options;
|
||||||
|
GVariant *refs_value;
|
||||||
|
const char *refs_array[] = { NULL, NULL };
|
||||||
|
g_autofree char *remote = NULL;
|
||||||
|
g_autofree char *ref = NULL;
|
||||||
|
g_autofree char *checksum = NULL;
|
||||||
|
int depth, ii;
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE);
|
||||||
|
g_return_val_if_fail (refspec != NULL, FALSE);
|
||||||
|
|
||||||
|
if (!ostree_parse_refspec (refspec, &remote, &ref, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* If no visitor function was provided then we won't be short-circuiting
|
||||||
|
* the recursion, so pull everything in one shot. Otherwise pull commits
|
||||||
|
* in increasingly large batches. */
|
||||||
|
depth = (visitor != NULL) ? 10 : -1;
|
||||||
|
|
||||||
|
flags = OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY;
|
||||||
|
|
||||||
|
/* It's important to use the ref name instead of a checksum on the first
|
||||||
|
* pass because we want to search from the latest available commit on the
|
||||||
|
* remote server, which is not necessarily what the ref name is currently
|
||||||
|
* pointing at in our local repo. */
|
||||||
|
refs_array[0] = ref;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
/* Floating reference, transferred to dictionary. */
|
||||||
|
refs_value = g_variant_new_strv ((const char * const *) refs_array, -1);
|
||||||
|
|
||||||
|
g_variant_dict_init (&options, NULL);
|
||||||
|
g_variant_dict_insert (&options, "depth", "i", depth);
|
||||||
|
g_variant_dict_insert (&options, "flags", "i", flags);
|
||||||
|
g_variant_dict_insert_value (&options, "refs", refs_value);
|
||||||
|
|
||||||
|
if (!ostree_repo_pull_with_options (repo, remote,
|
||||||
|
g_variant_dict_end (&options),
|
||||||
|
progress, cancellable, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* First pass only. Now we can resolve the ref to a checksum. */
|
||||||
|
if (checksum == NULL)
|
||||||
|
{
|
||||||
|
if (!ostree_repo_resolve_rev (repo, ref, FALSE, &checksum, error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If depth is negative (no visitor), this loop is skipped. */
|
||||||
|
for (ii = 0; ii < depth && checksum != NULL; ii++)
|
||||||
|
{
|
||||||
|
g_autoptr(GVariant) commit = NULL;
|
||||||
|
gboolean stop = FALSE;
|
||||||
|
|
||||||
|
if (!ostree_repo_load_commit (repo, checksum, &commit, NULL, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!visitor (repo, checksum, commit, visitor_data, &stop, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
g_clear_pointer (&checksum, g_free);
|
||||||
|
|
||||||
|
if (!stop)
|
||||||
|
checksum = ostree_commit_get_parent (commit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Break if no visitor, or visitor told us to stop. */
|
||||||
|
if (depth < 0 || checksum == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Pull the next batch of commits, twice as many. */
|
||||||
|
refs_array[0] = checksum;
|
||||||
|
depth = depth * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *version;
|
||||||
|
char *checksum;
|
||||||
|
} VersionVisitorClosure;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
version_visitor (OstreeRepo *repo,
|
||||||
|
const char *checksum,
|
||||||
|
GVariant *commit,
|
||||||
|
gpointer user_data,
|
||||||
|
gboolean *out_stop,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
VersionVisitorClosure *closure = user_data;
|
||||||
|
g_autoptr(GVariant) metadict = NULL;
|
||||||
|
const char *version = NULL;
|
||||||
|
|
||||||
|
metadict = g_variant_get_child_value (commit, 0);
|
||||||
|
if (g_variant_lookup (metadict, "version", "&s", &version))
|
||||||
|
{
|
||||||
|
if (g_str_equal (version, closure->version))
|
||||||
|
{
|
||||||
|
closure->checksum = g_strdup (checksum);
|
||||||
|
*out_stop = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rpmostreed_repo_lookup_version:
|
||||||
|
* @repo: Repo
|
||||||
|
* @refspec: Repository branch
|
||||||
|
* @version: Version to look for
|
||||||
|
* @progress: (allow-none): Progress
|
||||||
|
* @cancellable: Cancellable
|
||||||
|
* @out_checksum: (out) (allow-none): Commit checksum, or %NULL
|
||||||
|
* @error: Error
|
||||||
|
*
|
||||||
|
* Tries to determine the commit checksum for @version on @refspec.
|
||||||
|
* This may require pulling commit objects from a remote repository.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE on success, %FALSE on failure
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
rpmostreed_repo_lookup_version (OstreeRepo *repo,
|
||||||
|
const char *refspec,
|
||||||
|
const char *version,
|
||||||
|
OstreeAsyncProgress *progress,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
char **out_checksum,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
VersionVisitorClosure closure = { version, NULL };
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE);
|
||||||
|
g_return_val_if_fail (refspec != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (version != NULL, FALSE);
|
||||||
|
|
||||||
|
if (!rpmostreed_repo_pull_ancestry (repo, refspec,
|
||||||
|
version_visitor, &closure,
|
||||||
|
progress, cancellable, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (closure.checksum == NULL)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||||
|
"Version %s not found in %s", version, refspec);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_checksum != NULL)
|
||||||
|
*out_checksum = g_steal_pointer (&closure.checksum);
|
||||||
|
|
||||||
|
g_free (closure.checksum);
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -36,3 +36,30 @@ gboolean rpmostreed_refspec_parse_partial (const gchar *new_provided_refspec,
|
|||||||
GError **error);
|
GError **error);
|
||||||
void
|
void
|
||||||
rpmostreed_reboot (GCancellable *cancellable, GError **error);
|
rpmostreed_reboot (GCancellable *cancellable, GError **error);
|
||||||
|
|
||||||
|
/* XXX These pull-ancestry and lookup-version functions should eventually
|
||||||
|
* be integrated into libostree, but it's still a bit premature to do
|
||||||
|
* so now. Version integration in ostree needs more design work. */
|
||||||
|
|
||||||
|
typedef gboolean (*RpmostreedCommitVisitor) (OstreeRepo *repo,
|
||||||
|
const char *checksum,
|
||||||
|
GVariant *commit,
|
||||||
|
gpointer user_data,
|
||||||
|
gboolean *out_stop,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean rpmostreed_repo_pull_ancestry (OstreeRepo *repo,
|
||||||
|
const char *refspec,
|
||||||
|
RpmostreedCommitVisitor visitor,
|
||||||
|
gpointer visitor_data,
|
||||||
|
OstreeAsyncProgress *progress,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean rpmostreed_repo_lookup_version (OstreeRepo *repo,
|
||||||
|
const char *refspec,
|
||||||
|
const char *version,
|
||||||
|
OstreeAsyncProgress *progress,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
char **out_checksum,
|
||||||
|
GError **error);
|
||||||
|
Loading…
Reference in New Issue
Block a user