Split $releasever_{major,minor}, shell-style variable expansion
Resolves: RHEL-95006
This commit is contained in:
parent
9bb30a22ba
commit
03cea2d0b5
261
0023-conf-Add-limited-shell-style-variable-expansion.patch
Normal file
261
0023-conf-Add-limited-shell-style-variable-expansion.patch
Normal file
@ -0,0 +1,261 @@
|
||||
From 0eb17f745b4b23bd32e6c3896812bea8d1ed0dfc Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Wed, 20 Sep 2023 20:05:29 +0000
|
||||
Subject: [PATCH 01/11] [conf] Add limited shell-style variable expansion
|
||||
|
||||
Ported from the DNF 5 implementation here:
|
||||
https://github.com/rpm-software-management/dnf5/pull/800.
|
||||
|
||||
Adds support for ${variable:-word} and ${variable:+word}
|
||||
POSIX-shell-like expansions of vars.
|
||||
|
||||
${variable:-word} means if `variable` is unset or empty, the expansion
|
||||
of `word` is substituted. Otherwise, the value of `variable` is
|
||||
substituted.
|
||||
|
||||
${variable:+word} means if `variable` is unset or empty, nothing is
|
||||
substituted. Otherwise, the expansion of `word` is substituted.
|
||||
|
||||
Zypper supports these expansions, see here:
|
||||
https://doc.opensuse.org/projects/libzypp/HEAD/structzypp_1_1repo_1_1RepoVarExpand.html
|
||||
|
||||
For: https://bugzilla.redhat.com/show_bug.cgi?id=1789346
|
||||
---
|
||||
libdnf/conf/ConfigParser.cpp | 189 ++++++++++++++++++++++++++++++-----
|
||||
libdnf/conf/ConfigParser.hpp | 12 +++
|
||||
2 files changed, 175 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/libdnf/conf/ConfigParser.cpp b/libdnf/conf/ConfigParser.cpp
|
||||
index 0755c656..e5d6b3b7 100644
|
||||
--- a/libdnf/conf/ConfigParser.cpp
|
||||
+++ b/libdnf/conf/ConfigParser.cpp
|
||||
@@ -29,40 +29,177 @@ namespace libdnf {
|
||||
void ConfigParser::substitute(std::string & text,
|
||||
const std::map<std::string, std::string> & substitutions)
|
||||
{
|
||||
- auto start = text.find_first_of("$");
|
||||
- while (start != text.npos)
|
||||
- {
|
||||
- auto variable = start + 1;
|
||||
- if (variable >= text.length())
|
||||
- break;
|
||||
- bool bracket;
|
||||
- if (text[variable] == '{') {
|
||||
- bracket = true;
|
||||
- if (++variable >= text.length())
|
||||
+ text = ConfigParser::substitute_expression(text, substitutions, 0).first;
|
||||
+}
|
||||
+
|
||||
+const unsigned int MAXIMUM_EXPRESSION_DEPTH = 32;
|
||||
+
|
||||
+std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::string & text,
|
||||
+ const std::map<std::string, std::string> & substitutions,
|
||||
+ unsigned int depth) {
|
||||
+ if (depth > MAXIMUM_EXPRESSION_DEPTH) {
|
||||
+ return std::make_pair(std::string(text), text.length());
|
||||
+ }
|
||||
+ std::string res{text};
|
||||
+
|
||||
+ // The total number of characters read in the replacee
|
||||
+ size_t total_scanned = 0;
|
||||
+
|
||||
+ size_t pos = 0;
|
||||
+ while (pos < res.length()) {
|
||||
+ if (res[pos] == '}' && depth > 0) {
|
||||
+ return std::make_pair(res.substr(0, pos), total_scanned);
|
||||
+ }
|
||||
+
|
||||
+ if (res[pos] == '\\') {
|
||||
+ // Escape the next character (if there is one)
|
||||
+ if (pos + 1 >= res.length()) {
|
||||
break;
|
||||
- } else
|
||||
- bracket = false;
|
||||
- auto it = std::find_if_not(text.begin()+variable, text.end(),
|
||||
- [](char c){return std::isalnum(c) || c=='_';});
|
||||
- if (bracket && it == text.end())
|
||||
- break;
|
||||
- auto pastVariable = std::distance(text.begin(), it);
|
||||
- if (bracket && *it != '}') {
|
||||
- start = text.find_first_of("$", pastVariable);
|
||||
+ }
|
||||
+ res.erase(pos, 1);
|
||||
+ total_scanned += 2;
|
||||
+ pos += 1;
|
||||
continue;
|
||||
}
|
||||
- auto subst = substitutions.find(text.substr(variable, pastVariable - variable));
|
||||
- if (subst != substitutions.end()) {
|
||||
- if (bracket)
|
||||
- ++pastVariable;
|
||||
- text.replace(start, pastVariable - start, subst->second);
|
||||
- start = text.find_first_of("$", start + subst->second.length());
|
||||
+ if (res[pos] == '$') {
|
||||
+ // variable expression starts after the $ and includes the braces
|
||||
+ // ${variable:-word}
|
||||
+ // ^-- pos_variable_expression
|
||||
+ size_t pos_variable_expression = pos + 1;
|
||||
+ if (pos_variable_expression >= res.length()) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ // Does the variable expression use braces? If so, the variable name
|
||||
+ // starts one character after the start of the variable_expression
|
||||
+ bool has_braces;
|
||||
+ size_t pos_variable;
|
||||
+ if (res[pos_variable_expression] == '{') {
|
||||
+ has_braces = true;
|
||||
+ pos_variable = pos_variable_expression + 1;
|
||||
+ if (pos_variable >= res.length()) {
|
||||
+ break;
|
||||
+ }
|
||||
+ } else {
|
||||
+ has_braces = false;
|
||||
+ pos_variable = pos_variable_expression;
|
||||
+ }
|
||||
+
|
||||
+ // Find the end of the variable name
|
||||
+ auto it = std::find_if_not(res.begin() + static_cast<long>(pos_variable), res.end(), [](char c) {
|
||||
+ return std::isalnum(c) != 0 || c == '_';
|
||||
+ });
|
||||
+ auto pos_after_variable = static_cast<size_t>(std::distance(res.begin(), it));
|
||||
+
|
||||
+ // Find the substituting string and the end of the variable expression
|
||||
+ auto variable_mapping = substitutions.find(res.substr(pos_variable, pos_after_variable - pos_variable));
|
||||
+ const std::string * subst_str = nullptr;
|
||||
+
|
||||
+ size_t pos_after_variable_expression;
|
||||
+
|
||||
+ if (has_braces) {
|
||||
+ if (pos_after_variable >= res.length()) {
|
||||
+ break;
|
||||
+ }
|
||||
+ if (res[pos_after_variable] == ':') {
|
||||
+ if (pos_after_variable + 1 >= res.length()) {
|
||||
+ break;
|
||||
+ }
|
||||
+ char expansion_mode = res[pos_after_variable + 1];
|
||||
+ size_t pos_word = pos_after_variable + 2;
|
||||
+ if (pos_word >= res.length()) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ // Expand the default/alternate expression
|
||||
+ auto word_str = res.substr(pos_word);
|
||||
+ auto word_substitution = substitute_expression(word_str, substitutions, depth + 1);
|
||||
+ auto expanded_word = word_substitution.first;
|
||||
+ auto scanned = word_substitution.second;
|
||||
+ auto pos_after_word = pos_word + scanned;
|
||||
+ if (pos_after_word >= res.length()) {
|
||||
+ break;
|
||||
+ }
|
||||
+ if (res[pos_after_word] != '}') {
|
||||
+ // The variable expression doesn't end in a '}',
|
||||
+ // continue after the word and don't expand it
|
||||
+ total_scanned += pos_after_word - pos;
|
||||
+ pos = pos_after_word;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (expansion_mode == '-') {
|
||||
+ // ${variable:-word} (default value)
|
||||
+ // If variable is unset or empty, the expansion of word is
|
||||
+ // substituted. Otherwise, the value of variable is
|
||||
+ // substituted.
|
||||
+ if (variable_mapping == substitutions.end() || variable_mapping->second.empty()) {
|
||||
+ subst_str = &expanded_word;
|
||||
+ } else {
|
||||
+ subst_str = &variable_mapping->second;
|
||||
+ }
|
||||
+ } else if (expansion_mode == '+') {
|
||||
+ // ${variable:+word} (alternate value)
|
||||
+ // If variable is unset or empty nothing is substituted.
|
||||
+ // Otherwise, the expansion of word is substituted.
|
||||
+ if (variable_mapping == substitutions.end() || variable_mapping->second.empty()) {
|
||||
+ const std::string empty{};
|
||||
+ subst_str = ∅
|
||||
+ } else {
|
||||
+ subst_str = &expanded_word;
|
||||
+ }
|
||||
+ } else {
|
||||
+ // Unknown expansion mode, continue after the ':'
|
||||
+ pos = pos_after_variable + 1;
|
||||
+ continue;
|
||||
+ }
|
||||
+ pos_after_variable_expression = pos_after_word + 1;
|
||||
+ } else if (res[pos_after_variable] == '}') {
|
||||
+ // ${variable}
|
||||
+ if (variable_mapping != substitutions.end()) {
|
||||
+ subst_str = &variable_mapping->second;
|
||||
+ }
|
||||
+ // Move past the closing '}'
|
||||
+ pos_after_variable_expression = pos_after_variable + 1;
|
||||
+ } else {
|
||||
+ // Variable expression doesn't end in a '}', continue after the variable
|
||||
+ pos = pos_after_variable;
|
||||
+ continue;
|
||||
+ }
|
||||
+ } else {
|
||||
+ // No braces, we have a $variable
|
||||
+ if (variable_mapping != substitutions.end()) {
|
||||
+ subst_str = &variable_mapping->second;
|
||||
+ }
|
||||
+ pos_after_variable_expression = pos_after_variable;
|
||||
+ }
|
||||
+
|
||||
+ // If there is no substitution to make, move past the variable expression and continue.
|
||||
+ if (subst_str == nullptr) {
|
||||
+ total_scanned += pos_after_variable_expression - pos;
|
||||
+ pos = pos_after_variable_expression;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ res.replace(pos, pos_after_variable_expression - pos, *subst_str);
|
||||
+ total_scanned += pos_after_variable_expression - pos;
|
||||
+ pos += subst_str->length();
|
||||
} else {
|
||||
- start = text.find_first_of("$", pastVariable);
|
||||
+ total_scanned += 1;
|
||||
+ pos += 1;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // We have reached the end of the text
|
||||
+ if (depth > 0) {
|
||||
+ // If we are in a subexpression and we didn't find a closing '}', make no substitutions.
|
||||
+ return std::make_pair(std::string{text}, text.length());
|
||||
+ }
|
||||
+
|
||||
+ return std::make_pair(res, text.length());
|
||||
}
|
||||
|
||||
+
|
||||
static void read(ConfigParser & cfgParser, IniParser & parser)
|
||||
{
|
||||
IniParser::ItemType readedType;
|
||||
diff --git a/libdnf/conf/ConfigParser.hpp b/libdnf/conf/ConfigParser.hpp
|
||||
index a0d81837..f3d10061 100644
|
||||
--- a/libdnf/conf/ConfigParser.hpp
|
||||
+++ b/libdnf/conf/ConfigParser.hpp
|
||||
@@ -150,6 +150,18 @@ private:
|
||||
int itemNumber{0};
|
||||
std::string header;
|
||||
std::map<std::string, std::string> rawItems;
|
||||
+
|
||||
+ /**
|
||||
+ * @brief Expand variables in a subexpression
|
||||
+ *
|
||||
+ * @param text String with variable expressions
|
||||
+ * @param substitutions Substitution map
|
||||
+ * @param depth The recursive depth
|
||||
+ * @return Pair of the resulting string and the number of characters scanned in `text`
|
||||
+ */
|
||||
+ static std::pair<std::string, size_t> substitute_expression(const std::string & text,
|
||||
+ const std::map<std::string, std::string> & substitutions,
|
||||
+ unsigned int depth);
|
||||
};
|
||||
|
||||
inline void ConfigParser::setSubstitutions(const std::map<std::string, std::string> & substitutions)
|
||||
--
|
||||
2.49.0
|
||||
|
148
0024-conf-split-releasever-to-releasever_major-and-releas.patch
Normal file
148
0024-conf-split-releasever-to-releasever_major-and-releas.patch
Normal file
@ -0,0 +1,148 @@
|
||||
From 89053c30a56da51849ffd5f4323ba3ef04eb8fcd Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Mon, 9 Oct 2023 21:16:57 +0000
|
||||
Subject: [PATCH 02/11] [conf] split $releasever to $releasever_major and
|
||||
$releasever_minor
|
||||
|
||||
This functionality is also implemented in DNF 4, but direct consumers of
|
||||
libdnf (PackageKit, microdnf) also need it.
|
||||
|
||||
DNF 4 PR: https://github.com/rpm-software-management/dnf/pull/1989
|
||||
|
||||
For https://bugzilla.redhat.com/show_bug.cgi?id=1789346
|
||||
---
|
||||
libdnf/conf/ConfigParser.cpp | 50 +++++++++++++++++++++++++-------
|
||||
libdnf/conf/ConfigParser.hpp | 2 ++
|
||||
tests/libdnf/conf/CMakeLists.txt | 11 +++++++
|
||||
3 files changed, 53 insertions(+), 10 deletions(-)
|
||||
create mode 100644 tests/libdnf/conf/CMakeLists.txt
|
||||
|
||||
diff --git a/libdnf/conf/ConfigParser.cpp b/libdnf/conf/ConfigParser.cpp
|
||||
index e5d6b3b7..a89fd8bf 100644
|
||||
--- a/libdnf/conf/ConfigParser.cpp
|
||||
+++ b/libdnf/conf/ConfigParser.cpp
|
||||
@@ -92,7 +92,27 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
auto pos_after_variable = static_cast<size_t>(std::distance(res.begin(), it));
|
||||
|
||||
// Find the substituting string and the end of the variable expression
|
||||
- auto variable_mapping = substitutions.find(res.substr(pos_variable, pos_after_variable - pos_variable));
|
||||
+ const auto & variable_key = res.substr(pos_variable, pos_after_variable - pos_variable);
|
||||
+ const auto variable_mapping = substitutions.find(variable_key);
|
||||
+
|
||||
+ const std::string * variable_value = nullptr;
|
||||
+
|
||||
+ if (variable_mapping == substitutions.end()) {
|
||||
+ if (variable_key == "releasever_major" || variable_key == "releasever_minor") {
|
||||
+ const auto releasever_mapping = substitutions.find("releasever");
|
||||
+ if (releasever_mapping != substitutions.end()) {
|
||||
+ const auto & releasever_split = ConfigParser::split_releasever(releasever_mapping->second);
|
||||
+ if (variable_key == "releasever_major") {
|
||||
+ variable_value = &std::get<0>(releasever_split);
|
||||
+ } else {
|
||||
+ variable_value = &std::get<1>(releasever_split);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ variable_value = &variable_mapping->second;
|
||||
+ }
|
||||
+
|
||||
const std::string * subst_str = nullptr;
|
||||
|
||||
size_t pos_after_variable_expression;
|
||||
@@ -133,16 +153,16 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
// If variable is unset or empty, the expansion of word is
|
||||
// substituted. Otherwise, the value of variable is
|
||||
// substituted.
|
||||
- if (variable_mapping == substitutions.end() || variable_mapping->second.empty()) {
|
||||
+ if (variable_value == nullptr || variable_value->empty()) {
|
||||
subst_str = &expanded_word;
|
||||
} else {
|
||||
- subst_str = &variable_mapping->second;
|
||||
+ subst_str = variable_value;
|
||||
}
|
||||
} else if (expansion_mode == '+') {
|
||||
// ${variable:+word} (alternate value)
|
||||
// If variable is unset or empty nothing is substituted.
|
||||
// Otherwise, the expansion of word is substituted.
|
||||
- if (variable_mapping == substitutions.end() || variable_mapping->second.empty()) {
|
||||
+ if (variable_value == nullptr || variable_value->empty()) {
|
||||
const std::string empty{};
|
||||
subst_str = ∅
|
||||
} else {
|
||||
@@ -156,9 +176,7 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
pos_after_variable_expression = pos_after_word + 1;
|
||||
} else if (res[pos_after_variable] == '}') {
|
||||
// ${variable}
|
||||
- if (variable_mapping != substitutions.end()) {
|
||||
- subst_str = &variable_mapping->second;
|
||||
- }
|
||||
+ subst_str = variable_value;
|
||||
// Move past the closing '}'
|
||||
pos_after_variable_expression = pos_after_variable + 1;
|
||||
} else {
|
||||
@@ -168,9 +186,7 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
}
|
||||
} else {
|
||||
// No braces, we have a $variable
|
||||
- if (variable_mapping != substitutions.end()) {
|
||||
- subst_str = &variable_mapping->second;
|
||||
- }
|
||||
+ subst_str = variable_value;
|
||||
pos_after_variable_expression = pos_after_variable;
|
||||
}
|
||||
|
||||
@@ -199,6 +215,20 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
return std::make_pair(res, text.length());
|
||||
}
|
||||
|
||||
+std::tuple<std::string, std::string> ConfigParser::split_releasever(const std::string & releasever)
|
||||
+{
|
||||
+ // Uses the same logic as DNF 5 and as splitReleaseverTo in libzypp
|
||||
+ std::string releasever_major;
|
||||
+ std::string releasever_minor;
|
||||
+ const auto pos = releasever.find('.');
|
||||
+ if (pos == std::string::npos) {
|
||||
+ releasever_major = releasever;
|
||||
+ } else {
|
||||
+ releasever_major = releasever.substr(0, pos);
|
||||
+ releasever_minor = releasever.substr(pos + 1);
|
||||
+ }
|
||||
+ return std::make_tuple(releasever_major, releasever_minor);
|
||||
+}
|
||||
|
||||
static void read(ConfigParser & cfgParser, IniParser & parser)
|
||||
{
|
||||
diff --git a/libdnf/conf/ConfigParser.hpp b/libdnf/conf/ConfigParser.hpp
|
||||
index f3d10061..f30dd4a4 100644
|
||||
--- a/libdnf/conf/ConfigParser.hpp
|
||||
+++ b/libdnf/conf/ConfigParser.hpp
|
||||
@@ -162,6 +162,8 @@ private:
|
||||
static std::pair<std::string, size_t> substitute_expression(const std::string & text,
|
||||
const std::map<std::string, std::string> & substitutions,
|
||||
unsigned int depth);
|
||||
+
|
||||
+ static std::tuple<std::string, std::string> split_releasever(const std::string & releasever);
|
||||
};
|
||||
|
||||
inline void ConfigParser::setSubstitutions(const std::map<std::string, std::string> & substitutions)
|
||||
diff --git a/tests/libdnf/conf/CMakeLists.txt b/tests/libdnf/conf/CMakeLists.txt
|
||||
new file mode 100644
|
||||
index 00000000..05058367
|
||||
--- /dev/null
|
||||
+++ b/tests/libdnf/conf/CMakeLists.txt
|
||||
@@ -0,0 +1,11 @@
|
||||
+set(LIBDNF_TEST_SOURCES
|
||||
+ ${LIBDNF_TEST_SOURCES}
|
||||
+ ${CMAKE_CURRENT_SOURCE_DIR}/ConfigParserTest.cpp
|
||||
+ PARENT_SCOPE
|
||||
+)
|
||||
+
|
||||
+set(LIBDNF_TEST_HEADERS
|
||||
+ ${LIBDNF_TEST_HEADERS}
|
||||
+ ${CMAKE_CURRENT_SOURCE_DIR}/ConfigParserTest.hpp
|
||||
+ PARENT_SCOPE
|
||||
+)
|
||||
--
|
||||
2.49.0
|
||||
|
91
0025-Test-for-releasever_major-releasever_minor.patch
Normal file
91
0025-Test-for-releasever_major-releasever_minor.patch
Normal file
@ -0,0 +1,91 @@
|
||||
From 734a573408643c90faf7ff57ea33fec5fb467553 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Mon, 9 Oct 2023 21:54:08 +0000
|
||||
Subject: [PATCH 03/11] Test for $releasever_major, $releasever_minor
|
||||
|
||||
---
|
||||
tests/CMakeLists.txt | 1 +
|
||||
tests/libdnf/conf/ConfigParserTest.cpp | 33 ++++++++++++++++++++++++++
|
||||
tests/libdnf/conf/ConfigParserTest.hpp | 21 ++++++++++++++++
|
||||
3 files changed, 55 insertions(+)
|
||||
create mode 100644 tests/libdnf/conf/ConfigParserTest.cpp
|
||||
create mode 100644 tests/libdnf/conf/ConfigParserTest.hpp
|
||||
|
||||
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
|
||||
index 85b47ca1..e4909682 100644
|
||||
--- a/tests/CMakeLists.txt
|
||||
+++ b/tests/CMakeLists.txt
|
||||
@@ -1,3 +1,4 @@
|
||||
+add_subdirectory(libdnf/conf)
|
||||
add_subdirectory(libdnf/module/modulemd)
|
||||
add_subdirectory(libdnf/module)
|
||||
add_subdirectory(libdnf/repo)
|
||||
diff --git a/tests/libdnf/conf/ConfigParserTest.cpp b/tests/libdnf/conf/ConfigParserTest.cpp
|
||||
new file mode 100644
|
||||
index 00000000..70278196
|
||||
--- /dev/null
|
||||
+++ b/tests/libdnf/conf/ConfigParserTest.cpp
|
||||
@@ -0,0 +1,33 @@
|
||||
+#include "ConfigParserTest.hpp"
|
||||
+
|
||||
+CPPUNIT_TEST_SUITE_REGISTRATION(ConfigParserTest);
|
||||
+
|
||||
+void ConfigParserTest::setUp()
|
||||
+{}
|
||||
+
|
||||
+void ConfigParserTest::testConfigParserReleasever()
|
||||
+{
|
||||
+ {
|
||||
+ // Test $releasever_major, $releasever_minor
|
||||
+ std::map<std::string, std::string> substitutions = {
|
||||
+ {"releasever", "1.23"},
|
||||
+ };
|
||||
+
|
||||
+ std::string text = "major: $releasever_major, minor: $releasever_minor";
|
||||
+ libdnf::ConfigParser::substitute(text, substitutions);
|
||||
+ CPPUNIT_ASSERT(text == "major: 1, minor: 23");
|
||||
+
|
||||
+ text = "full releasever: $releasever";
|
||||
+ libdnf::ConfigParser::substitute(text, substitutions);
|
||||
+ CPPUNIT_ASSERT(text == "full releasever: 1.23");
|
||||
+ }
|
||||
+ {
|
||||
+ // Test with empty $releasever, should set empty $releasever_major, $releasever_minor
|
||||
+ std::map<std::string, std::string> substitutions = {
|
||||
+ {"releasever", ""},
|
||||
+ };
|
||||
+ std::string text = "major: $releasever_major, minor: $releasever_minor";
|
||||
+ libdnf::ConfigParser::substitute(text, substitutions);
|
||||
+ CPPUNIT_ASSERT(text == "major: , minor: ");
|
||||
+ }
|
||||
+}
|
||||
diff --git a/tests/libdnf/conf/ConfigParserTest.hpp b/tests/libdnf/conf/ConfigParserTest.hpp
|
||||
new file mode 100644
|
||||
index 00000000..7f1faf47
|
||||
--- /dev/null
|
||||
+++ b/tests/libdnf/conf/ConfigParserTest.hpp
|
||||
@@ -0,0 +1,21 @@
|
||||
+#ifndef LIBDNF_CONFIGPARSERTEST_HPP
|
||||
+#define LIBDNF_CONFIGPARSERTEST_HPP
|
||||
+
|
||||
+#include <cppunit/TestCase.h>
|
||||
+#include <cppunit/extensions/HelperMacros.h>
|
||||
+
|
||||
+#include <libdnf/conf/ConfigParser.hpp>
|
||||
+
|
||||
+class ConfigParserTest : public CppUnit::TestCase
|
||||
+{
|
||||
+ CPPUNIT_TEST_SUITE(ConfigParserTest);
|
||||
+ CPPUNIT_TEST(testConfigParserReleasever);
|
||||
+ CPPUNIT_TEST_SUITE_END();
|
||||
+
|
||||
+public:
|
||||
+ void setUp() override;
|
||||
+ void testConfigParserReleasever();
|
||||
+
|
||||
+};
|
||||
+
|
||||
+#endif // LIBDNF_CONFIGPARSERTEST_HPP
|
||||
--
|
||||
2.49.0
|
||||
|
124
0026-ConfigParser-fix-use-out-of-scope-leaks.patch
Normal file
124
0026-ConfigParser-fix-use-out-of-scope-leaks.patch
Normal file
@ -0,0 +1,124 @@
|
||||
From ff0911502ae7a71f9a872d93f91c395f2f04de4a Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Tue, 7 May 2024 16:33:03 +0000
|
||||
Subject: [PATCH 04/11] ConfigParser: fix use-out-of-scope leaks
|
||||
|
||||
---
|
||||
libdnf/conf/ConfigParser.cpp | 48 ++++++++++++++++++++++++------------
|
||||
1 file changed, 32 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/libdnf/conf/ConfigParser.cpp b/libdnf/conf/ConfigParser.cpp
|
||||
index a89fd8bf..18e5a195 100644
|
||||
--- a/libdnf/conf/ConfigParser.cpp
|
||||
+++ b/libdnf/conf/ConfigParser.cpp
|
||||
@@ -95,7 +95,9 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
const auto & variable_key = res.substr(pos_variable, pos_after_variable - pos_variable);
|
||||
const auto variable_mapping = substitutions.find(variable_key);
|
||||
|
||||
- const std::string * variable_value = nullptr;
|
||||
+ // No std::optional here.
|
||||
+ bool variable_value_has_value{false};
|
||||
+ std::string variable_value{""};
|
||||
|
||||
if (variable_mapping == substitutions.end()) {
|
||||
if (variable_key == "releasever_major" || variable_key == "releasever_minor") {
|
||||
@@ -103,17 +105,22 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
if (releasever_mapping != substitutions.end()) {
|
||||
const auto & releasever_split = ConfigParser::split_releasever(releasever_mapping->second);
|
||||
if (variable_key == "releasever_major") {
|
||||
- variable_value = &std::get<0>(releasever_split);
|
||||
+ variable_value = std::get<0>(releasever_split);
|
||||
+ variable_value_has_value = true;
|
||||
} else {
|
||||
- variable_value = &std::get<1>(releasever_split);
|
||||
+ variable_value = std::get<1>(releasever_split);
|
||||
+ variable_value_has_value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
- variable_value = &variable_mapping->second;
|
||||
+ variable_value = variable_mapping->second;
|
||||
+ variable_value_has_value = true;
|
||||
}
|
||||
|
||||
- const std::string * subst_str = nullptr;
|
||||
+ // No std::optional here
|
||||
+ std::string subst_str{""};
|
||||
+ bool subst_str_has_value{false};
|
||||
|
||||
size_t pos_after_variable_expression;
|
||||
|
||||
@@ -153,20 +160,23 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
// If variable is unset or empty, the expansion of word is
|
||||
// substituted. Otherwise, the value of variable is
|
||||
// substituted.
|
||||
- if (variable_value == nullptr || variable_value->empty()) {
|
||||
- subst_str = &expanded_word;
|
||||
+ if (!variable_value_has_value || variable_value.empty()) {
|
||||
+ subst_str = expanded_word;
|
||||
+ subst_str_has_value = true;
|
||||
} else {
|
||||
subst_str = variable_value;
|
||||
+ subst_str_has_value = true;
|
||||
}
|
||||
} else if (expansion_mode == '+') {
|
||||
// ${variable:+word} (alternate value)
|
||||
// If variable is unset or empty nothing is substituted.
|
||||
// Otherwise, the expansion of word is substituted.
|
||||
- if (variable_value == nullptr || variable_value->empty()) {
|
||||
- const std::string empty{};
|
||||
- subst_str = ∅
|
||||
+ if (!variable_value_has_value || variable_value.empty()) {
|
||||
+ subst_str = "";
|
||||
+ subst_str_has_value = true;
|
||||
} else {
|
||||
- subst_str = &expanded_word;
|
||||
+ subst_str = expanded_word;
|
||||
+ subst_str_has_value = true;
|
||||
}
|
||||
} else {
|
||||
// Unknown expansion mode, continue after the ':'
|
||||
@@ -176,7 +186,10 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
pos_after_variable_expression = pos_after_word + 1;
|
||||
} else if (res[pos_after_variable] == '}') {
|
||||
// ${variable}
|
||||
- subst_str = variable_value;
|
||||
+ if (variable_value_has_value) {
|
||||
+ subst_str = variable_value;
|
||||
+ subst_str_has_value = true;
|
||||
+ }
|
||||
// Move past the closing '}'
|
||||
pos_after_variable_expression = pos_after_variable + 1;
|
||||
} else {
|
||||
@@ -186,20 +199,23 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
}
|
||||
} else {
|
||||
// No braces, we have a $variable
|
||||
- subst_str = variable_value;
|
||||
+ if (variable_value_has_value) {
|
||||
+ subst_str = variable_value;
|
||||
+ subst_str_has_value = true;
|
||||
+ }
|
||||
pos_after_variable_expression = pos_after_variable;
|
||||
}
|
||||
|
||||
// If there is no substitution to make, move past the variable expression and continue.
|
||||
- if (subst_str == nullptr) {
|
||||
+ if (!subst_str_has_value) {
|
||||
total_scanned += pos_after_variable_expression - pos;
|
||||
pos = pos_after_variable_expression;
|
||||
continue;
|
||||
}
|
||||
|
||||
- res.replace(pos, pos_after_variable_expression - pos, *subst_str);
|
||||
+ res.replace(pos, pos_after_variable_expression - pos, subst_str);
|
||||
total_scanned += pos_after_variable_expression - pos;
|
||||
- pos += subst_str->length();
|
||||
+ pos += subst_str.length();
|
||||
} else {
|
||||
total_scanned += 1;
|
||||
pos += 1;
|
||||
--
|
||||
2.49.0
|
||||
|
38
0027-Add-tests-for-shell-style-variable-expansion.patch
Normal file
38
0027-Add-tests-for-shell-style-variable-expansion.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From 89b931d8036499af18697332e16fe66955fdcee0 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Tue, 7 May 2024 16:28:59 +0000
|
||||
Subject: [PATCH 05/11] Add tests for shell-style variable expansion
|
||||
|
||||
---
|
||||
tests/libdnf/conf/ConfigParserTest.cpp | 17 +++++++++++++++++
|
||||
1 file changed, 17 insertions(+)
|
||||
|
||||
diff --git a/tests/libdnf/conf/ConfigParserTest.cpp b/tests/libdnf/conf/ConfigParserTest.cpp
|
||||
index 70278196..1448d8d3 100644
|
||||
--- a/tests/libdnf/conf/ConfigParserTest.cpp
|
||||
+++ b/tests/libdnf/conf/ConfigParserTest.cpp
|
||||
@@ -30,4 +30,21 @@ void ConfigParserTest::testConfigParserReleasever()
|
||||
libdnf::ConfigParser::substitute(text, substitutions);
|
||||
CPPUNIT_ASSERT(text == "major: , minor: ");
|
||||
}
|
||||
+ {
|
||||
+ std::map<std::string, std::string> substitutions = {
|
||||
+ {"var1", "value123"},
|
||||
+ {"var2", "456"},
|
||||
+ };
|
||||
+ std::string text = "foo$var1-bar";
|
||||
+ libdnf::ConfigParser::substitute(text, substitutions);
|
||||
+ CPPUNIT_ASSERT(text == "foovalue123-bar");
|
||||
+
|
||||
+ text = "${var1:+alternate}-${unset:-default}-${nn:+n${nn:-${nnn:}";
|
||||
+ libdnf::ConfigParser::substitute(text, substitutions);
|
||||
+ CPPUNIT_ASSERT(text == "alternate-default-${nn:+n${nn:-${nnn:}");
|
||||
+
|
||||
+ text = "${unset:-${var1:+${var2:+$var2}}}";
|
||||
+ libdnf::ConfigParser::substitute(text, substitutions);
|
||||
+ CPPUNIT_ASSERT(text == "456");
|
||||
+ }
|
||||
}
|
||||
--
|
||||
2.49.0
|
||||
|
156
0028-Split-releasever-to-releasever_major-and-releasever_.patch
Normal file
156
0028-Split-releasever-to-releasever_major-and-releasever_.patch
Normal file
@ -0,0 +1,156 @@
|
||||
From a131207df220a5f81e57f1a61931b0fdcba3325c Mon Sep 17 00:00:00 2001
|
||||
From: Diego Herrera <dherrera@redhat.com>
|
||||
Date: Thu, 16 Jan 2025 18:29:40 -0300
|
||||
Subject: [PATCH 06/11] Split $releasever to $releasever_major and
|
||||
$releasever_minor in c api
|
||||
|
||||
---
|
||||
libdnf/dnf-repo.cpp | 6 ++++-
|
||||
libdnf/dnf-utils.cpp | 44 ++++++++++++++++++++++++++++++++++++
|
||||
libdnf/dnf-utils.h | 3 +++
|
||||
tests/libdnf/dnf-self-test.c | 27 ++++++++++++++++++++++
|
||||
4 files changed, 79 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp
|
||||
index 48434fd9..17aa04be 100644
|
||||
--- a/libdnf/dnf-repo.cpp
|
||||
+++ b/libdnf/dnf-repo.cpp
|
||||
@@ -1191,6 +1191,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 *major = NULL;
|
||||
+ g_autofree gchar *minor = NULL;
|
||||
|
||||
basearch = g_key_file_get_string(priv->keyfile, "general", "arch", NULL);
|
||||
if (basearch == NULL)
|
||||
@@ -1218,9 +1220,11 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
|
||||
return FALSE;
|
||||
if (!lr_handle_setopt(priv->repo_handle, error, LRO_INTERRUPTIBLE, 0L))
|
||||
return FALSE;
|
||||
+ dnf_split_releasever(release, &major, &minor);
|
||||
priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever", release);
|
||||
+ priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever_major", major);
|
||||
+ priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever_minor", 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))
|
||||
diff --git a/libdnf/dnf-utils.cpp b/libdnf/dnf-utils.cpp
|
||||
index 874282cf..43c84b82 100644
|
||||
--- a/libdnf/dnf-utils.cpp
|
||||
+++ b/libdnf/dnf-utils.cpp
|
||||
@@ -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
|
||||
diff --git a/libdnf/dnf-utils.h b/libdnf/dnf-utils.h
|
||||
index 6b711918..c10dd53f 100644
|
||||
--- a/libdnf/dnf-utils.h
|
||||
+++ b/libdnf/dnf-utils.h
|
||||
@@ -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,
|
||||
diff --git a/tests/libdnf/dnf-self-test.c b/tests/libdnf/dnf-self-test.c
|
||||
index 1e2bfac3..2a0371c9 100644
|
||||
--- a/tests/libdnf/dnf-self-test.c
|
||||
+++ b/tests/libdnf/dnf-self-test.c
|
||||
@@ -188,6 +188,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)
|
||||
{
|
||||
@@ -1239,6 +1265,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);
|
||||
--
|
||||
2.49.0
|
||||
|
78
0029-ConfigParser-make-splitReleasever-public.patch
Normal file
78
0029-ConfigParser-make-splitReleasever-public.patch
Normal file
@ -0,0 +1,78 @@
|
||||
From 15f1efa3d3b60e2de34f130e9f4d190a92a5a4ed Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Tue, 21 Jan 2025 19:19:06 +0000
|
||||
Subject: [PATCH 07/11] ConfigParser: make splitReleasever public
|
||||
|
||||
---
|
||||
bindings/swig/conf.i | 1 +
|
||||
libdnf/conf/ConfigParser.cpp | 6 +++---
|
||||
libdnf/conf/ConfigParser.hpp | 3 +--
|
||||
3 files changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/bindings/swig/conf.i b/bindings/swig/conf.i
|
||||
index b6a0ce88..c42d5673 100644
|
||||
--- a/bindings/swig/conf.i
|
||||
+++ b/bindings/swig/conf.i
|
||||
@@ -199,6 +199,7 @@ public:
|
||||
std::string & getHeader() noexcept;
|
||||
const Container & getData() const noexcept;
|
||||
Container & getData() noexcept;
|
||||
+ static std::pair<std::string, std::string> splitReleasever(const std::string & releasever);
|
||||
};
|
||||
}
|
||||
%clear std::string & text;
|
||||
diff --git a/libdnf/conf/ConfigParser.cpp b/libdnf/conf/ConfigParser.cpp
|
||||
index 18e5a195..6ff110a7 100644
|
||||
--- a/libdnf/conf/ConfigParser.cpp
|
||||
+++ b/libdnf/conf/ConfigParser.cpp
|
||||
@@ -103,7 +103,7 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
if (variable_key == "releasever_major" || variable_key == "releasever_minor") {
|
||||
const auto releasever_mapping = substitutions.find("releasever");
|
||||
if (releasever_mapping != substitutions.end()) {
|
||||
- const auto & releasever_split = ConfigParser::split_releasever(releasever_mapping->second);
|
||||
+ const auto & releasever_split = ConfigParser::splitReleasever(releasever_mapping->second);
|
||||
if (variable_key == "releasever_major") {
|
||||
variable_value = std::get<0>(releasever_split);
|
||||
variable_value_has_value = true;
|
||||
@@ -231,7 +231,7 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
|
||||
return std::make_pair(res, text.length());
|
||||
}
|
||||
|
||||
-std::tuple<std::string, std::string> ConfigParser::split_releasever(const std::string & releasever)
|
||||
+std::pair<std::string, std::string> ConfigParser::splitReleasever(const std::string & releasever)
|
||||
{
|
||||
// Uses the same logic as DNF 5 and as splitReleaseverTo in libzypp
|
||||
std::string releasever_major;
|
||||
@@ -243,7 +243,7 @@ std::tuple<std::string, std::string> ConfigParser::split_releasever(const std::s
|
||||
releasever_major = releasever.substr(0, pos);
|
||||
releasever_minor = releasever.substr(pos + 1);
|
||||
}
|
||||
- return std::make_tuple(releasever_major, releasever_minor);
|
||||
+ return std::make_pair(releasever_major, releasever_minor);
|
||||
}
|
||||
|
||||
static void read(ConfigParser & cfgParser, IniParser & parser)
|
||||
diff --git a/libdnf/conf/ConfigParser.hpp b/libdnf/conf/ConfigParser.hpp
|
||||
index f30dd4a4..2d269147 100644
|
||||
--- a/libdnf/conf/ConfigParser.hpp
|
||||
+++ b/libdnf/conf/ConfigParser.hpp
|
||||
@@ -143,6 +143,7 @@ public:
|
||||
std::string & getHeader() noexcept;
|
||||
const Container & getData() const noexcept;
|
||||
Container & getData() noexcept;
|
||||
+ static std::pair<std::string, std::string> splitReleasever(const std::string & releasever);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> substitutions;
|
||||
@@ -162,8 +163,6 @@ private:
|
||||
static std::pair<std::string, size_t> substitute_expression(const std::string & text,
|
||||
const std::map<std::string, std::string> & substitutions,
|
||||
unsigned int depth);
|
||||
-
|
||||
- static std::tuple<std::string, std::string> split_releasever(const std::string & releasever);
|
||||
};
|
||||
|
||||
inline void ConfigParser::setSubstitutions(const std::map<std::string, std::string> & substitutions)
|
||||
--
|
||||
2.49.0
|
||||
|
147
0030-C-API-Detect-releasever_major-releasever_minor-from-.patch
Normal file
147
0030-C-API-Detect-releasever_major-releasever_minor-from-.patch
Normal file
@ -0,0 +1,147 @@
|
||||
From beff5b9e184de2df12be744f8e776846dda49459 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Mon, 3 Feb 2025 20:31:12 +0000
|
||||
Subject: [PATCH 08/11] C API: Detect releasever_major, releasever_minor from
|
||||
provides
|
||||
|
||||
releasever_major and releasever_minor can now be overridden by virtual
|
||||
provides on the system-release package (any of `DISTROVERPKG`). The
|
||||
detection of releasever is unchanged. releasever_major and
|
||||
releasever_minor are specified by the versions of the
|
||||
`system-release-major` and `system-release-minor` provides,
|
||||
respectively.
|
||||
|
||||
Introduces dnf_context_set_release_ver_major and
|
||||
dnf_context_set_release_ver_minor.
|
||||
---
|
||||
libdnf/dnf-context.cpp | 65 ++++++++++++++++++++++++++++++++++++++++--
|
||||
libdnf/dnf-context.h | 4 +++
|
||||
2 files changed, 66 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/libdnf/dnf-context.cpp b/libdnf/dnf-context.cpp
|
||||
index 97d1c599..d51cb08c 100644
|
||||
--- a/libdnf/dnf-context.cpp
|
||||
+++ b/libdnf/dnf-context.cpp
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -1263,7 +1267,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
|
||||
**/
|
||||
@@ -1273,6 +1279,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1649,13 +1695,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;
|
||||
}
|
||||
diff --git a/libdnf/dnf-context.h b/libdnf/dnf-context.h
|
||||
index cb00a29b..4d8481b2 100644
|
||||
--- a/libdnf/dnf-context.h
|
||||
+++ b/libdnf/dnf-context.h
|
||||
@@ -164,6 +164,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,
|
||||
--
|
||||
2.49.0
|
||||
|
121
0031-C-API-Use-releasever_-major-minor-from-context-inste.patch
Normal file
121
0031-C-API-Use-releasever_-major-minor-from-context-inste.patch
Normal file
@ -0,0 +1,121 @@
|
||||
From 80f6ccc3f3f28ddc48e1f175e7948b0c1a663337 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Mon, 3 Feb 2025 16:06:35 -0500
|
||||
Subject: [PATCH 09/11] C API: Use releasever_{major,minor} from context
|
||||
instead of always splitting
|
||||
|
||||
Introduces dnf_context_get_release_ver_major and
|
||||
dnf_context_get_release_ver_minor.
|
||||
---
|
||||
libdnf/dnf-context.cpp | 36 ++++++++++++++++++++++++++++++++++++
|
||||
libdnf/dnf-context.h | 2 ++
|
||||
libdnf/dnf-repo.cpp | 17 +++++++++++------
|
||||
3 files changed, 49 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/libdnf/dnf-context.cpp b/libdnf/dnf-context.cpp
|
||||
index d51cb08c..ffd6b662 100644
|
||||
--- a/libdnf/dnf-context.cpp
|
||||
+++ b/libdnf/dnf-context.cpp
|
||||
@@ -620,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.
|
||||
diff --git a/libdnf/dnf-context.h b/libdnf/dnf-context.h
|
||||
index 4d8481b2..8e1c948e 100644
|
||||
--- a/libdnf/dnf-context.h
|
||||
+++ b/libdnf/dnf-context.h
|
||||
@@ -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);
|
||||
diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp
|
||||
index 17aa04be..00555ada 100644
|
||||
--- a/libdnf/dnf-repo.cpp
|
||||
+++ b/libdnf/dnf-repo.cpp
|
||||
@@ -1191,8 +1191,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 *major = NULL;
|
||||
- g_autofree gchar *minor = 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)
|
||||
@@ -1205,8 +1205,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,
|
||||
@@ -1220,10 +1226,9 @@ dnf_repo_setup(DnfRepo *repo, GError **error) try
|
||||
return FALSE;
|
||||
if (!lr_handle_setopt(priv->repo_handle, error, LRO_INTERRUPTIBLE, 0L))
|
||||
return FALSE;
|
||||
- dnf_split_releasever(release, &major, &minor);
|
||||
priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever", release);
|
||||
- priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever_major", major);
|
||||
- priv->urlvars = lr_urlvars_set(priv->urlvars, "releasever_minor", minor);
|
||||
+ 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)?
|
||||
--
|
||||
2.49.0
|
||||
|
60
0032-C-API-support-shell-style-variable-substitution.patch
Normal file
60
0032-C-API-support-shell-style-variable-substitution.patch
Normal file
@ -0,0 +1,60 @@
|
||||
From 39d5473950b5ea0a3c75c30ad729538143fcb017 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Fri, 7 Feb 2025 18:02:20 +0000
|
||||
Subject: [PATCH 10/11] C API: support shell-style variable substitution
|
||||
|
||||
Rework `dnf_repo_substitute` to call the C++ API's
|
||||
ConfigParser::substitute instead of librepo's lr_url_substitute.
|
||||
|
||||
Resolves https://github.com/rpm-software-management/libdnf/issues/1690
|
||||
---
|
||||
libdnf/dnf-repo.cpp | 17 +++++++++++------
|
||||
1 file changed, 11 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/libdnf/dnf-repo.cpp b/libdnf/dnf-repo.cpp
|
||||
index 00555ada..86c31be0 100644
|
||||
--- a/libdnf/dnf-repo.cpp
|
||||
+++ b/libdnf/dnf-repo.cpp
|
||||
@@ -34,6 +34,7 @@
|
||||
*/
|
||||
|
||||
#include "conf/OptionBool.hpp"
|
||||
+#include "conf/ConfigParser.hpp"
|
||||
|
||||
#include "dnf-context.hpp"
|
||||
#include "hy-repo-private.hpp"
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
--
|
||||
2.49.0
|
||||
|
80
0033-C-API-test-shell-style-variable-expressions.patch
Normal file
80
0033-C-API-test-shell-style-variable-expressions.patch
Normal file
@ -0,0 +1,80 @@
|
||||
From bb6638eafa4c82cbbc672645dbf30575e7453b15 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Fri, 7 Feb 2025 18:42:43 +0000
|
||||
Subject: [PATCH 11/11] C API: test shell-style variable expressions
|
||||
|
||||
---
|
||||
data/tests/vars/var1 | 1 +
|
||||
data/tests/vars/var2 | 1 +
|
||||
data/tests/yum.repos.d/shell-expansion.repo | 5 +++++
|
||||
tests/libdnf/dnf-self-test.c | 10 ++++++++++
|
||||
4 files changed, 17 insertions(+)
|
||||
create mode 100644 data/tests/vars/var1
|
||||
create mode 100644 data/tests/vars/var2
|
||||
create mode 100644 data/tests/yum.repos.d/shell-expansion.repo
|
||||
|
||||
diff --git a/data/tests/vars/var1 b/data/tests/vars/var1
|
||||
new file mode 100644
|
||||
index 00000000..a9f37252
|
||||
--- /dev/null
|
||||
+++ b/data/tests/vars/var1
|
||||
@@ -0,0 +1 @@
|
||||
+value123
|
||||
diff --git a/data/tests/vars/var2 b/data/tests/vars/var2
|
||||
new file mode 100644
|
||||
index 00000000..8d38505c
|
||||
--- /dev/null
|
||||
+++ b/data/tests/vars/var2
|
||||
@@ -0,0 +1 @@
|
||||
+456
|
||||
diff --git a/data/tests/yum.repos.d/shell-expansion.repo b/data/tests/yum.repos.d/shell-expansion.repo
|
||||
new file mode 100644
|
||||
index 00000000..3bd4c1ec
|
||||
--- /dev/null
|
||||
+++ b/data/tests/yum.repos.d/shell-expansion.repo
|
||||
@@ -0,0 +1,5 @@
|
||||
+[shell-expansion]
|
||||
+name=${unset:-${var1:+${var2:+$var2}}}
|
||||
+baseurl=https://${unset:-${var1:+${var2:+$var2}}}
|
||||
+enabled=1
|
||||
+gpgcheck=0
|
||||
diff --git a/tests/libdnf/dnf-self-test.c b/tests/libdnf/dnf-self-test.c
|
||||
index 2a0371c9..1596870e 100644
|
||||
--- a/tests/libdnf/dnf-self-test.c
|
||||
+++ b/tests/libdnf/dnf-self-test.c
|
||||
@@ -842,6 +842,7 @@ 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;
|
||||
@@ -849,8 +850,10 @@ dnf_repo_loader_func(void)
|
||||
/* 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);
|
||||
@@ -906,6 +909,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
|
||||
--
|
||||
2.49.0
|
||||
|
17
libdnf.spec
17
libdnf.spec
@ -58,7 +58,7 @@
|
||||
|
||||
Name: libdnf
|
||||
Version: %{libdnf_major_version}.%{libdnf_minor_version}.%{libdnf_micro_version}
|
||||
Release: 15%{?dist}
|
||||
Release: 16%{?dist}
|
||||
Summary: Library providing simplified C and Python API to libsolv
|
||||
License: LGPLv2+
|
||||
URL: https://github.com/rpm-software-management/libdnf
|
||||
@ -85,6 +85,17 @@ Patch19: 0019-module-Warn-if-module-config-file-is-inaccessible.patch
|
||||
Patch20: 0020-history-DB-Add-persistence-column.patch
|
||||
Patch21: 0021-MergedTransaction-listPersistences.patch
|
||||
Patch22: 0022-conf-Add-usr_drift_protected_paths.patch
|
||||
Patch23: 0023-conf-Add-limited-shell-style-variable-expansion.patch
|
||||
Patch24: 0024-conf-split-releasever-to-releasever_major-and-releas.patch
|
||||
Patch25: 0025-Test-for-releasever_major-releasever_minor.patch
|
||||
Patch26: 0026-ConfigParser-fix-use-out-of-scope-leaks.patch
|
||||
Patch27: 0027-Add-tests-for-shell-style-variable-expansion.patch
|
||||
Patch28: 0028-Split-releasever-to-releasever_major-and-releasever_.patch
|
||||
Patch29: 0029-ConfigParser-make-splitReleasever-public.patch
|
||||
Patch30: 0030-C-API-Detect-releasever_major-releasever_minor-from-.patch
|
||||
Patch31: 0031-C-API-Use-releasever_-major-minor-from-context-inste.patch
|
||||
Patch32: 0032-C-API-support-shell-style-variable-substitution.patch
|
||||
Patch33: 0033-C-API-test-shell-style-variable-expressions.patch
|
||||
|
||||
|
||||
BuildRequires: cmake
|
||||
@ -334,6 +345,10 @@ popd
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Jun 30 2025 Evan Goode <egoode@redhat.com> - 0.69.0-16
|
||||
- Introduce $releasever_major, $releasever_minor variables, shell-style
|
||||
variable substitution (RHEL-95006)
|
||||
|
||||
* Thu Jun 26 2025 Evan Goode <egoode@redhat.com> - 0.69.0-15
|
||||
- history DB: Add "persistence" column (RHEL-100623)
|
||||
- conf: Add bootc_unsafe_paths (RHEL-100622)
|
||||
|
Loading…
Reference in New Issue
Block a user