Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rhel-10.0: $releasever_{major,minor}, shell-style variable substitution #1702

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions data/tests/vars/var1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
value123
1 change: 1 addition & 0 deletions data/tests/vars/var2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
456
5 changes: 5 additions & 0 deletions data/tests/yum.repos.d/shell-expansion.repo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[shell-expansion]
name=${unset:-${var1:+${var2:+$var2}}}
baseurl=https://${unset:-${var1:+${var2:+$var2}}}
enabled=1
gpgcheck=0
101 changes: 98 additions & 3 deletions libdnf/dnf-context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
#define MAX_NATIVE_ARCHES 12

#define RELEASEVER_PROV "system-release(releasever)"
#define RELEASEVER_MAJOR_PROV "system-release(releasever_major)"
#define RELEASEVER_MINOR_PROV "system-release(releasever_minor)"

/* data taken from https://github.com/rpm-software-management/dnf/blob/master/dnf/arch.py */
static const struct {
Expand Down Expand Up @@ -142,6 +144,8 @@ typedef struct
gchar **installonlypkgs;
gchar *base_arch;
gchar *release_ver;
gchar *release_ver_major;
gchar *release_ver_minor;
gchar *platform_module;
gchar *cache_dir;
gchar *solv_dir;
Expand Down Expand Up @@ -616,6 +620,42 @@ dnf_context_get_release_ver(DnfContext *context)
return priv->release_ver;
}

/**
* dnf_context_get_release_ver_major:
* @context: a #DnfContext instance.
*
* Gets the release major version. Usually derived by taking the substring of releasever before the
* first ".", but can be overridden by the distribution.
*
* Returns: the release major version, e.g. "10"
*
* Since: 0.74.0
**/
const gchar *
dnf_context_get_release_ver_major(DnfContext *context)
{
DnfContextPrivate *priv = GET_PRIVATE(context);
return priv->release_ver_major;
}

/**
* dnf_context_get_release_ver_minor:
* @context: a #DnfContext instance.
*
* Gets the release minor version. Usually derived by taking the substring of releasever after the
* first ".", but can be overridden by the distribution.
*
* Returns: the release minor version, e.g. "1"
*
* Since: 0.74.0
**/
const gchar *
dnf_context_get_release_ver_minor(DnfContext *context)
{
DnfContextPrivate *priv = GET_PRIVATE(context);
return priv->release_ver_minor;
}

/**
* dnf_context_get_platform_module:
* @context: a #DnfContext instance.
Expand Down Expand Up @@ -1271,7 +1311,9 @@ dnf_context_set_vars_dir(DnfContext *context, const gchar * const *vars_dir)
* @context: a #DnfContext instance.
* @release_ver: the release version, e.g. "20"
*
* Sets the release version.
* Sets the release version. Sets the major and minor release version by splitting `release_ver` on
* the first ".". The derived major and minor versions can later be overridden by calling
*`dnf_context_set_release_ver_major` and `dnf_context_set_release_ver_minor`, respectively.
*
* Since: 0.1.0
**/
Expand All @@ -1281,6 +1323,46 @@ dnf_context_set_release_ver(DnfContext *context, const gchar *release_ver)
DnfContextPrivate *priv = GET_PRIVATE(context);
g_free(priv->release_ver);
priv->release_ver = g_strdup(release_ver);

g_free(priv->release_ver_major);
g_free(priv->release_ver_minor);
dnf_split_releasever(release_ver, &priv->release_ver_major, &priv->release_ver_minor);
}

/**
* dnf_context_set_release_ver_major:
* @context: a #DnfContext instance.
* @release_ver_major: the release major version, e.g. "10"
*
* Sets the release major version, which is usually derived by splitting releasever on the first
* ".". This setter does not update the value of $releasever.
*
* Since: 0.74.0
**/
void
dnf_context_set_release_ver_major(DnfContext *context, const gchar *release_ver_major)
{
DnfContextPrivate *priv = GET_PRIVATE(context);
g_free(priv->release_ver_major);
priv->release_ver_major = g_strdup(release_ver_major);
}

/**
* dnf_context_set_release_ver_minor:
* @context: a #DnfContext instance.
* @release_ver_minor: the release minor version, e.g. "1"
*
* Sets the release minor version, which is usually derived by splitting releasever on the first
* ".". This setter does not update the value of $releasever.
*
* Since: 0.74.0
**/
void
dnf_context_set_release_ver_minor(DnfContext *context, const gchar *release_ver_minor)
{
DnfContextPrivate *priv = GET_PRIVATE(context);
g_free(priv->release_ver_minor);
priv->release_ver_minor = g_strdup(release_ver_minor);
}

/**
Expand Down Expand Up @@ -1660,13 +1742,26 @@ dnf_context_set_os_release(DnfContext *context, GError **error) try
Header hdr;
while ((hdr = rpmdbNextIterator (mi)) != NULL) {
const char *v = headerGetString (hdr, RPMTAG_VERSION);
const char *v_major = nullptr;
const char *v_minor = nullptr;
rpmds ds = rpmdsNew (hdr, RPMTAG_PROVIDENAME, 0);
while (rpmdsNext (ds) >= 0) {
if (strcmp (rpmdsN (ds), RELEASEVER_PROV) == 0 && rpmdsFlags (ds) == RPMSENSE_EQUAL)
if (strcmp (rpmdsN (ds), RELEASEVER_PROV) == 0 && rpmdsFlags (ds) == RPMSENSE_EQUAL) {
v = rpmdsEVR (ds);
} else if (strcmp (rpmdsN (ds), RELEASEVER_MAJOR_PROV) == 0 && rpmdsFlags (ds) == RPMSENSE_EQUAL) {
v_major = rpmdsEVR(ds);
} else if (strcmp (rpmdsN (ds), RELEASEVER_MINOR_PROV) == 0 && rpmdsFlags (ds) == RPMSENSE_EQUAL) {
v_minor = rpmdsEVR(ds);
}
}
found_in_rpmdb = TRUE;
dnf_context_set_release_ver (context, v);
dnf_context_set_release_ver(context, v);
if (v_major != nullptr) {
dnf_context_set_release_ver_major(context, v_major);
}
if (v_minor != nullptr) {
dnf_context_set_release_ver_minor(context, v_minor);
}
rpmdsFree (ds);
break;
}
Expand Down
6 changes: 6 additions & 0 deletions libdnf/dnf-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ const gchar *dnf_context_get_base_arch (DnfContext *context
const gchar *dnf_context_get_os_info (DnfContext *context);
const gchar *dnf_context_get_arch_info (DnfContext *context);
const gchar *dnf_context_get_release_ver (DnfContext *context);
const gchar *dnf_context_get_release_ver_major (DnfContext *context);
const gchar *dnf_context_get_release_ver_minor (DnfContext *context);
const gchar *dnf_context_get_platform_module (DnfContext *context);
const gchar *dnf_context_get_cache_dir (DnfContext *context);
const gchar *dnf_context_get_arch (DnfContext *context);
Expand Down Expand Up @@ -164,6 +166,10 @@ void dnf_context_set_vars_dir (DnfContext *context
const gchar * const *vars_dir);
void dnf_context_set_release_ver (DnfContext *context,
const gchar *release_ver);
void dnf_context_set_release_ver_major (DnfContext *context,
const gchar *release_ver_major);
void dnf_context_set_release_ver_minor (DnfContext *context,
const gchar *release_ver_minor);
void dnf_context_set_platform_module (DnfContext *context,
const gchar *platform_module);
void dnf_context_set_cache_dir (DnfContext *context,
Expand Down
30 changes: 22 additions & 8 deletions libdnf/dnf-repo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
*/

#include "conf/OptionBool.hpp"
#include "conf/ConfigParser.hpp"

#include "dnf-context.hpp"
#include "hy-repo-private.hpp"
Expand All @@ -45,6 +46,7 @@
#include <glib/gstdio.h>
#include "hy-util.h"
#include <librepo/librepo.h>
#include <librepo/url_substitution.h>
#include <rpm/rpmts.h>
#include <librepo/yum.h>

Expand Down Expand Up @@ -242,14 +244,17 @@ static gchar *
dnf_repo_substitute(DnfRepo *repo, const gchar *url)
{
DnfRepoPrivate *priv = GET_PRIVATE(repo);
char *tmp;
gchar *substituted;

/* do a little dance so we can use g_free() rather than lr_free() */
tmp = lr_url_substitute(url, priv->urlvars);
substituted = g_strdup(tmp);
lr_free(tmp);
std::map<std::string, std::string> substitutions;
for (LrUrlVars *elem = priv->urlvars; elem; elem = g_slist_next(elem)) {
const auto * pair = static_cast<LrVar*>(elem->data);
substitutions.insert({std::string{pair->var}, std::string{pair->val}});
}

std::string tmp{url};
libdnf::ConfigParser::substitute(tmp, substitutions);

auto * substituted = g_strdup(tmp.c_str());
return substituted;
}

Expand Down Expand Up @@ -1194,6 +1199,8 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
DnfRepoEnabled enabled = DNF_REPO_ENABLED_NONE;
g_autofree gchar *basearch = NULL;
g_autofree gchar *release = NULL;
g_autofree gchar *release_major = NULL;
g_autofree gchar *release_minor = NULL;

basearch = g_key_file_get_string(priv->keyfile, "general", "arch", NULL);
if (basearch == NULL)
Expand All @@ -1206,8 +1213,14 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
return FALSE;
}
release = g_key_file_get_string(priv->keyfile, "general", "version", NULL);
if (release == NULL)
if (release == NULL) {
release = g_strdup(dnf_context_get_release_ver(priv->context));
release_major = g_strdup(dnf_context_get_release_ver_major(priv->context));
release_minor = g_strdup(dnf_context_get_release_ver_minor(priv->context));
} else {
dnf_split_releasever(release, &release_major, &release_minor);
}

if (release == NULL) {
g_set_error_literal(error,
DNF_ERROR,
Expand All @@ -1222,8 +1235,9 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
if (!lr_handle_setopt(priv->repo_handle, error, LRO_INTERRUPTIBLE, 0L))
return FALSE;
priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever", release);
priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever_major", release_major);
priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever_minor", release_minor);
priv->urlvars = lr_urlvars_set(priv->urlvars, "basearch", basearch);

/* Call libdnf::dnf_context_load_vars(priv->context); only when values not in cache.
* But what about if variables on disk change during long running programs (PackageKit daemon)?
* if (!libdnf::dnf_context_get_vars_cached(priv->context))
Expand Down
44 changes: 44 additions & 0 deletions libdnf/dnf-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,50 @@ dnf_realpath(const gchar *path)
return real;
}

/**
* dnf_split_releasever:
* @releasever: A releasever string
* @releasever_major: Output string, or %NULL
* @releasever_minor: Output string, or %NULL
*
* Splits a releasever string into mayor and minor
* using the same logic as DNF 5 and as splitReleaseverTo in libzypp.
**/
void
dnf_split_releasever(const gchar *releasever,
gchar **releasever_major,
gchar **releasever_minor)
{
g_autofree gchar** result = NULL;

// Uses the same logic as DNF 5 and as splitReleaseverTo in libzypp
result = g_strsplit(releasever, ".", 2);

if(result[0] == NULL) {
if(releasever_major != NULL)
*releasever_major = g_strdup("");
if(releasever_minor != NULL)
*releasever_minor = g_strdup("");
return;
}
else {
if(releasever_major != NULL)
*releasever_major = result[0];
else
g_free(result[0]);
}

if(result[1] == NULL) {
if(releasever_minor != NULL)
*releasever_minor = g_strdup("");
} else {
if(releasever_minor != NULL)
*releasever_minor = result[1];
else
g_free(result[1]);
}
}

/**
* dnf_remove_recursive:
* @directory: A directory path
Expand Down
3 changes: 3 additions & 0 deletions libdnf/dnf-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ extern "C" {
#endif

gchar *dnf_realpath (const gchar *path);
void dnf_split_releasever (const gchar *releasever,
gchar **releasever_major,
gchar **releasever_minor);
gboolean dnf_remove_recursive (const gchar *directory,
GError **error);
gboolean dnf_ensure_file_unlinked (const gchar *src_path,
Expand Down
37 changes: 37 additions & 0 deletions tests/libdnf/dnf-self-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,32 @@ dnf_lock_threads_func(void)
g_object_unref(lock);
}

static void
dnf_split_releasever_func(void)
{
gchar *major, *minor;
dnf_split_releasever("1.23.45", &major, &minor);
g_assert_cmpstr(major, ==, "1");
g_assert_cmpstr(minor, ==, "23.45");
g_free(major);
g_free(minor);
dnf_split_releasever("6.789", &major, &minor);
g_assert_cmpstr(major, ==, "6");
g_assert_cmpstr(minor, ==, "789");
g_free(major);
g_free(minor);
dnf_split_releasever("10", &major, &minor);
g_assert_cmpstr(major, ==, "10");
g_assert_cmpstr(minor, ==, "");
g_free(major);
g_free(minor);
dnf_split_releasever("", &major, &minor);
g_assert_cmpstr(major, ==, "");
g_assert_cmpstr(minor, ==, "");
g_free(major);
g_free(minor);
}

static void
ch_test_repo_func(void)
{
Expand Down Expand Up @@ -817,15 +843,18 @@ dnf_repo_loader_func(void)
DnfState *state;
gboolean ret;
g_autofree gchar *repos_dir = NULL;
g_autofree gchar *vars_dir = NULL;
g_autoptr(DnfContext) ctx = NULL;
g_autoptr(DnfRepoLoader) repo_loader = NULL;
guint metadata_expire;

/* set up local context */
ctx = dnf_context_new();
repos_dir = dnf_test_get_filename("yum.repos.d");
vars_dir = dnf_test_get_filename("vars");
dnf_context_set_repo_dir(ctx, repos_dir);
dnf_context_set_solv_dir(ctx, "/tmp");
dnf_context_set_vars_dir(ctx, (const gchar *[]){vars_dir, NULL});
ret = dnf_context_setup(ctx, NULL, &error);
g_assert_no_error(error);
g_assert(ret);
Expand Down Expand Up @@ -881,6 +910,13 @@ dnf_repo_loader_func(void)
g_assert_error(error, DNF_ERROR, DNF_ERROR_REPO_NOT_AVAILABLE);
g_assert(!ret);
g_clear_error(&error);

/* check that shell-style variable expressions are correctly expanded in repo values */
dnf_state_reset(state);
repo = dnf_repo_loader_get_repo_by_id(repo_loader, "shell-expansion", &error);
g_assert_no_error(error);
g_assert(repo != NULL);
g_assert_cmpstr(dnf_repo_get_description(repo), ==, "456");
}

static void
Expand Down Expand Up @@ -1240,6 +1276,7 @@ main(int argc, char **argv)
g_test_add_func("/libdnf/context{cache-clean-check}", dnf_context_cache_clean_check_func);
g_test_add_func("/libdnf/lock", dnf_lock_func);
g_test_add_func("/libdnf/lock[threads]", dnf_lock_threads_func);
g_test_add_func("/libdnf/split_releasever", dnf_split_releasever_func);
g_test_add_func("/libdnf/repo", ch_test_repo_func);
g_test_add_func("/libdnf/repo_empty_keyfile", dnf_repo_setup_with_empty_keyfile);
g_test_add_func("/libdnf/state", dnf_state_func);
Expand Down
Loading